feat: Update CI workflows for Docker image build and deployment, enhance test configurations, and add testing documentation
This commit is contained in:
@@ -1,11 +1,16 @@
|
|||||||
name: Build and Push Docker Image
|
name: Build and Push Docker Image
|
||||||
on:
|
on:
|
||||||
push:
|
workflow_run:
|
||||||
|
workflows:
|
||||||
|
- Run Tests
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
types:
|
||||||
|
- completed
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-and-push:
|
build-and-push:
|
||||||
|
if: ${{ github.event.workflow_run.conclusion == 'success' }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
env:
|
env:
|
||||||
DEFAULT_BRANCH: main
|
DEFAULT_BRANCH: main
|
||||||
@@ -14,6 +19,8 @@ jobs:
|
|||||||
REGISTRY_URL: ${{ secrets.REGISTRY_URL }}
|
REGISTRY_URL: ${{ secrets.REGISTRY_URL }}
|
||||||
REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }}
|
REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }}
|
||||||
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
|
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
|
||||||
|
WORKFLOW_RUN_HEAD_BRANCH: ${{ github.event.workflow_run.head_branch }}
|
||||||
|
WORKFLOW_RUN_HEAD_SHA: ${{ github.event.workflow_run.head_sha }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -26,6 +33,14 @@ jobs:
|
|||||||
event_name="${GITHUB_EVENT_NAME:-}"
|
event_name="${GITHUB_EVENT_NAME:-}"
|
||||||
sha="${GITHUB_SHA:-}"
|
sha="${GITHUB_SHA:-}"
|
||||||
|
|
||||||
|
if [ -z "$ref_name" ] && [ -n "${WORKFLOW_RUN_HEAD_BRANCH:-}" ]; then
|
||||||
|
ref_name="${WORKFLOW_RUN_HEAD_BRANCH}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$sha" ] && [ -n "${WORKFLOW_RUN_HEAD_SHA:-}" ]; then
|
||||||
|
sha="${WORKFLOW_RUN_HEAD_SHA}"
|
||||||
|
fi
|
||||||
|
|
||||||
if [ "$ref_name" = "${DEFAULT_BRANCH:-main}" ]; then
|
if [ "$ref_name" = "${DEFAULT_BRANCH:-main}" ]; then
|
||||||
echo "on_default=true" >> "$GITHUB_OUTPUT"
|
echo "on_default=true" >> "$GITHUB_OUTPUT"
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -1,11 +1,16 @@
|
|||||||
name: Deploy to Server
|
name: Deploy to Server
|
||||||
on:
|
on:
|
||||||
push:
|
workflow_run:
|
||||||
|
workflows:
|
||||||
|
- Build and Push Docker Image
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
types:
|
||||||
|
- completed
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
deploy:
|
deploy:
|
||||||
|
if: ${{ github.event.workflow_run.conclusion == 'success' }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
env:
|
env:
|
||||||
DEFAULT_BRANCH: main
|
DEFAULT_BRANCH: main
|
||||||
@@ -14,6 +19,8 @@ jobs:
|
|||||||
REGISTRY_URL: ${{ secrets.REGISTRY_URL }}
|
REGISTRY_URL: ${{ secrets.REGISTRY_URL }}
|
||||||
REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }}
|
REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }}
|
||||||
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
|
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
|
||||||
|
WORKFLOW_RUN_HEAD_BRANCH: ${{ github.event.workflow_run.head_branch }}
|
||||||
|
WORKFLOW_RUN_HEAD_SHA: ${{ github.event.workflow_run.head_sha }}
|
||||||
steps:
|
steps:
|
||||||
- name: SSH and deploy
|
- name: SSH and deploy
|
||||||
uses: appleboy/ssh-action@master
|
uses: appleboy/ssh-action@master
|
||||||
@@ -22,7 +29,15 @@ jobs:
|
|||||||
username: ${{ secrets.SSH_USERNAME }}
|
username: ${{ secrets.SSH_USERNAME }}
|
||||||
key: ${{ secrets.SSH_PRIVATE_KEY }}
|
key: ${{ secrets.SSH_PRIVATE_KEY }}
|
||||||
script: |
|
script: |
|
||||||
docker pull ${{ env.REGISTRY_URL }}/${{ env.REGISTRY_ORG }}/${{ env.REGISTRY_IMAGE_NAME }}:latest
|
IMAGE_SHA="${{ env.WORKFLOW_RUN_HEAD_SHA }}"
|
||||||
|
IMAGE_PATH="${{ env.REGISTRY_URL }}/${{ env.REGISTRY_ORG }}/${{ env.REGISTRY_IMAGE_NAME }}"
|
||||||
|
|
||||||
|
if [ -z "$IMAGE_SHA" ]; then
|
||||||
|
echo "Missing workflow run head SHA; aborting deployment." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
docker pull "$IMAGE_PATH:$IMAGE_SHA"
|
||||||
docker stop calminer || true
|
docker stop calminer || true
|
||||||
docker rm calminer || true
|
docker rm calminer || true
|
||||||
docker run -d --name calminer -p 8000:8000 \
|
docker run -d --name calminer -p 8000:8000 \
|
||||||
@@ -33,4 +48,4 @@ jobs:
|
|||||||
-e DATABASE_PASSWORD=${{ secrets.DATABASE_PASSWORD }} \
|
-e DATABASE_PASSWORD=${{ secrets.DATABASE_PASSWORD }} \
|
||||||
-e DATABASE_NAME=${{ secrets.DATABASE_NAME }} \
|
-e DATABASE_NAME=${{ secrets.DATABASE_NAME }} \
|
||||||
-e DATABASE_SCHEMA=${{ secrets.DATABASE_SCHEMA }} \
|
-e DATABASE_SCHEMA=${{ secrets.DATABASE_SCHEMA }} \
|
||||||
${{ secrets.REGISTRY_URL }}/${{ secrets.REGISTRY_USERNAME }}/calminer:latest
|
"$IMAGE_PATH:$IMAGE_SHA"
|
||||||
|
|||||||
@@ -2,7 +2,13 @@ name: Run Tests
|
|||||||
on: [push]
|
on: [push]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
tests:
|
||||||
|
name: ${{ matrix.target }} tests
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
target: [unit, e2e]
|
||||||
services:
|
services:
|
||||||
postgres:
|
postgres:
|
||||||
image: postgres:16-alpine
|
image: postgres:16-alpine
|
||||||
@@ -10,14 +16,11 @@ jobs:
|
|||||||
POSTGRES_DB: calminer_ci
|
POSTGRES_DB: calminer_ci
|
||||||
POSTGRES_USER: calminer
|
POSTGRES_USER: calminer
|
||||||
POSTGRES_PASSWORD: secret
|
POSTGRES_PASSWORD: secret
|
||||||
ports:
|
|
||||||
- 5432:5432
|
|
||||||
options: >-
|
options: >-
|
||||||
--health-cmd "pg_isready -U calminer -d calminer_ci"
|
--health-cmd "pg_isready -U calminer -d calminer_ci"
|
||||||
--health-interval 10s
|
--health-interval 10s
|
||||||
--health-timeout 5s
|
--health-timeout 5s
|
||||||
--health-retries 10
|
--health-retries 10
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -54,6 +57,7 @@ jobs:
|
|||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
pip install -r requirements-test.txt
|
pip install -r requirements-test.txt
|
||||||
- name: Install Playwright browsers
|
- name: Install Playwright browsers
|
||||||
|
if: ${{ matrix.target == 'e2e' }}
|
||||||
run: |
|
run: |
|
||||||
python -m playwright install --with-deps
|
python -m playwright install --with-deps
|
||||||
- name: Wait for database service
|
- name: Wait for database service
|
||||||
@@ -122,4 +126,9 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
DATABASE_URL: postgresql+psycopg2://calminer:secret@postgres:5432/calminer_ci
|
DATABASE_URL: postgresql+psycopg2://calminer:secret@postgres:5432/calminer_ci
|
||||||
DATABASE_SCHEMA: public
|
DATABASE_SCHEMA: public
|
||||||
run: pytest
|
run: |
|
||||||
|
if [ "${{ matrix.target }}" = "unit" ]; then
|
||||||
|
pytest tests/unit
|
||||||
|
else
|
||||||
|
pytest tests/e2e
|
||||||
|
fi
|
||||||
|
|||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -45,3 +45,6 @@ logs/
|
|||||||
# SQLite database
|
# SQLite database
|
||||||
*.sqlite3
|
*.sqlite3
|
||||||
test*.db
|
test*.db
|
||||||
|
|
||||||
|
# Docker files
|
||||||
|
.runner
|
||||||
|
|||||||
@@ -21,9 +21,9 @@ CalMiner uses a combination of unit, integration, and end-to-end tests to ensure
|
|||||||
### CI/CD
|
### CI/CD
|
||||||
|
|
||||||
- Use Gitea Actions for CI/CD; workflows live under `.gitea/workflows/`.
|
- Use Gitea Actions for CI/CD; workflows live under `.gitea/workflows/`.
|
||||||
- `test.yml` runs on every push, provisions a temporary Postgres 16 service, waits for readiness, executes the setup script in dry-run and live modes, installs Playwright browsers, and finally runs the full pytest suite.
|
- `test.yml` runs on every push, provisions a temporary Postgres 16 service, waits for readiness, executes the setup script in dry-run and live modes, then fans out into parallel matrix jobs for unit (`pytest tests/unit`) and end-to-end (`pytest tests/e2e`) suites. Playwright browsers install only for the E2E job.
|
||||||
- `build-and-push.yml` builds the Docker image with `docker/build-push-action@v2`, reusing GitHub Actions cache-backed layers, and pushes to the Gitea registry.
|
- `build-and-push.yml` runs only after the **Run Tests** workflow finishes successfully (triggered via `workflow_run` on `main`). Once tests pass, it builds the Docker image with `docker/build-push-action@v2`, reuses cache-backed layers, and pushes to the Gitea registry.
|
||||||
- `deploy.yml` connects to the target host (via `appleboy/ssh-action`) to pull the freshly pushed image and restart the container.
|
- `deploy.yml` runs only after the build workflow reports success on `main`. It connects to the target host (via `appleboy/ssh-action`), pulls the Docker image tagged with the build commit SHA, and restarts the container with that exact image reference.
|
||||||
- Mandatory secrets: `REGISTRY_USERNAME`, `REGISTRY_PASSWORD`, `REGISTRY_URL`, `SSH_HOST`, `SSH_USERNAME`, `SSH_PRIVATE_KEY`.
|
- Mandatory secrets: `REGISTRY_USERNAME`, `REGISTRY_PASSWORD`, `REGISTRY_URL`, `SSH_HOST`, `SSH_USERNAME`, `SSH_PRIVATE_KEY`.
|
||||||
- Run tests on pull requests to shared branches; enforce coverage target ≥80% (pytest-cov).
|
- Run tests on pull requests to shared branches; enforce coverage target ≥80% (pytest-cov).
|
||||||
|
|
||||||
@@ -18,30 +18,34 @@ The CalMiner application is deployed using a multi-tier architecture consisting
|
|||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
graph TD
|
graph TD
|
||||||
A[Client Layer<br/>(Web Browsers)] --> B[Web Application Layer<br/>(FastAPI)]
|
A[Client Layer] --> B[Web Application Layer]
|
||||||
B --> C[Database Layer<br/>(PostgreSQL)]
|
B --> C[Database Layer]
|
||||||
```
|
```
|
||||||
|
|
||||||
## Infrastructure Components
|
## Infrastructure Components
|
||||||
|
|
||||||
The infrastructure components for the application include:
|
The infrastructure components for the application include:
|
||||||
|
|
||||||
- **Web Server**: Hosts the FastAPI application and serves API endpoints.
|
|
||||||
- **Database Server**: PostgreSQL database for persisting application data.
|
|
||||||
- **Static File Server**: Serves static assets such as CSS, JavaScript, and image files.
|
|
||||||
- **Reverse Proxy (optional)**: An Nginx or Apache server can be used as a reverse proxy.
|
- **Reverse Proxy (optional)**: An Nginx or Apache server can be used as a reverse proxy.
|
||||||
- **Containerization**: Docker images are generated via the repository `Dockerfile`, using a multi-stage build to keep the final runtime minimal.
|
- **Containerization**: Docker images are generated via the repository `Dockerfile`, using a multi-stage build to keep the final runtime minimal.
|
||||||
- **CI/CD Pipeline**: Automated pipelines (Gitea Actions) run tests, build/push Docker images, and trigger deployments.
|
- **CI/CD Pipeline**: Automated pipelines (Gitea Actions) run tests, build/push Docker images, and trigger deployments.
|
||||||
|
- **Gitea Actions Workflows**: Located under `.gitea/workflows/`, these workflows handle testing, building, pushing, and deploying the application.
|
||||||
|
- **Gitea Action Runners**: Self-hosted runners execute the CI/CD workflows.
|
||||||
|
- **Testing and Continuous Integration**: Automated tests ensure code quality before deployment, also documented in [Testing & CI](07_deployment/07_01_testing_ci.md.md).
|
||||||
|
- **Docker Infrastructure**: Docker is used to containerize the application for consistent deployment across environments.
|
||||||
|
- **Portainer**: Production deployment environment for managing Docker containers.
|
||||||
|
- **Web Server**: Hosts the FastAPI application and serves API endpoints.
|
||||||
|
- **Database Server**: PostgreSQL database for persisting application data.
|
||||||
|
- **Static File Server**: Serves static assets such as CSS, JavaScript, and image files.
|
||||||
- **Cloud Infrastructure (optional)**: The application can be deployed on cloud platforms.
|
- **Cloud Infrastructure (optional)**: The application can be deployed on cloud platforms.
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
graph TD
|
graph TD
|
||||||
A[Web Server] --> B[Database Server]
|
W[Web Server] --> DB[Database Server]
|
||||||
A --> C[Static File Server]
|
W --> S[Static File Server]
|
||||||
A --> D[Reverse Proxy]
|
P[Reverse Proxy] --> W
|
||||||
A --> E[Containerization]
|
C[CI/CD Pipeline] --> W
|
||||||
A --> F[CI/CD Pipeline]
|
F[Containerization] --> W
|
||||||
A --> G[Cloud Infrastructure]
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Environments
|
## Environments
|
||||||
@@ -74,7 +78,7 @@ The production environment is set up for serving live traffic and includes:
|
|||||||
|
|
||||||
## Containerized Deployment Flow
|
## Containerized Deployment Flow
|
||||||
|
|
||||||
The Docker-based deployment path aligns with the solution strategy documented in [04 — Solution Strategy](04_solution_strategy.md) and the CI practices captured in [14 — Testing & CI](14_testing_ci.md).
|
The Docker-based deployment path aligns with the solution strategy documented in [Solution Strategy](04_solution_strategy.md) and the CI practices captured in [Testing & CI](07_deployment/07_01_testing_ci.md.md).
|
||||||
|
|
||||||
### Image Build
|
### Image Build
|
||||||
|
|
||||||
@@ -95,7 +99,7 @@ The Docker-based deployment path aligns with the solution strategy documented in
|
|||||||
- `build-and-push.yml` logs into the container registry, rebuilds the Docker image using GitHub Actions cache-backed layers, and pushes `latest` (and additional tags as required).
|
- `build-and-push.yml` logs into the container registry, rebuilds the Docker image using GitHub Actions cache-backed layers, and pushes `latest` (and additional tags as required).
|
||||||
- `deploy.yml` connects to the target host via SSH, pulls the pushed tag, stops any existing container, and launches the new version.
|
- `deploy.yml` connects to the target host via SSH, pulls the pushed tag, stops any existing container, and launches the new version.
|
||||||
- Required secrets: `REGISTRY_URL`, `REGISTRY_USERNAME`, `REGISTRY_PASSWORD`, `SSH_HOST`, `SSH_USERNAME`, `SSH_PRIVATE_KEY`.
|
- Required secrets: `REGISTRY_URL`, `REGISTRY_USERNAME`, `REGISTRY_PASSWORD`, `SSH_HOST`, `SSH_USERNAME`, `SSH_PRIVATE_KEY`.
|
||||||
- Extend these workflows when introducing staging/blue-green deployments; keep cross-links with [14 — Testing & CI](14_testing_ci.md) up to date.
|
- Extend these workflows when introducing staging/blue-green deployments; keep cross-links with [Testing & CI](07_deployment/07_01_testing_ci.md.md) up to date.
|
||||||
|
|
||||||
## Integrations and Future Work (deployment-related)
|
## Integrations and Future Work (deployment-related)
|
||||||
|
|
||||||
|
|||||||
@@ -16,11 +16,11 @@ This folder mirrors the arc42 chapter structure (adapted to Markdown).
|
|||||||
- [05 Building Block View](05_building_block_view.md)
|
- [05 Building Block View](05_building_block_view.md)
|
||||||
- [06 Runtime View](06_runtime_view.md)
|
- [06 Runtime View](06_runtime_view.md)
|
||||||
- [07 Deployment View](07_deployment_view.md)
|
- [07 Deployment View](07_deployment_view.md)
|
||||||
|
- [Testing & CI](07_deployment/07_01_testing_ci.md.md)
|
||||||
- [08 Concepts](08_concepts.md)
|
- [08 Concepts](08_concepts.md)
|
||||||
- [09 Architecture Decisions](09_architecture_decisions.md)
|
- [09 Architecture Decisions](09_architecture_decisions.md)
|
||||||
- [10 Quality Requirements](10_quality_requirements.md)
|
- [10 Quality Requirements](10_quality_requirements.md)
|
||||||
- [11 Technical Risks](11_technical_risks.md)
|
- [11 Technical Risks](11_technical_risks.md)
|
||||||
- [12 Glossary](12_glossary.md)
|
- [12 Glossary](12_glossary.md)
|
||||||
- [13 UI and Style](13_ui_and_style.md)
|
- [13 UI and Style](13_ui_and_style.md)
|
||||||
- [14 Testing & CI](14_testing_ci.md)
|
|
||||||
- [15 Development Setup](15_development_setup.md)
|
- [15 Development Setup](15_development_setup.md)
|
||||||
|
|||||||
@@ -245,7 +245,7 @@ The database contains tables such as `capex`, `opex`, `chemical_consumption`, `f
|
|||||||
## Where to look next
|
## Where to look next
|
||||||
|
|
||||||
- Architecture overview & chapters: [architecture](architecture/README.md) (per-chapter files under `docs/architecture/`)
|
- Architecture overview & chapters: [architecture](architecture/README.md) (per-chapter files under `docs/architecture/`)
|
||||||
- [Testing & CI](architecture/14_testing_ci.md)
|
- [Testing & CI](architecture/07_deployment/07_01_testing_ci.md.md)
|
||||||
- [Development setup](architecture/15_development_setup.md)
|
- [Development setup](architecture/15_development_setup.md)
|
||||||
- Implementation plan & roadmap: [Solution strategy](architecture/04_solution_strategy.md)
|
- Implementation plan & roadmap: [Solution strategy](architecture/04_solution_strategy.md)
|
||||||
- Routes: [routes](../routes/)
|
- Routes: [routes](../routes/)
|
||||||
|
|||||||
Reference in New Issue
Block a user