From 783bcfdcb53e115d8e1d917e061d2c8cd771a476 Mon Sep 17 00:00:00 2001 From: Jasper Van Date: Thu, 18 Jun 2026 22:59:03 -0400 Subject: [PATCH] ci: publish :dev and :nightly docker images from main (#457) The Docker image was only built on release tags (`v*`), so there was no published image tracking the latest code on main. - ci.yml: add `docker-dev` job that pushes `:dev`/`:dev-cli` on every green push to main (gated on check + docker-smoke; skipped on PRs/forks). - docker-nightly.yml: scheduled (03:27 UTC) no-cache rebuild publishing `:nightly`/`:nightly-cli` so base-image/OS security patches land daily even when no code changes. - docs/deploy/docker.md: document the tag scheme. `:latest` stays pinned to releases; `:dev`/`:nightly` are moving, unreviewed tags. Co-authored-by: Claude Opus 4.8 (1M context) --- .github/workflows/ci.yml | 48 ++++++++++++++++++++++++ .github/workflows/docker-nightly.yml | 56 ++++++++++++++++++++++++++++ docs/deploy/docker.md | 12 ++++++ 3 files changed, 116 insertions(+) create mode 100644 .github/workflows/docker-nightly.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f0469405..d4f4116f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -99,6 +99,54 @@ jobs: if: always() run: docker compose -f docker-compose.yml down -v + docker-dev: + name: Docker dev image + runs-on: ubuntu-latest + needs: [check, docker-smoke] + # Publish the bleeding-edge `:dev` image only for green pushes to main on the + # canonical repo — never on PRs or forks. `:latest` stays pinned to releases. + if: github.repository == 'saltbo/zpan' && github.event_name == 'push' && github.ref == 'refs/heads/main' + permissions: + contents: read + packages: write + steps: + - uses: actions/checkout@v6 + + - uses: docker/login-action@v4 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - uses: docker/setup-buildx-action@v4 + + - name: Build & push server image + uses: docker/build-push-action@v7 + with: + context: . + push: true + platforms: linux/amd64,linux/arm64 + build-args: | + APP_VERSION=dev + APP_COMMIT=${{ github.sha }} + tags: ghcr.io/${{ github.repository }}:dev + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Build & push CLI image + uses: docker/build-push-action@v7 + with: + context: . + target: cli + push: true + platforms: linux/amd64,linux/arm64 + build-args: | + APP_VERSION=dev + APP_COMMIT=${{ github.sha }} + tags: ghcr.io/${{ github.repository }}:dev-cli + cache-from: type=gha + cache-to: type=gha,mode=max + cf-deploy-dry-run: name: CF deploy dry-run runs-on: ubuntu-latest diff --git a/.github/workflows/docker-nightly.yml b/.github/workflows/docker-nightly.yml new file mode 100644 index 00000000..a79acf2c --- /dev/null +++ b/.github/workflows/docker-nightly.yml @@ -0,0 +1,56 @@ +name: Docker Nightly + +# A genuine daily rebuild of main's HEAD. Unlike `:dev` (which only rebuilds when +# code lands), this runs on a schedule with no cache so it picks up base-image and +# OS security updates (node/debian, aria2, qbittorrent-nox) even on quiet days. +# Scheduled runs only fire on the default branch, so this always tracks main. +on: + schedule: + - cron: '27 3 * * *' # 03:27 UTC daily — minute offset dodges GitHub's congested top-of-hour scheduling + workflow_dispatch: + +jobs: + nightly: + name: Nightly image + runs-on: ubuntu-latest + if: github.repository == 'saltbo/zpan' + permissions: + contents: read + packages: write + steps: + - uses: actions/checkout@v6 + + - uses: docker/login-action@v4 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - uses: docker/setup-buildx-action@v4 + + - name: Build & push server image + uses: docker/build-push-action@v7 + with: + context: . + push: true + pull: true + no-cache: true + platforms: linux/amd64,linux/arm64 + build-args: | + APP_VERSION=nightly + APP_COMMIT=${{ github.sha }} + tags: ghcr.io/${{ github.repository }}:nightly + + - name: Build & push CLI image + uses: docker/build-push-action@v7 + with: + context: . + target: cli + push: true + pull: true + no-cache: true + platforms: linux/amd64,linux/arm64 + build-args: | + APP_VERSION=nightly + APP_COMMIT=${{ github.sha }} + tags: ghcr.io/${{ github.repository }}:nightly-cli diff --git a/docs/deploy/docker.md b/docs/deploy/docker.md index dee3b8a9..a6b6d11c 100644 --- a/docs/deploy/docker.md +++ b/docs/deploy/docker.md @@ -2,6 +2,18 @@ ZPan ships as a single Docker image. By default it uses an embedded SQLite database (`better-sqlite3`). For production multi-replica deployments you can opt into [Turso](https://turso.tech) (libSQL) as a shared remote database. +## Image tags + +Images are published to `ghcr.io/saltbo/zpan`. A CLI-only variant (downloader) is published under the matching `-cli` suffix. + +| Tag | Built from | When | Use for | +| --- | --- | --- | --- | +| `latest`, `2.6.1`, `2.6`, `2` | release tags (`v*`) | on every release | **production** — stable, version-pinned | +| `dev` | `main` HEAD | every push to `main` (after CI passes) | trying the newest code as soon as it lands | +| `nightly` | `main` HEAD | daily, full no-cache rebuild | newest code **plus** fresh base-image/OS security patches | + +`dev` and `nightly` are moving, unreviewed tags — do not pin production to them. Pulling without a tag (`ghcr.io/saltbo/zpan`) resolves to `latest`. + ## Default: local SQLite No extra configuration needed. Mount a volume so the database survives container restarts: