docs: refresh CI + lint/format docs (Phase 8 + Phase 13)
CI / fast-checks (pull_request) Successful in 1m23s
CI / api (pull_request) Successful in 1m0s
CI / harness (pull_request) Successful in 3m27s

This commit is contained in:
Lucas Berger
2026-06-11 22:21:23 -04:00
parent 63ae0c69d4
commit 213aeba347
5 changed files with 378 additions and 60 deletions
+100 -7
View File
@@ -24,6 +24,61 @@ The production compose file brings up three services:
---
## CI/CD Pipeline
FamilySync uses a self-hosted Gitea Actions runner. Two workflows govern the release path.
### PR gate — `.gitea/workflows/ci.yml`
Triggered on every pull request targeting `main`. Three jobs run in parallel; all three must pass before the PR can be merged:
| Job | What it checks |
| ------------- | --------------------------------------------------------------------------------- |
| `fast-checks` | Lint (`pnpm lint`), format check (`pnpm format:check`), typecheck, PWA unit tests |
| `api` | DB migrations + API integration tests against a live MariaDB service container |
| `harness` | Full Playwright E2E suite (iPhone + Pixel profiles) against the compiled API |
A PR with lint or format violations is blocked from merging by the `fast-checks` job.
Branch protection on `main` blocks direct push and force push. Only PRs with all three required checks (`CI / fast-checks`, `CI / api`, `CI / harness`) passing can merge.
### Publish — `.gitea/workflows/publish.yml`
Triggered on push to `main` (i.e., when any PR merges). Builds the `apps/api` Docker image and pushes it to the Gitea container registry.
**Registry:** `git.bergerhouse.net/luckberg/familysync-api`
**Image tags produced per merge:**
| Tag | Example | Purpose |
| ------------------------- | --------------- | ------------------------------------------ |
| `:latest` | `:latest` | Moving pointer for easy `docker pull` |
| `:<milestone>-<shortsha>` | `:v1.1-98acff8` | Immutable, rollback-traceable (7-char SHA) |
The current milestone prefix (`v1.1`) is set in the `MILESTONE` env var at the top of `publish.yml`. Update it at milestone boundaries.
The immutable `:<milestone>-<sha>` tag is pushed first. `:latest` is only moved after the immutable tag has landed, so a failed second push can never leave `:latest` advanced without a corresponding rollback tag.
**Authentication — `REGISTRY_PAT` secret:**
The workflow authenticates with the Gitea container registry using a PAT stored in the `REGISTRY_PAT` Actions secret. The secret must have `write:package` scope. It is named `REGISTRY_PAT` — not `GITEA_REGISTRY_PAT` or any `GITEA_`-prefixed name, because Gitea reserves the `GITEA_` prefix and will reject those names at secret-creation time. `GITEA_TOKEN` and `GITHUB_TOKEN` cannot push packages.
The PAT is passed via `--password-stdin` (never via `-p`/`--password`) and is bound through `env:` so it is never interpolated into the script body:
```yaml
env:
REGISTRY_PAT: ${{ secrets.REGISTRY_PAT }}
run: |
printf '%s' "$REGISTRY_PAT" | \
docker login git.bergerhouse.net \
--username luckberg \
--password-stdin
```
The credential is purged from the runner with `docker logout` in an `if: always()` step after every push.
---
## Prerequisites
- Docker and Docker Compose available on the Unraid host.
@@ -163,7 +218,40 @@ Ensure `OIDC_AUTH_EXTERNAL_URL` and `OIDC_REDIRECT_URI` in `.env` match the publ
---
## Build and Start
## Pulling the Published Image on Unraid
After a PR merges, `publish.yml` pushes two tags to the Gitea registry. To deploy the latest build on the Unraid host:
```bash
# Pull the moving :latest pointer
docker pull git.bergerhouse.net/luckberg/familysync-api:latest
# Or pin to a specific immutable tag (recommended for production)
docker pull git.bergerhouse.net/luckberg/familysync-api:v1.1-98acff8
```
<!-- VERIFY: Confirm the Gitea registry requires authentication for pulls on the Unraid host (docker login git.bergerhouse.net) -->
Update `docker-compose.yml` to reference the pre-built image instead of building locally:
```yaml
services:
api:
image: git.bergerhouse.net/luckberg/familysync-api:latest
# remove the build: block when using the published image
```
Then restart the service:
```bash
docker compose pull api && docker compose up -d api
```
---
## Build and Start (local build)
If you need to build locally rather than pull from the registry:
```bash
# From the repo root — builds both the API and the React PWA into one image
@@ -206,13 +294,18 @@ A `503` response (`{"ok":false,"db":"down"}`) means the API cannot reach MariaDB
## Rollback
There is no automated rollback pipeline. To revert to a previous build:
To revert to a specific prior build, use the immutable image tag produced by `publish.yml`:
1. Identify the prior working Git commit.
2. Stop the API: `docker compose stop api`.
3. Rebuild from the target commit: `git checkout <commit> && docker compose build api`.
4. Start: `docker compose up -d api`.
5. If the rollback crosses a schema migration boundary, restore the MariaDB volume from a backup — schema downgrades are not supported by Drizzle Kit's migrate command.
```bash
# Identify the immutable tag from the Gitea registry or CI run history
# e.g. git.bergerhouse.net/luckberg/familysync-api:v1.1-98acff8
docker compose stop api
# Update docker-compose.yml image: line to the target immutable tag, then:
docker compose pull api && docker compose up -d api
```
If the rollback crosses a schema migration boundary, restore the MariaDB volume from a backup — schema downgrades are not supported by Drizzle Kit's migrate command.
Take a MariaDB dump before every deployment that includes a migration: