diff --git a/.gitea/workflows/build-images.yaml b/.gitea/workflows/build-images.yaml index 9546197..2fde3fb 100644 --- a/.gitea/workflows/build-images.yaml +++ b/.gitea/workflows/build-images.yaml @@ -6,8 +6,8 @@ # plain HTTP), each tagged with the version vMAJOR.MINOR.PATCH and `latest`, then pushes the git tag of that version. # On the Kvitt Pi: /opt/kvitt/deploy.sh . # -# Secret: `kvitt_cicd`, a repository secret holding a Gitea access token with scope write:package of the user that -# pushes (owner step, infra/README.md). It is used only for `docker login`; the git tag push at the end goes through +# Secret: `kvitt_cicd`, a user-level secret of the repository owner (Gitea gives a repository its owner's secrets) +# holding a Gitea access token with scope write:package of the user that pushes (owner step, infra/README.md). It is used only for `docker login`; the git tag push at the end goes through # the credentials actions/checkout stored (the job's own token), not this secret, so it needs no write:repository. # # RISK: the runner is a Raspberry Pi 3 with 1 GB RAM and the server image compiles about 232 Rust crates @@ -44,25 +44,6 @@ jobs: steps: - name: Install Git and Docker CLI run: apk add --no-cache git docker-cli docker-cli-buildx - # TEMPORARY (D-120 follow-up): remove this step once the cause of the empty `kvitt_cicd` secret is known. - # Prints only lengths (0 = absent or empty) and non-secret context values, never a secret value. - # Gitea stores secret names upper-cased and the expression lookup is case-insensitive; the upper-case line - # tests that on this runner. babymonitor_cicd is the owner's known-working user-level secret (control). - # GITEA_TOKEN and github.token are the job's automatic token. - - name: Diagnose secrets (temporary, D-120 follow-up) - run: | - A='${{ secrets.kvitt_cicd }}'; printf 'secrets.kvitt_cicd length: %s\n' "${#A}" - A='${{ secrets.KVITT_CICD }}'; printf 'secrets.KVITT_CICD length: %s\n' "${#A}" - A='${{ secrets.babymonitor_cicd }}'; printf 'secrets.babymonitor_cicd length: %s\n' "${#A}" - A='${{ secrets.GITEA_TOKEN }}'; printf 'secrets.GITEA_TOKEN length: %s\n' "${#A}" - A='${{ github.token }}'; printf 'github.token length: %s\n' "${#A}" - unset A - printf 'github.repository: %s\n' '${{ github.repository }}' - printf 'github.repository_owner: %s\n' '${{ github.repository_owner }}' - printf 'github.actor: %s\n' '${{ github.actor }}' - printf 'github.event_name: %s\n' '${{ github.event_name }}' - printf 'github.ref: %s\n' '${{ github.ref }}' - printf 'github.server_url: %s\n' '${{ github.server_url }}' - name: Log in to Gitea Container Registry # The owner's proven form: the secret interpolated into the run line (act_runner masks it in the log). # The env-based form (env: REGISTRY_TOKEN: ${{ secrets.kvitt_cicd }}) came through empty on act_runner 0.6.1. @@ -84,6 +65,18 @@ jobs: with: # Tells the action to use the local 'git' binary installed above set-safe-directory: 'true' + # Only the two build contexts: server/Dockerfile copies its own context (server/, Cargo workspace, + # migrations and the import's category table included) and infra/backup/Dockerfile copies backup.sh and + # healthcheck.sh. Cone mode also checks out the files at the root and directly in infra/. A sparse + # checkout makes the action fetch with --filter=blob:none, so only the blobs of these paths are downloaded + # (not the Android screenshot goldens). + sparse-checkout: | + server + infra/backup + sparse-checkout-cone-mode: true + # Full commit and tag history (trees and commits only, no blobs, because of the filter above): "Determine + # Version" needs every v1.0.* tag. With fetch-depth 1, fetch-tags: true only drops --no-tags, so git + # fetches just the tags that point at the fetched commit, not the older v1.0.N. fetch-depth: 0 - name: Determine Version run: | @@ -132,6 +125,7 @@ jobs: rm -f "/tmp/kvitt-$name.metadata.json" done - name: Tag and Push Version + # The tag points at HEAD, which the remote already has; works with the sparse, blob-filtered checkout. run: | git tag "$VERSION" git push origin "$VERSION" diff --git a/infra/README.md b/infra/README.md index b652ec2..6d8296b 100644 --- a/infra/README.md +++ b/infra/README.md @@ -241,10 +241,16 @@ fresh start and after a run, red at 26 h. (The Caddy and cert-sync rehearsals of ### Gitea Actions workflow (`.gitea/workflows/build-images.yaml`) Same setup as the owner's working example: `runs-on: host`, `sh` steps, `apk add git docker-cli docker-cli-buildx`, -`docker login gitea.lan` with the repository secret `kvitt_cicd` (created by the owner) and `${{ github.actor }}`, -a buildx builder whose `buildkitd.toml` sets `http = true` for `gitea.lan` (created once and reused, so its cache -survives), `actions/checkout@v4` with `set-safe-directory` and `fetch-depth: 0`, the version from the newest -`v1.0.*` git tag plus one, and the git tag pushed after the build. That version is what `deploy.sh` takes. +`docker login gitea.lan` with `kvitt_cicd` (a user-level secret of the repository owner, created by the owner; +Gitea gives a repository its owner's secrets) and `${{ github.actor }}`, a buildx builder whose `buildkitd.toml` +sets `http = true` for `gitea.lan` (created once and reused, so its cache survives), `actions/checkout@v4` with +`set-safe-directory` and `fetch-depth: 0`, the version from the newest `v1.0.*` git tag plus one, and the git tag +pushed after the build. That version is what `deploy.sh` takes. +The checkout is a cone-mode sparse checkout of `server` and `infra/backup` only, the two build contexts (neither +Dockerfile copies anything outside its context), so the action fetches with `--filter=blob:none` and writes only +those files instead of all 2118 (52 MB of Android screenshot goldens among them; about 95 s on the first runs). +`fetch-depth: 0` stays because the version needs every `v1.0.*` tag (commits and trees only, no blobs): with +`fetch-depth: 1`, `fetch-tags: true` brings only tags that point at the fetched commit. The token is interpolated into the login step's `run` line (`printf '%s' "${{ secrets.kvitt_cicd }}" | docker login ...`), the owner's proven form: the first run on the runner (2026-10-19) got an empty token from the env-based form (`env: REGISTRY_TOKEN: ${{ secrets.kvitt_cicd }}`) on act_runner 0.6.1 and failed with "password is empty";