Skip to content

Commit c86e7ad

Browse files
committed
ci(deploy): harden GitHub publish workflow + document image sources
Bump docker/build-push-action v5->v7, metadata-action v5->v6, login-action v3->v4 (safe to use latest majors on GitHub-hosted runners), and add concurrency grouped by ref so tag/release publishes are isolated. Also add a 'Container images & custom registries' section to docs/INSTALL.md: GHCR by default, REGISTRY/REPOSITORY/IMAGE_TAG overrides, and a build-from-source path for forks/offline.
1 parent bfe5950 commit c86e7ad

3 files changed

Lines changed: 61 additions & 6 deletions

File tree

.github/workflows/docker-publish.yml

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,13 @@ on:
88
# Allow manual trigger from the GitHub Actions UI
99
workflow_dispatch:
1010

11+
# One publish pipeline per ref at a time; a newer push cancels a stale run on
12+
# the same ref. Tag builds (refs/tags/*) form their own groups, so release
13+
# publishes never cancel each other or a main build.
14+
concurrency:
15+
group: ${{ github.workflow }}-${{ github.ref }}
16+
cancel-in-progress: true
17+
1118
env:
1219
REGISTRY: ghcr.io
1320
IMAGE_NAME: ${{ github.repository }}
@@ -30,15 +37,15 @@ jobs:
3037
uses: actions/checkout@v4
3138

3239
- name: Log in to the Container registry
33-
uses: docker/login-action@v3
40+
uses: docker/login-action@v4
3441
with:
3542
registry: ${{ env.REGISTRY }}
3643
username: ${{ github.actor }}
3744
password: ${{ secrets.GITHUB_TOKEN }}
3845

3946
- name: Extract metadata (tags, labels) for Docker
4047
id: meta
41-
uses: docker/metadata-action@v5
48+
uses: docker/metadata-action@v6
4249
with:
4350
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/health-assistant-backend
4451
tags: |
@@ -49,7 +56,7 @@ jobs:
4956
type=raw,value=latest,enable={{is_default_branch}}
5057
5158
- name: Build and push Backend Docker image
52-
uses: docker/build-push-action@v5
59+
uses: docker/build-push-action@v7
5360
with:
5461
context: .
5562
file: docker/Dockerfile
@@ -70,15 +77,15 @@ jobs:
7077
uses: actions/checkout@v4
7178

7279
- name: Log in to the Container registry
73-
uses: docker/login-action@v3
80+
uses: docker/login-action@v4
7481
with:
7582
registry: ${{ env.REGISTRY }}
7683
username: ${{ github.actor }}
7784
password: ${{ secrets.GITHUB_TOKEN }}
7885

7986
- name: Extract metadata (tags, labels) for Docker
8087
id: meta
81-
uses: docker/metadata-action@v5
88+
uses: docker/metadata-action@v6
8289
with:
8390
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/health-assistant-frontend
8491
tags: |
@@ -89,7 +96,7 @@ jobs:
8996
type=raw,value=latest,enable={{is_default_branch}}
9097
9198
- name: Build and push Frontend Docker image
92-
uses: docker/build-push-action@v5
99+
uses: docker/build-push-action@v7
93100
with:
94101
context: .
95102
file: docker/Dockerfile.frontend

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1212
1313
## [Unreleased]
1414

15+
- **CI/CD: hardened the GitHub image-publish workflow (`.github/workflows/docker-publish.yml`).** Bumped the Docker actions to current majors now that this workflow runs on GitHub-hosted runners (no self-hosted act-runner compatibility concern, unlike the Gitea pipeline): `docker/build-push-action@v5``@v7` (v6 is already superseded), `docker/metadata-action@v5``@v6`, `docker/login-action@v3``@v4`. Added a `concurrency` block grouped by `${{ github.ref }}` with `cancel-in-progress: true` — a newer push to `main` cancels a stale publish run, while tag-driven release publishes (`refs/tags/*`) form their own groups and never cancel each other or a main build. `actions/checkout@v4` left as-is (current). Brings the GitHub publish path to parity with the Gitea hardening.
16+
17+
- **Docs: `INSTALL.md` documents the container image model — GHCR by default, custom registries, and building from source.** The compose files are pull-only (no `build:` step) and default to `ghcr.io/health-assistant-io/health-assistant`, but INSTALL.md never said where images come from or that the source is configurable — so a self-hoster who doesn't use the project's Gitea CI had no documented "just pull the images" path even though it already works. Added a **"Container images & custom registries"** subsection under Production Deployment: explains that every flavor (and the quick start) pulls pre-built images from GHCR by default, with a table of the `REGISTRY` / `REPOSITORY` / `IMAGE_TAG` env vars to redirect to a private registry/mirror, point at a fork's namespace, or pin a release tag for reproducible deploys; plus a "run from source" path (build the default tags locally so `compose up` reuses them instead of pulling) for offline/fork/no-registry cases. Closes the non-Gitea deployer gap.
18+
1519
- **CI/CD: Gitea Actions deploy pipeline unblocked — job image pulled from Docker Hub instead of `docker.gitea.com`.** The runner timed out fetching job containers (`Get "https://docker.gitea.com/v2/": context deadline exceeded`), stalling every job at the pull step. `.gitea/workflows/deploy.yml` now pins `container.image: gitea/runner-images:ubuntu-latest` (the Docker Hub mirror of the default image — Docker CLI included so `build-and-push` still runs; `-slim` has no Docker CLI, `-full` is a 16 GB overkill) on all four jobs, and gains `concurrency` (cancel superseded runs per branch) and least-privilege `permissions` (`contents: read`, `packages: write`). Stale actions bumped: `docker/login-action@v2`→`v3`, `appleboy/ssh-action` + `scp-action` `@master`→`@v1` (master branches are unpinned); `checkout@v4` / `setup-python@v5` / `setup-node@v4` left as-is (current and known-good on Gitea's act runtime — avoided jumping to the days-old v7 majors). `docs/CI_CD_SETUP.md` §3A documents the canonical runner-side fix: set `runner.labels` (`ubuntu-latest:docker://gitea/runner-images:ubuntu-latest`, plus 22.04/24.04) in the runner's `config.yaml` (Gitea 1.21+) so every workflow inherits the image, applied via `docker compose restart runner` + `docker pull`. The per-job `container:` blocks stay as defense-in-depth so the pipeline isn't solely dependent on out-of-repo runner config.
1620

1721
- **Dev: `./scripts/run-dev.sh --no-admin` skips admin creation so the first-run setup wizard can be exercised.** The dev script previously always ran `create_system_admin.py` (`admin@healthassistant.local` / `admin123`), which left a user row in the DB — so `GET /auth/setup-status` returned `initialized=true` and the new `/setup` wizard could never be reached in dev. New `--no-admin` flag gates that step; default behavior is unchanged. The script prints a hint pointing at the wizard URL and at `psql … TRUNCATE users, tenants …` for resetting a DB that already has the admin. Documented in `DEVELOPMENT.md` alongside `reset-dev-db.sh` (the clean-slate path: `reset-dev-db.sh -y --migrate` then `run-dev.sh --no-admin`). bash syntax + arg parsing verified.

docs/INSTALL.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,50 @@ This flavor runs the application containers without an internal proxy. By defaul
150150
docker compose --env-file .env -f docker/docker-compose.prod.yml up -d
151151
```
152152

153+
### Container images & custom registries
154+
155+
The compose files pull **pre-built images** — they have no `build:` step, so `docker compose up` fetches images from a registry rather than building locally. This applies to every flavor above and to the quick start. By default the images come from the public GitHub Container Registry:
156+
157+
```
158+
ghcr.io/health-assistant-io/health-assistant/health-assistant-backend:latest
159+
ghcr.io/health-assistant-io/health-assistant/health-assistant-frontend:latest
160+
```
161+
162+
Three environment variables (set in `.env`) redirect the images without editing the compose file:
163+
164+
| Variable | Default | Purpose |
165+
|---|---|---|
166+
| `REGISTRY` | `ghcr.io` | Point at your own registry or mirror (a private registry, an air-gapped mirror, etc.) |
167+
| `REPOSITORY` | `health-assistant-io/health-assistant` | Your namespace or fork |
168+
| `IMAGE_TAG` | `latest` | Pin a specific release for reproducible deploys |
169+
170+
**Pin a release** — recommended for production, so an upstream `latest` push can't change your running version. Add to `.env`:
171+
172+
```bash
173+
IMAGE_TAG=0.3.2 # example — use a tag published to your registry (see CHANGELOG.md)
174+
```
175+
176+
**Use your own registry** — a fork that publishes its own images, or an internal mirror. Add to `.env`:
177+
178+
```bash
179+
REGISTRY=registry.yourdomain.tld
180+
REPOSITORY=myorg/health-assistant
181+
```
182+
183+
**Run from source** — no registry, offline, or a modified build. Since the compose files only pull, build and tag the images locally first; `docker compose up` then reuses the local images instead of pulling:
184+
185+
```bash
186+
docker build -t ghcr.io/health-assistant-io/health-assistant/health-assistant-backend:latest -f docker/Dockerfile .
187+
```
188+
189+
```bash
190+
docker build -t ghcr.io/health-assistant-io/health-assistant/health-assistant-frontend:latest -f docker/Dockerfile.frontend .
191+
```
192+
193+
```bash
194+
docker compose --env-file .env -f docker/docker-compose.standalone.yml up -d
195+
```
196+
153197
### Security Checklist
154198

155199
- [ ] Change `SECRET_KEY` to a secure random value *(handled by `setup_env.py` if used)*

0 commit comments

Comments
 (0)