diff options
| author | agent <agent@local> | 2026-11-07 09:00:55 +0100 |
|---|---|---|
| committer | agent <agent@local> | 2026-11-07 09:00:55 +0100 |
| commit | 54541fa8357cf2af616effbaeb7d5b8d76f80ea2 (patch) | |
| tree | 0be5059280b10545912e01081f7d95aaf9055623 /.forgejo/workflows/mirror-sync.yml | |
Public read-only distroless OCI mirror (zot + edge vhost + auditor)
Diffstat (limited to '.forgejo/workflows/mirror-sync.yml')
| -rw-r--r-- | .forgejo/workflows/mirror-sync.yml | 69 |
1 files changed, 69 insertions, 0 deletions
diff --git a/.forgejo/workflows/mirror-sync.yml b/.forgejo/workflows/mirror-sync.yml new file mode 100644 index 0000000..a95bad4 --- /dev/null +++ b/.forgejo/workflows/mirror-sync.yml @@ -0,0 +1,69 @@ +name: Mirror distroless images + +# Keeps https://img.dieschoenewolke.de in step with gcr.io/distroless. +# +# The copying is done by zot's sync extension on the VPS, not by this job: the +# registry diffs digests itself, pulls only what changed, and can do so while +# the home Forgejo instance (and this runner) is down. This workflow is the +# auditor — it compares upstream and mirror digests, pull-verifies anything new, +# and only then writes a `synced-<repo>-<tag>-<digest12>` marker tag. +# +# That is the same "marker last" discipline as the Pureblog release pipeline: +# a marker exists only for a ref that was proven pullable by digest. A run that +# dies before the push leaves no marker, so the next run retries. +# +# gcr.io load: one anonymous token request per repo plus one manifest request +# per ref = 8 requests per run, every 6 hours. No per-request storm, no docker +# login, nothing that could hit an anonymous rate limit. + +on: + schedule: + - cron: "17 */6 * * *" + workflow_dispatch: + inputs: + verify_all: + description: "Pull-verify every ref, not just new digests (1/0)" + required: false + default: "0" + type: string + +concurrency: + group: distroless-mirror-sync + # Never cancel a run half way: a cancelled run could leave a tag pushed + # without the verification it claims. Runs are minutes apart anyway. + cancel-in-progress: false + +jobs: + verify: + runs-on: docker-cli + timeout-minutes: 30 + container: + image: docker:29-cli + steps: + - name: Install workflow tools + run: apk add --no-cache bash curl git jq + + - name: Check out the marker tags + uses: https://github.com/actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Compare upstream and mirror, verify, mark + env: + # docker:cli keeps the CLI in /usr/local/bin; the runner's DOCKER_HOST + # points at the daemon, so the pull goes over the public hostname and + # the real TLS/edge path. + VERIFY_PULL: ${{ inputs.verify_all == '1' && 'always' || 'new' }} + MARKERS_FILE: ${{ runner.temp }}/mirror-markers + run: ./scripts/verify-mirror.sh + + - name: Publish marker tags + run: | + set -eu + file="${MARKERS_FILE:-$RUNNER_TEMP/mirror-markers}" + [ -s "$file" ] || { echo "no new markers"; exit 0; } + while read -r marker; do + [ -n "$marker" ] || continue + git push origin "refs/tags/$marker" + echo "marked $marker" + done <"$file" |
