Skip to content

Commit 95a3ca9

Browse files
authored
Chore/docker app image (#4)
* ci(release): build & push GHCR image on tags; attach OpenAPI + env to releases * docker: add app Dockerfile and compose service; expose 8000
1 parent 450ab2d commit 95a3ca9

File tree

5 files changed

+147
-0
lines changed

5 files changed

+147
-0
lines changed

.dockerignore

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
.git
2+
.venv/
3+
__pycache__/
4+
*.pyc
5+
*.pyo
6+
.pytest_cache/
7+
dist/
8+
build/
9+
.env
10+
.DS_Store
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
name: Release Image
2+
on:
3+
push:
4+
tags:
5+
- 'v*'
6+
7+
jobs:
8+
docker:
9+
runs-on: ubuntu-latest
10+
permissions:
11+
contents: write # needed to attach release assets
12+
packages: write
13+
steps:
14+
- uses: actions/checkout@v4
15+
16+
- uses: actions/setup-python@v5
17+
with:
18+
python-version: "3.12"
19+
cache: pip
20+
cache-dependency-path: requirements.txt
21+
22+
- name: Generate OpenAPI
23+
run: |
24+
python -m pip install --upgrade pip
25+
pip install -r requirements.txt
26+
python scripts/generate_openapi.py
27+
28+
- uses: docker/setup-buildx-action@v3
29+
30+
- uses: docker/login-action@v3
31+
with:
32+
registry: ghcr.io
33+
username: ${{ github.actor }}
34+
password: ${{ secrets.GITHUB_TOKEN }}
35+
36+
- uses: docker/metadata-action@v5
37+
id: meta
38+
with:
39+
images: ghcr.io/${{ github.repository }}
40+
tags: |
41+
type=semver,pattern={{version}}
42+
type=semver,pattern={{major}}.{{minor}}
43+
type=raw,value=latest
44+
45+
- uses: docker/build-push-action@v6
46+
with:
47+
context: .
48+
push: true
49+
tags: ${{ steps.meta.outputs.tags }}
50+
labels: ${{ steps.meta.outputs.labels }}
51+
52+
- name: Attach release assets
53+
if: startsWith(github.ref, 'refs/tags/')
54+
uses: softprops/action-gh-release@v2
55+
with:
56+
files: |
57+
dist/openapi.json
58+
.env.example

Dockerfile

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# syntax=docker/dockerfile:1
2+
FROM python:3.12-slim
3+
4+
ENV PYTHONDONTWRITEBYTECODE=1 \
5+
PYTHONUNBUFFERED=1 \
6+
PIP_NO_CACHE_DIR=1
7+
8+
WORKDIR /app
9+
10+
# (optional) system deps for builds; keep minimal
11+
RUN apt-get update && apt-get install -y --no-install-recommends \
12+
build-essential curl && \
13+
rm -rf /var/lib/apt/lists/*
14+
15+
# Install Python deps first for better layer caching
16+
COPY requirements.txt .
17+
RUN python -m pip install --upgrade pip && pip install -r requirements.txt
18+
19+
# Copy the app
20+
COPY . .
21+
22+
EXPOSE 8000
23+
24+
# If your FastAPI app lives at app/api/main.py as `app`, this is correct:
25+
CMD ["uvicorn", "app.api.main:app", "--host", "0.0.0.0", "--port", "8000"]

docker-compose.yml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,29 @@ services:
115115
interval: 10s
116116
timeout: 5s
117117
retries: 12
118+
app:
119+
build:
120+
context: .
121+
dockerfile: Dockerfile
122+
container_name: multidb-chatbot
123+
ports:
124+
- "8000:8000"
125+
env_file:
126+
- .env
127+
environment:
128+
# Sensible defaults for local stack; override via .env if needed
129+
MONGODB_URI: "mongodb://root:example@mongodb-atlas-local:27017/?replicaSet=mongodb-atlas-local&directConnection=true"
130+
REDIS_URL: "redis://my-redis:6379/0"
131+
POSTGRES_DSN: "postgresql+asyncpg://chatbot_user:secure_password@chatbot-postgres:5432/chatbot_app"
132+
depends_on:
133+
postgres:
134+
condition: service_healthy
135+
redis:
136+
condition: service_healthy
137+
mongodb:
138+
condition: service_healthy
139+
scylla-node1:
140+
condition: service_healthy
118141

119142
volumes:
120143
postgres_data:

scripts/generate_openapi.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#!/usr/bin/env python3
2+
import json, importlib, sys
3+
from pathlib import Path
4+
5+
PROJECT_ROOT = Path(__file__).resolve().parents[1]
6+
sys.path.insert(0, str(PROJECT_ROOT))
7+
8+
candidates = [
9+
("main", "app"), # if you have top-level main.py
10+
("app.main", "app"), # if app/main.py exists
11+
("app.api.main", "app"), # your repo often uses this path
12+
]
13+
14+
app = None
15+
last_err = None
16+
for module_name, attr in candidates:
17+
try:
18+
mod = importlib.import_module(module_name)
19+
app = getattr(mod, attr)
20+
break
21+
except Exception as e:
22+
last_err = e
23+
24+
if app is None:
25+
raise RuntimeError(f"Could not locate FastAPI 'app' in any of {candidates}. Last error: {last_err}")
26+
27+
out = PROJECT_ROOT / "dist"
28+
out.mkdir(exist_ok=True)
29+
with open(out / "openapi.json", "w") as f:
30+
json.dump(app.openapi(), f, indent=2)
31+
print(f"wrote {out/'openapi.json'}")

0 commit comments

Comments
 (0)