6.0 KiB
Guide 26: VigilCareRecords CI/CD (Gitea Actions + Docker Compose)
How VigilCareRecords is built, tested, and deployed on the same Gitea Actions + act_runner + container registry setup already used by VigilCareClinical. See docs/25-gitea-cicd-docker-deploy.md for the general pattern this guide instantiates; this doc only covers what is specific to this repo.
Related files:
.gitea/workflows/ci.yml/.gitea/workflows/cd.ymldocker-compose.yml(local + CI dependencies)docker-compose.prod.yml(production app stack).env.exampleVigilCareRecordsAPI/Dockerfile/vigilcare-records-web/Dockerfile
1. Topology
VigilCareRecords ships two images:
| Image | Dockerfile | Build context |
|---|---|---|
vigilcare-records-api |
VigilCareRecordsAPI/Dockerfile |
repo root |
vigilcare-records-dashboard |
vigilcare-records-web/Dockerfile |
vigilcare-records-web/ |
Unlike VigilCareClinical, there is no gateway service, and the frontend does not bake in an absolute API URL at build time — src/api/client.ts and src/api/fhirClient.ts use relative base URLs (/api/v1, /fhir). The dashboard's nginx.conf reverse-proxies those paths to the api container, so the same image works behind any hostname without a build-arg.
2. Shared production infrastructure
Per the "Integrated Database Deployment" decision documented in vigilcare-records-clinical-overview.md, VigilCareRecords does not run its own Postgres/Redis/MinIO/Seq in production. It joins the external shared-services Docker network already created by VigilCareClinical's own compose project and connects to those same containers, using:
- Its own logical Postgres database (
vigilcare_records, separate schema/tables from VigilCareClinical'spatients/encounters/observations— see the promotion service for how the two connect at the application layer, not the infrastructure layer) - A dedicated Redis logical database (
defaultDatabase=2in.env.example) so batch-assignment locks never collide with VigilCareClinical's keys - A dedicated MinIO bucket (
vigilcare-records-scans) separate from any clinical document buckets
Before the first deploy, confirm the VigilCareClinical infra stack (or whatever compose project owns shared-services) is already running on the deploy host — docker network ls | grep shared-services should show it. docker compose -f docker-compose.prod.yml up will fail to find the network otherwise.
3. One-time host setup
ssh deploy@YOUR_HOST "mkdir -p /opt/vigilcare-records"
scp .env deploy@YOUR_HOST:/opt/vigilcare-records/.env
ssh deploy@YOUR_HOST "chmod 600 /opt/vigilcare-records/.env"
Fill in .env from .env.example first — generate JWT_SECRET with openssl rand -base64 48, and get real credentials for the shared Postgres/Redis/MinIO/Seq services from whoever manages that stack. CD never uploads or overwrites .env; only the IMAGE_TAG line is patched automatically on each release.
Create the Records MinIO bucket once (the API also creates it on startup if missing):
ssh deploy@YOUR_HOST bash -euo pipefail <<'EOF'
cd /opt/vigilcare-records
env_val() { sed -n "s/^${1}=//p" .env | tail -n1 | tr -d '\r'; }
ACCESS="$(env_val MINIO_ACCESS_KEY)"
SECRET="$(env_val MINIO_SECRET_KEY)"
BUCKET="$(env_val MINIO_BUCKET_NAME)"; BUCKET="${BUCKET:-vigilcare-records-scans}"
docker run --rm --network shared-services --entrypoint /bin/sh minio/mc \
-c "mc alias set local http://minio:9000 '${ACCESS}' '${SECRET}' && mc mb --ignore-existing local/${BUCKET}"
EOF
4. Gitea secrets and variables
Repo → Settings → Actions.
Secrets
| Secret | Used by |
|---|---|
REGISTRY_USERNAME |
docker login |
REGISTRY_TOKEN |
docker login (access token / PAT with package write) |
PG_CONNECTION_DDL |
migrate job only — DDL-privileged connection (Host=postgres on shared-services), never given to the API container |
DEPLOY_HOST |
SSH / SCP |
DEPLOY_USER |
SSH / SCP |
DEPLOY_SSH_KEY |
Private key PEM / OpenSSH private key body |
Variables
| Variable | Used by |
|---|---|
REGISTRY |
Optional override of the default git.vectur45.com/trent/vigilcare-records |
5. Release flow
# CI green on master, then:
git tag v1.0.0
git push origin v1.0.0
This triggers .gitea/workflows/cd.yml: build & push both images → SSH the EF migrations bundle onto the deploy host and run it on shared-services with PG_CONNECTION_DDL → SSH deploy (scp the prod compose file, patch IMAGE_TAG, pull + up -d) → smoke test (/health/ready, dashboard /) → automatic rollback to the previous IMAGE_TAG on failure (schema changes are not reverted; see the expand/contract note in cd.yml).
Manual redeploy of an existing tag: Gitea UI → Actions → CD → Run workflow, with image_tag input.
6. Pre-production checklist (application-level, not part of this CI/CD change)
- Demo data seeding runs unconditionally on startup.
Program.cscallsDataSeeder.SeedAsync(db)wheneverASPNETCORE_ENVIRONMENTis notTesting— includingProduction. Unlike the VigilCareClinical example (Seeding__EnableDemoData=false), this app has no seeding toggle. Before a real clinical deploy, either add a guard aroundDataSeeder.SeedAsyncforProduction, or confirm the twelve seeded demo accounts (all passwordpassword) are acceptable/rotated before go-live. - Confirm
Cors:AllowedOrigins/DASHBOARD_ORIGINmatches the real public dashboard origin if the dashboard is ever served from a different origin than the API (not the default same-origin nginx-proxy setup described above). - Rotate
JWT_SECRETfrom any value used during testing before the first real deploy.