Set up Gitea Actions Docker build workflow #4

Closed
opened 2026-02-27 23:48:29 +01:00 by timothy · 1 comment
Owner

Background

The Gitea Actions runner and container registry are now live on jazz (see server-management#172). The ersatztv-ffmpeg base image is building successfully. The main ErsatzTV repo needs its own .gitea/workflows/docker-build.yml to build the app image.

Current State

  • The repo still has upstream .github/workflows/ (docker.yml, ci.yml, etc.) — these target Docker Hub/GHCR and multi-arch (amd64/arm32/arm64)
  • No .gitea/workflows/ directory exists yet
  • The Dockerfile at docker/Dockerfile references ghcr.io/ersatztv/ersatztv-ffmpeg:7.1.1 as base image — needs updating to 192.168.1.95:3000/timothy/ersatztv-ffmpeg:7.1.1

Tasks

  • Create .gitea/workflows/docker-build.yml — simplified amd64-only build targeting the Gitea registry
  • Update docker/Dockerfile to use 192.168.1.95:3000/timothy/ersatztv-ffmpeg:7.1.1 as base image
  • Configure buildx layer caching (cache-from/cache-to) — .NET restore + build are slow without cache
  • Set REGISTRY_USER and REGISTRY_PASSWORD secrets (already done)
  • Test a full build end-to-end and verify the image runs
  • The built image should be a test container — NOT replacing the production ghcr.io/ersatztv/ersatztv:latest that's currently running

Notes

  • The upstream docker.yml is a workflow_call (called from release.yml) with multi-arch matrix builds and manifest merging — we don't need any of that, just a simple amd64 build+push
  • Build args: INFO_VERSION (version string), BUILD_CONFIG (release)
  • Depends on the ffmpeg base image being available in the registry (ersatztv-ffmpeg#1 or equivalent)
## Background The Gitea Actions runner and container registry are now live on jazz (see server-management#172). The `ersatztv-ffmpeg` base image is building successfully. The main ErsatzTV repo needs its own `.gitea/workflows/docker-build.yml` to build the app image. ## Current State - The repo still has upstream `.github/workflows/` (docker.yml, ci.yml, etc.) — these target Docker Hub/GHCR and multi-arch (amd64/arm32/arm64) - No `.gitea/workflows/` directory exists yet - The Dockerfile at `docker/Dockerfile` references `ghcr.io/ersatztv/ersatztv-ffmpeg:7.1.1` as base image — needs updating to `192.168.1.95:3000/timothy/ersatztv-ffmpeg:7.1.1` ## Tasks - [ ] Create `.gitea/workflows/docker-build.yml` — simplified amd64-only build targeting the Gitea registry - [ ] Update `docker/Dockerfile` to use `192.168.1.95:3000/timothy/ersatztv-ffmpeg:7.1.1` as base image - [ ] Configure buildx layer caching (`cache-from`/`cache-to`) — .NET restore + build are slow without cache - [ ] Set `REGISTRY_USER` and `REGISTRY_PASSWORD` secrets (already done) - [ ] Test a full build end-to-end and verify the image runs - [ ] The built image should be a **test container** — NOT replacing the production `ghcr.io/ersatztv/ersatztv:latest` that's currently running ## Notes - The upstream `docker.yml` is a `workflow_call` (called from `release.yml`) with multi-arch matrix builds and manifest merging — we don't need any of that, just a simple amd64 build+push - Build args: `INFO_VERSION` (version string), `BUILD_CONFIG` (release) - Depends on the ffmpeg base image being available in the registry (ersatztv-ffmpeg#1 or equivalent)
timothy added the ci-cdenhancementpriority: high labels 2026-03-08 23:02:34 +01:00
Author
Owner

Done — the Gitea Actions build pipeline is live on main and verified end-to-end (run #129).

What was done

  • .gitea/workflows/docker-build.yml — test-gated (dotnet build+test) amd64 build → push to the Gitea registry. Tags: push main → :latest + :<sha>; push v* → :prod + :<version> + :<sha>; PRs run tests only; buildx registry layer caching; an HTTP smoke test (docker exec python3 → :8409) that verifies the container actually serves before the run goes green.
  • docker/Dockerfile — base image → 192.168.1.95:3000/timothy/ersatztv-ffmpeg:7.1.1; also copies Directory.Build.props/.targets/global.json/.editorconfig so the image build matches local/CI.
  • Removed the upstream .github/workflows/* (+ dead dependabot.yml/FUNDING.yml) — they targeted GHCR/DockerHub/Azure/Apple and ran as noise (incl. a daily stale cron) on the runner.
  • Hardened after an adversarial self-review: concurrency guard (single runner), PR test gate, publish only from main/v*, real HTTP smoke + trap cleanup, NU1904 blocks repo-wide, build-config/SDK consistency.

Root cause of the one real blocker found

.NET 10 enables NuGet audit on restore; the app projects set TreatWarningsAsErrors, so vulnerable transitive deps (NCalcSync 5.11.0, SQLitePCLRaw 2.1.10) failed dotnet restore for both the test job and the Docker build. Fixed by demoting NU1901-1903 to warnings (NU1904/critical still blocks) in Directory.Build.props; underlying deps tracked in #8.

Verified

192.168.1.95:3000/timothy/ersatztv:latest (+ :<sha>) in the registry; smoke test confirms HTTP serving; build ~3-4 min with the layer cache warm.

Deferred / handed off

The test/prod containers (compose, ports 8410/8409, volumes, registry pull, Watchtower) are infra → server-management#481. The :prod image only exists once a v* tag is pushed (none yet). This image is a test image and does not replace the running prod ghcr.io/ersatztv/ersatztv:latest.

Follow-ups opened

#8 (vuln deps), #9 (FFmpeg 8 app), ersatztv-ffmpeg#4 (FFmpeg 8 base), #10 (contributors guide), #11–#16 (test/CI foundations), server-management#481 (test/prod containers), server-management#482 (prod backup).

Docs

docs/ci-cd.md rewritten to the implemented pipeline; CLAUDE.md + project memory updated.

Closing.

Done — the Gitea Actions build pipeline is live on `main` and verified end-to-end (run #129). ## What was done - **`.gitea/workflows/docker-build.yml`** — test-gated (`dotnet build`+`test`) amd64 build → push to the Gitea registry. Tags: push `main` → `:latest` + `:<sha>`; push `v*` → `:prod` + `:<version>` + `:<sha>`; PRs run tests only; buildx registry layer caching; an HTTP smoke test (`docker exec python3` → `:8409`) that verifies the container actually **serves** before the run goes green. - **`docker/Dockerfile`** — base image → `192.168.1.95:3000/timothy/ersatztv-ffmpeg:7.1.1`; also copies `Directory.Build.props`/`.targets`/`global.json`/`.editorconfig` so the image build matches local/CI. - Removed the upstream `.github/workflows/*` (+ dead `dependabot.yml`/`FUNDING.yml`) — they targeted GHCR/DockerHub/Azure/Apple and ran as noise (incl. a daily stale cron) on the runner. - Hardened after an adversarial self-review: `concurrency` guard (single runner), PR test gate, publish only from `main`/`v*`, real HTTP smoke + `trap` cleanup, NU1904 blocks repo-wide, build-config/SDK consistency. ## Root cause of the one real blocker found .NET 10 enables NuGet audit on restore; the app projects set `TreatWarningsAsErrors`, so vulnerable transitive deps (NCalcSync 5.11.0, SQLitePCLRaw 2.1.10) failed `dotnet restore` for **both** the test job and the Docker build. Fixed by demoting NU1901-1903 to warnings (NU1904/critical still blocks) in `Directory.Build.props`; underlying deps tracked in #8. ## Verified `192.168.1.95:3000/timothy/ersatztv:latest` (+ `:<sha>`) in the registry; smoke test confirms HTTP serving; build ~3-4 min with the layer cache warm. ## Deferred / handed off The test/prod **containers** (compose, ports 8410/8409, volumes, registry pull, Watchtower) are infra → **server-management#481**. The `:prod` image only exists once a `v*` tag is pushed (none yet). This image is a **test** image and does not replace the running prod `ghcr.io/ersatztv/ersatztv:latest`. ## Follow-ups opened #8 (vuln deps), #9 (FFmpeg 8 app), ersatztv-ffmpeg#4 (FFmpeg 8 base), #10 (contributors guide), #11–#16 (test/CI foundations), server-management#481 (test/prod containers), server-management#482 (prod backup). ## Docs `docs/ci-cd.md` rewritten to the implemented pipeline; `CLAUDE.md` + project memory updated. Closing.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: timothy/ersatztv#4