Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
.git
.github
.venv
.pytest_cache
__pycache__
*.pyc
.coverage
htmlcov
.env
.env.*
LICENSE
tests
docs
81 changes: 81 additions & 0 deletions .github/workflows/build-and-deploy-dev.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
name: build-and-deploy-dev

on:
push:
branches: [main]
workflow_dispatch:

permissions:
contents: read
id-token: write

env:
AWS_REGION: ap-southeast-2
IMAGE_REPO: 868550873346.dkr.ecr.ap-southeast-2.amazonaws.com/atol-backend-dev
DEPLOY_FUNCTION_NAME: AtolBackendDevDeploymentFunction

jobs:
docker:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

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

- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ECR_PUSH }}
aws-region: ${{ env.AWS_REGION }}

- name: Login to ECR
uses: aws-actions/amazon-ecr-login@v2

- name: Build and push dev image
uses: docker/build-push-action@v6
with:
context: .
file: ./Dockerfile
platforms: linux/amd64
build-args: |
APP_VERSION=dev-${{ github.sha }}
push: true
tags: |
${{ env.IMAGE_REPO }}:latest
${{ env.IMAGE_REPO }}:${{ github.sha }}
provenance: false
sbom: false
cache-from: type=gha
cache-to: type=gha,mode=max

- name: Deploy backend via Lambda
env:
IMAGE_TAG: latest
run: |
set -euo pipefail
PAYLOAD=$(jq -n --arg tag "${IMAGE_TAG}" '{tag: $tag}')
RESPONSE_FILE=$(mktemp)

INVOKE_METADATA=$(aws lambda invoke \
--function-name "${DEPLOY_FUNCTION_NAME}" \
--payload "${PAYLOAD}" \
--cli-binary-format raw-in-base64-out \
--cli-read-timeout 0 \
"${RESPONSE_FILE}")

echo "${INVOKE_METADATA}"
cat "${RESPONSE_FILE}"

FUNCTION_ERROR=$(echo "${INVOKE_METADATA}" | jq -r '.FunctionError // empty')
if [ -n "${FUNCTION_ERROR}" ]; then
echo "Deployment lambda reported an error: ${FUNCTION_ERROR}" >&2
exit 1
fi

STATUS=$(jq -r '.status // empty' "${RESPONSE_FILE}")
if [ "${STATUS}" != "SUCCESS" ]; then
echo "Deployment lambda returned unexpected status: ${STATUS}" >&2
exit 1
fi
51 changes: 51 additions & 0 deletions .github/workflows/build-publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: build-and-push-release

on:
push:
tags:
- 'v*'
workflow_dispatch:

permissions:
contents: read
id-token: write

env:
AWS_REGION: ap-southeast-2
IMAGE_REPO: 868550873346.dkr.ecr.ap-southeast-2.amazonaws.com/atol-backend-dev

jobs:
docker:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

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

- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ECR_PUSH }}
aws-region: ${{ env.AWS_REGION }}

- name: Login to ECR
uses: aws-actions/amazon-ecr-login@v2

- name: Build and push release image
uses: docker/build-push-action@v6
with:
context: .
file: ./Dockerfile
platforms: linux/amd64
build-args: |
APP_VERSION=${{ github.ref_name }}
push: true
tags: |
${{ env.IMAGE_REPO }}:${{ github.ref_name }}
${{ env.IMAGE_REPO }}:latest
provenance: false
sbom: false
cache-from: type=gha
cache-to: type=gha,mode=max
2 changes: 2 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
FROM ghcr.io/astral-sh/uv:python3.13-bookworm-slim

WORKDIR /app
ARG APP_VERSION=dev

# Ensure uv installs into a local virtual environment for the project
ENV UV_PROJECT_ENVIRONMENT=/app/.venv
Expand All @@ -18,6 +19,7 @@ RUN chmod +x scripts/entrypoint.sh
ENV PATH="/app/.venv/bin:${PATH}"
ENV PYTHONPATH=/app
ENV PORT=8000
ENV APP_VERSION=${APP_VERSION}

EXPOSE 8000

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,7 @@ The system implements role-based access control with the following roles:
- Apply migrations: `uv run alembic upgrade head`
- Create a new revision: `uv run alembic revision --autogenerate -m "description"`
- Docker Compose runs migrations automatically on container start via `scripts/entrypoint.sh`.
- `scripts/entrypoint.sh` supports `APP_MODE=serve` (default) and `APP_MODE=migrate` (run migrations then exit).

### Environment configuration

Expand Down
1 change: 1 addition & 0 deletions app/core/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class Settings(BaseSettings):

# Environment
ENVIRONMENT: Optional[str] = None # Options: "dev", "prod"
APP_VERSION: str = "dev"

# Model config
model_config = SettingsConfigDict(
Expand Down
12 changes: 12 additions & 0 deletions app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,18 @@ def root():
}


@app.get("/health")
def health():
"""ALB health-check endpoint."""
return {"status": "ok"}


@app.get("/version")
def version():
"""Application version endpoint."""
return {"version": settings.APP_VERSION}


if __name__ == "__main__":
import uvicorn

Expand Down
11 changes: 11 additions & 0 deletions scripts/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#!/usr/bin/env bash
set -euo pipefail

MODE="${APP_MODE:-serve}"
DB_URL="${DATABASE_URI:-}"
if [ -z "${DB_URL}" ]; then
echo "DATABASE_URI is not set; cannot run migrations."
Expand All @@ -26,6 +27,16 @@ done
echo "Running database migrations..."
uv run alembic upgrade head

if [ "${MODE}" = "migrate" ]; then
echo "Migration-only mode complete."
exit 0
fi

if [ "${MODE}" != "serve" ]; then
echo "Unknown APP_MODE '${MODE}'. Expected 'serve' or 'migrate'."
exit 1
fi

if [ "${ENVIRONMENT:-prod}" = "dev" ]; then
echo "Starting application (dev mode with reload)..."
exec uv run --frozen uvicorn app.main:app \
Expand Down