Skip to content

Commit c40fa82

Browse files
authored
Merge pull request #1 from profcomff/template
2 parents eda7fd6 + 483980e commit c40fa82

37 files changed

Lines changed: 1283 additions & 18 deletions
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
name: Build, publish and deploy docker
2+
3+
on:
4+
push:
5+
branches: ['main']
6+
tags:
7+
- 'v*'
8+
9+
10+
env:
11+
REGISTRY: ghcr.io
12+
IMAGE_NAME: ${{ github.repository }}
13+
14+
15+
jobs:
16+
build-and-push-image:
17+
name: Build and push
18+
runs-on: ubuntu-latest
19+
permissions:
20+
contents: read
21+
packages: write
22+
steps:
23+
- name: Checkout repository
24+
uses: actions/checkout@v4
25+
- name: Log in to the Container registry
26+
uses: docker/login-action@v2
27+
with:
28+
registry: ${{ env.REGISTRY }}
29+
username: ${{ github.actor }}
30+
password: ${{ secrets.GITHUB_TOKEN }}
31+
- name: Extract metadata (tags, labels) for Docker
32+
id: meta
33+
uses: docker/metadata-action@v4
34+
with:
35+
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
36+
tags: |
37+
type=ref,event=tag,enable=${{ startsWith(github.ref, 'refs/tags/v') }}
38+
type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/v') }}
39+
type=raw,value=test,enable=true
40+
- name: Build and push Docker image
41+
uses: docker/build-push-action@v4
42+
with:
43+
context: .
44+
push: true
45+
tags: ${{ steps.meta.outputs.tags }}
46+
labels: ${{ steps.meta.outputs.labels }}
47+
build-args: |
48+
APP_VERSION=${{ github.ref_name }}
49+
50+
deploy-testing:
51+
name: Deploy Testing
52+
needs: build-and-push-image
53+
runs-on: [self-hosted, Linux, testing]
54+
environment:
55+
name: Testing
56+
url: https://api.test.profcomff.com/?urls.primaryName=modal_backend
57+
env:
58+
CONTAINER_NAME: com_profcomff_api_modal_backend_test
59+
permissions:
60+
packages: read
61+
steps:
62+
- name: Pull new version
63+
run: docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:test
64+
65+
- name: Migrate DB
66+
run: |
67+
docker run \
68+
--rm \
69+
--network=web \
70+
--env DB_DSN=${{ secrets.DB_DSN }} \
71+
--name ${{ env.CONTAINER_NAME }}_migration \
72+
--workdir="/" \
73+
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:test \
74+
alembic upgrade head
75+
- name: Run new version
76+
id: run_test
77+
run: |
78+
docker stop ${{ env.CONTAINER_NAME }} || true && docker rm ${{ env.CONTAINER_NAME }} || true
79+
docker run \
80+
--detach \
81+
--restart on-failure:3 \
82+
--network=web \
83+
--env DB_DSN='${{ secrets.DB_DSN }}' \
84+
--env ROOT_PATH='/modal_backend' \
85+
--env AUTH_URL='https://api.test.profcomff.com/auth' \
86+
--env GUNICORN_CMD_ARGS='--log-config logging_test.conf' \
87+
--name ${{ env.CONTAINER_NAME }} \
88+
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:test
89+
90+
deploy-production:
91+
name: Deploy Production
92+
needs: build-and-push-image
93+
if: startsWith(github.ref, 'refs/tags/v')
94+
runs-on: [self-hosted, Linux, production]
95+
environment:
96+
name: Production
97+
url: https://api.profcomff.com/?urls.primaryName=modal_backend
98+
env:
99+
CONTAINER_NAME: com_profcomff_api_modal_backend
100+
permissions:
101+
packages: read
102+
steps:
103+
- name: Pull new version
104+
run: docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
105+
- name: Migrate DB
106+
run: |
107+
docker run \
108+
--rm \
109+
--network=web \
110+
--env DB_DSN=${{ secrets.DB_DSN }} \
111+
--name ${{ env.CONTAINER_NAME }}_migration \
112+
--workdir="/" \
113+
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest \
114+
alembic upgrade head
115+
- name: Run new version
116+
id: run_test
117+
run: |
118+
docker stop ${{ env.CONTAINER_NAME }} || true && docker rm ${{ env.CONTAINER_NAME }} || true
119+
docker run \
120+
--detach \
121+
--restart always \
122+
--network=web \
123+
--env DB_DSN='${{ secrets.DB_DSN }}' \
124+
--env ROOT_PATH='/modal_backend' \
125+
--env AUTH_URL='https://api.profcomff.com/auth' \
126+
--env GUNICORN_CMD_ARGS='--log-config logging_prod.conf' \
127+
--name ${{ env.CONTAINER_NAME }} \
128+
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest

.github/workflows/checks.yml

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
name: Python tests
2+
3+
on:
4+
pull_request:
5+
6+
jobs:
7+
test:
8+
name: Unit tests
9+
runs-on: ubuntu-latest
10+
services:
11+
postgres:
12+
image: postgres:15
13+
env:
14+
POSTGRES_HOST_AUTH_METHOD: trust
15+
options: >-
16+
--health-cmd pg_isready
17+
--health-interval 10s
18+
--health-timeout 5s
19+
--health-retries 5
20+
-p 5432:5432
21+
steps:
22+
- name: Checkout
23+
uses: actions/checkout@v4
24+
- uses: actions/setup-python@v4
25+
with:
26+
python-version: "3.11"
27+
- name: Install dependencies
28+
run: |
29+
python -m ensurepip
30+
python -m pip install --upgrade --no-cache-dir pip
31+
python -m pip install --upgrade --no-cache-dir -r requirements.txt -r requirements.dev.txt
32+
- name: Migrate DB
33+
run: |
34+
DB_DSN=postgresql://postgres@localhost:5432/postgres alembic upgrade head
35+
- name: Build coverage file
36+
run: |
37+
DB_DSN=postgresql://postgres@localhost:5432/postgres pytest --junitxml=pytest.xml --cov-report=term-missing:skip-covered --cov=modal_backend tests/ | tee pytest-coverage.txt
38+
exit ${PIPESTATUS[0]}
39+
- name: Print report
40+
if: always()
41+
run: |
42+
cat pytest-coverage.txt
43+
- name: Pytest coverage comment
44+
uses: MishaKav/pytest-coverage-comment@main
45+
with:
46+
pytest-coverage-path: ./pytest-coverage.txt
47+
title: Coverage Report
48+
badge-title: Code Coverage
49+
hide-badge: false
50+
hide-report: false
51+
create-new-comment: false
52+
hide-comment: false
53+
report-only-changed-files: false
54+
remove-link-from-badge: false
55+
junitxml-path: ./pytest.xml
56+
junitxml-title: Summary
57+
- name: Fail on pytest errors
58+
if: steps.pytest.outcome == 'failure'
59+
run: exit 1
60+
61+
linting:
62+
runs-on: ubuntu-latest
63+
steps:
64+
- uses: actions/checkout@v4
65+
- uses: actions/setup-python@v2
66+
with:
67+
python-version: 3.11
68+
- uses: isort/isort-action@master
69+
with:
70+
requirementsFiles: "requirements.txt requirements.dev.txt"
71+
- uses: psf/black@23.11.0
72+
- name: Comment if linting failed
73+
if: failure()
74+
uses: thollander/actions-comment-pull-request@v2
75+
with:
76+
message: |
77+
:poop: Code linting failed, use `black` and `isort` to fix it.

.gitignore

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
# Byte-compiled / optimized / DLL files
2+
__pycache__/
3+
*.py[cod]
4+
*$py.class
5+
6+
# C extensions
7+
*.so
8+
9+
# Distribution / packaging
10+
.Python
11+
build/
12+
develop-eggs/
13+
dist/
14+
downloads/
15+
eggs/
16+
.eggs/
17+
lib/
18+
lib64/
19+
parts/
20+
sdist/
21+
var/
22+
wheels/
23+
pip-wheel-metadata/
24+
share/python-wheels/
25+
*.egg-info/
26+
.installed.cfg
27+
*.egg
28+
MANIFEST
29+
30+
# PyInstaller
31+
# Usually these files are written by a python script from a template
32+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
33+
*.manifest
34+
*.spec
35+
36+
# Installer logs
37+
pip-log.txt
38+
pip-delete-this-directory.txt
39+
40+
# Unit test / coverage reports
41+
htmlcov/
42+
.tox/
43+
.nox/
44+
.coverage
45+
.coverage.*
46+
.cache
47+
nosetests.xml
48+
coverage.xml
49+
*.cover
50+
*.py,cover
51+
.hypothesis/
52+
.pytest_cache/
53+
54+
# Translations
55+
*.mo
56+
*.pot
57+
58+
# Django stuff:
59+
*.log
60+
local_settings.py
61+
db.sqlite3
62+
db.sqlite3-journal
63+
64+
# Flask stuff:
65+
instance/
66+
.webassets-cache
67+
68+
# Scrapy stuff:
69+
.scrapy
70+
71+
# Sphinx documentation
72+
docs/_build/
73+
74+
# PyBuilder
75+
target/
76+
77+
# Jupyter Notebook
78+
.ipynb_checkpoints
79+
80+
# IPython
81+
profile_default/
82+
ipython_config.py
83+
84+
# pyenv
85+
.python-version
86+
87+
# pipenv
88+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
89+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
90+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
91+
# install all needed dependencies.
92+
#Pipfile.lock
93+
94+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
95+
__pypackages__/
96+
97+
# Celery stuff
98+
celerybeat-schedule
99+
celerybeat.pid
100+
101+
# SageMath parsed files
102+
*.sage.py
103+
104+
# Environments
105+
.env
106+
.venv
107+
env/
108+
venv/
109+
ENV/
110+
env.bak/
111+
venv.bak/
112+
113+
# Spyder project settings
114+
.spyderproject
115+
.spyproject
116+
117+
# Rope project settings
118+
.ropeproject
119+
120+
# mkdocs documentation
121+
/site
122+
123+
# mypy
124+
.mypy_cache/
125+
.dmypy.json
126+
dmypy.json
127+
128+
# Pyre type checker
129+
.pyre/

Dockerfile

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
FROM tiangolo/uvicorn-gunicorn-fastapi:python3.11
2+
ARG APP_VERSION=dev
3+
ENV APP_VERSION=${APP_VERSION}
4+
ENV APP_NAME=modal_backend
5+
ENV APP_MODULE=${APP_NAME}.routes.base:app
6+
7+
COPY ./requirements.txt /app/
8+
COPY ./logging_prod.conf /app/
9+
COPY ./logging_test.conf /app/
10+
RUN pip install -U -r /app/requirements.txt
11+
12+
COPY ./alembic.ini /alembic.ini
13+
COPY ./migrations /migrations/
14+
15+
COPY ./${APP_NAME} /app/${APP_NAME}
16+
17+
18+

0 commit comments

Comments
 (0)