Skip to content

Commit

Permalink
copy-pasted the code from another branch (#86)
Browse files Browse the repository at this point in the history
Added CI pipeline, formatters, and secret management
  • Loading branch information
agennadi authored Nov 19, 2024
1 parent 34a5d38 commit feffc61
Show file tree
Hide file tree
Showing 32 changed files with 295 additions and 80 deletions.
2 changes: 2 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

**/.DS_Store
**/__pycache__
**/.pytest_cache
**/.mypy_cache
**/.venv
**/.classpath
**/.dockerignore
Expand Down
1 change: 1 addition & 0 deletions .env.local → .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ SECRET_KEY=default-secret-key_local # Replace with your actual secret key

# Frontend Environment Variables
NEXT_PUBLIC_API_URL=http://localhost:8000/api # The base URL for API calls to the backend
NEXT_PUBLIC_MAPBOX_TOKEN=dummy-mapbox-token
NODE_ENV=development # For libraries and general Node.js practices
92 changes: 92 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
name: CI Pipeline

on:
push:
branches:
- master
pull_request:
types: [opened, synchronize, reopened]
branches:
- master

jobs:
eslint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: '18.x'

- name: Install Node.js dependencies
run: npm ci

- name: Run ESLint
run: npm run lint

black_lint_and_mypy_type_check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.x'

- name: Install dependencies
run: |
pip install black mypy
pip install fastapi pydantic pydantic-settings sqlalchemy GeoAlchemy2 pytest
- name: Check code formatting with black
run: black --check .

- name: Type check with mypy
run: mypy --config-file mypy.ini .

docker_build_test:
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v3

- name: Get Run ID of Most Recent Successful Run
id: get_run_id
run: |
response=$(curl -s -H "Authorization: token ${{ secrets.GH_PAT }}" \
"https://api.github.com/repos/sfbrigade/datasci-earthquake/actions/workflows/env_vars.yml/runs?status=completed&conclusion=success")
run_id=$(echo $response | jq '.workflow_runs[0].id')
echo "Run ID: $run_id"
echo "run_id=$run_id" >> $GITHUB_ENV
- name: Download .env Artifact
uses: actions/download-artifact@v4
with:
name: env-file
github-token: ${{ secrets.GH_PAT }}
repository: sfbrigade/datasci-earthquake
run-id: ${{ env.run_id }}

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2

- name: Cache Docker layers
uses: actions/cache@v3
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ github.sha }}
restore-keys: |
${{ runner.os }}-buildx-
- name: Build Docker Containers
run: docker compose build

- name: Start Services
run: docker compose up -d

- name: Run Backend Tests
run: docker compose run backend pytest backend/database/tests

- name: Run Frontend Tests
run: docker compose run frontend npm test

- name: Clean Up
run: docker compose down --volumes
42 changes: 42 additions & 0 deletions .github/workflows/env_vars.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: Generate .env File

on:
workflow_dispatch:

jobs:
create-envfile:

runs-on: ubuntu-latest

steps:
- name: Make envfile
uses: SpicyPizza/create-envfile@v2.0
with:
envkey_POSTGRES_USER: ${{ secrets.POSTGRES_USER }}
envkey_POSTGRES_PASSWORD: ${{ secrets.POSTGRES_PASSWORD }}
envkey_POSTGRES_DB: ${{ secrets.POSTGRES_DB }}
envkey_POSTGIS_VERSION: ${{ secrets.POSTGIS_VERSION }}

envkey_FRONTEND_HOST: ${{ secrets.FRONTEND_HOST }}
envkey_DATABASE_URL: ${{ secrets.DATABASE_URL }}
envkey_LOCALHOST_DATABASE_URL: ${{ secrets.LOCALHOST_DATABASE_URL }}
envkey_DATABASE_URL_SQLALCHEMY: ${{ secrets.DATABASE_URL_SQLALCHEMY }}
envkey_LOCALHOST_DATABASE_URL_SQLALCHEMY: ${{ secrets.LOCALHOST_DATABASE_URL_SQLALCHEMY }}
envkey_ENVIRONMENT: ${{ secrets.ENVIRONMENT }}
envkey_SECRET_KEY: ${{ secrets.SECRET_KEY }}

envkey_NEXT_PUBLIC_API_URL: ${{ secrets.NEXT_PUBLIC_API_URL }}
envkey_NEXT_PUBLIC_MAPBOX_TOKEN: ${{ secrets.NEXT_PUBLIC_MAPBOX_TOKEN }}
envkey_NODE_ENV: ${{ secrets.NODE_ENV }}

file_name: .env
directory: './'
fail_on_empty: false
sort_keys: false

- name: Upload .env as Artifact
uses: actions/upload-artifact@v3
with:
name: env-file
include-hidden-files: true
path: ./.env
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

# Python
__pycache__/
pytest_cache/
mypy_cache/
*.py[cod]
bin/
build/
Expand All @@ -19,4 +21,5 @@ node_modules
/.next/

# Sensitive config
.env.development.local
.env.development.local
.env
33 changes: 33 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
repos:
- repo: https://github.com/psf/black
rev: 23.3.0
hooks:
- id: black
- repo: https://github.com/pre-commit/mirrors-mypy
rev: "v1.13.0"
hooks:
- id: mypy
args: ["--config-file", "mypy.ini"]
additional_dependencies:
- "pydantic>=2.9.0"
- "sqlalchemy>=2.0.35"
- "pydantic-settings>=2.5.2"
- "fastapi>=0.114.0"
- "GeoAlchemy2>=0.15.2"
- "pytest>=8.3.3"
- repo: https://github.com/pre-commit/mirrors-eslint
rev: "v9.14.0"
hooks:
- id: eslint
args:
- "--config=.eslintrc.js"
- "--cache"
- "--ignore-pattern=node_modules/*"
entry: npm run lint
language: node
files: \.[jt]sx?$
additional_dependencies:
- "eslint"
- "eslint-plugin-prettier"
- "eslint-config-prettier"
- "prettier"
2 changes: 2 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ build
api
.github
pull_request_template.md
.mypy_cache/
__pycache__/
46 changes: 40 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ pnpm dev

Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.

The FastApi server will be running on [http://127.0.0.1:8000](http://127.0.0.1:8000) – feel free to change the port in `package.json` (you'll also need to update it in `next.config.js`).
The FastApi server will be running on [http://127.0.0.1:8000](http://127.0.0.1:8000) – feel free to change the port in `package.json` (you'll also need to update it in `next.config.js`).

## Learn More

Expand All @@ -42,6 +42,31 @@ You can check out [the Next.js GitHub repository](https://github.com/vercel/next

---

# Formatting with a Pre-Commit Hook

This repository uses `Black` for Python and `ESLint` for JS/TS to enforce code style standards. We also use `MyPy` to perform static type checking on Python code. The pre-commit hook runs the formatters automatically before each commit, helping maintain code consistency across the project.

## Prerequisites

- If you haven't already, install pre-commit:
`pip install pre-commit`
- Run the following command to install the pre-commit hooks defined in the configuration file `.pre-commit-config.yaml`:
`pre-commit install`
This command sets up pre-commit to automatically run ESLint, Black, and MyPy before each commit.

## Usage

- **Running Black Automatically**: After setup, every time you attempt to commit code, Black will check the staged files and apply formatting if necessary. If files are reformatted, the commit will be stopped, and you’ll need to review the changes before committing again.
- **Bypassing the Hook**: If you want to skip the pre-commit hook for a specific commit, use the --no-verify flag with your commit command:
`git commit -m "your commit message" --no-verify`.

**Note**: The `--no-verify` flag is helpful in cases where you need to make a quick commit without running the pre-commit checks, but it should be used sparingly to maintain code quality. CI pipeline will fail during the `pull request` action if the code is not formatted.

- **Running Pre-commit on All Files**: If you want to format all files in the repository, use:
`pre-commit run --all-files`

---

# Docker

This project uses Docker and Docker Compose to run the application, which includes the frontend, backend, and postgres database.
Expand Down Expand Up @@ -81,12 +106,21 @@ To stop and shut down the application:

# Configuration of environment variables

The `.env.local` file contains environment variables used in the application to configure settings for both the backend and frontend components. If it contains sensitive information, `.env.local` should not be checked into version control for security reasons. Right now there is no sensitive information but later secret management tools will be introduced.
We use GitHub Secrets to store sensitive environment variables. A template `.env.example` file is provided in the repository as a reference. Only users with **write** access to the repository can manually trigger the `Generate .env File` workflow, which creates and uploads the actual `.env` file as an artifact.

**Note**: Before starting work on the project, make sure to:

1. Get **write** access to the repository
2. Trigger the `Generate .env File` workflow and download the artifact.
3. Place the artifact in the root folder of the project. Make sure the file is named `.env`.

The file is organized into three main sections:
- **Postgres Environment Variables**. This section contains the credentials to connect to the PostgreSQL database, such as the username, password, and the name of the database.
- **Backend Environment Variables**. These variables are used by the backend (i.e., FastAPI) to configure its behavior and to connect to the database and the frontend application.
- **Frontend Environment Variables**. This section contains the base URL for API calls to the backend and ```NODE_ENV``` variable that determines in which environment the Node.js application is running.
***

- **Postgres Environment Variables**. This section contains the credentials to connect to the PostgreSQL database, such as the username, password, and the name of the database.
- **Backend Environment Variables**. These variables are used by the backend (e.g., FastAPI) to configure its behavior and to connect to the database and the frontend application.
- **Frontend Environment Variables**. This section contains the base URL for API calls to the backend and `NODE_ENV` variable that determines in which environment the Node.js application is running.

---

# Disclaimer

Expand Down
6 changes: 4 additions & 2 deletions app/components/__tests__/search-bar.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import "@testing-library/jest-dom";

jest.mock("@mapbox/search-js-react", () => ({
AddressAutofill: ({ children, onRetrieve }) => (
<div onClick={() => onRetrieve({ features: [{ place_name: "Mock Address" }] })}>
<div
onClick={() => onRetrieve({ features: [{ place_name: "Mock Address" }] })}
>
{children}
</div>
),
Expand All @@ -13,7 +15,7 @@ jest.mock("@mapbox/search-js-react", () => ({
describe("SearchBar Component", () => {
it("renders search input and icons correctly", () => {
render(<SearchBar />);

const input = screen.getByPlaceholderText("Search San Francisco address");
const searchIcon = screen.getByTestId("search-icon");
const clearIcon = screen.getByTestId("clear-icon");
Expand Down
3 changes: 2 additions & 1 deletion backend/api/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,11 @@ class Settings(BaseSettings):
environment: str
secret_key: str
next_public_api_url: str
next_public_mapbox_token: str
node_env: str

class Config:
env_file = ".env.local"
env_file = ".env"
env_file_encoding = "utf-8"


Expand Down
14 changes: 5 additions & 9 deletions backend/api/models/landslide_zones.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
"""All data of the Landslide Zones table from SFData."""

from sqlalchemy import String, Integer
from sqlalchemy import String, Integer, Float, DateTime, func
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column
from geoalchemy2 import Geometry
from datetime import datetime, DateTime
from datetime import datetime
from .base import Base


Expand All @@ -23,17 +23,13 @@ class LandslideZone(Base):
sum_shape: Mapped[float] = mapped_column(Float)
shape_length: Mapped[float] = mapped_column(Float)
created_us: Mapped[str] = mapped_column(String)
created_da: Mapped[datetime] = mapped_column(
DateTime(timezone=True), server_default=datetime.utcnow
)
created_da: Mapped[datetime] = mapped_column(DateTime(timezone=True))
last_edited: Mapped[str] = mapped_column(String)
last_edi_1: Mapped[datetime] = mapped_column(
DateTime(timezone=True), server_default=datetime.utcnow
)
last_edi_1: Mapped[datetime] = mapped_column(DateTime(timezone=True))
shape_Le_1: Mapped[float] = mapped_column(Float)
shape_area: Mapped[float] = mapped_column(Float)
update_timestamp: Mapped[datetime] = mapped_column(
DateTime(timezone=True), server_default=datetime.utcnow
DateTime(timezone=True), server_default=func.now()
)

def __repr__(self) -> str:
Expand Down
8 changes: 4 additions & 4 deletions backend/api/models/liquefaction_zones.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
"""All data of the Liquefaction Zones table from SFData."""

from sqlalchemy import String
from sqlalchemy import String, Float, DateTime, func
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column
from geoalchemy2 import Geometry
from datetime import datetime, DateTime
from datetime import datetime
from .base import Base


class LiquefactionZons(Base):
class LiquefactionZone(Base):
"""
All data of the Liquefaction Zones table from SFData.
Contains multipolygon geometries defining soil liquefaction zones as High (H) or
Expand All @@ -24,7 +24,7 @@ class LiquefactionZons(Base):
shape_length: Mapped[float] = mapped_column(Float)
shape_area: Mapped[float] = mapped_column(Float)
update_timestamp: Mapped[datetime] = mapped_column(
DateTime(timezone=True), server_default=datetime.utcnow
DateTime(timezone=True), server_default=func.now()
)

def __repr__(self) -> str:
Expand Down
Loading

0 comments on commit feffc61

Please sign in to comment.