ci: publish the Image on push to main (#7) #28

Merged
piscis merged 2 commits from piscis/forgejo-7-publish-image into main 2026-09-30 17:13:22 +00:00
Owner

Closes #7

Summary

A push to main, or a dispatch from main, now builds, smoke-tests and publishes the multi-arch Image to code.vicoli.de/vicoli-oss/forgejo-mcp. PRs and dispatches from other branches still only build and smoke-test. They never log in.

ci.yml (one job)
  make verify-pin
  make build-multiarch           # one amd64+arm64 Image, SBOM + provenance (containerd store)
  make smoke-test-multiarch      # both platforms
  [main only] did the push change the Pin's values?   # comments don't count
  [main only] scripts/publish.sh <local image> <repo> $(make print-tag) <pin-changed>
    X.Y.Z-rN exists?  yes + Pin unchanged -> publish nothing, exit 0
                      yes + Pin changed   -> fail (REBUILD raised, REBUILD_OF stale)
                      HTTP other than 200/404 -> fail (never mistaken for "missing")
    docker push X.Y.Z-rN                 # the exact Image that was smoke-tested
    imagetools create X.Y.Z, X.Y, X, latest  <- X.Y.Z-rN, each only if it is the newest Rebuild in its line
    anonymous imagetools inspect: amd64 + arm64 present
  • The tag comes from make print-tag (#16). The workflow doesn't work out the Rebuild again.
  • Labels: source is now this repo, licenses=GPL-3.0, version = the Image tag (3.2.0-r1), and CI adds revision = this repo's commit. I moved Upstream's source URL and commit to de.vicoli.upstream.source and de.vicoli.upstream.revision.
  • Moving tags are handled per line: 3 and latest never go back to an older Upstream Version. The ticket says X.Y.Z, X.Y and X "always" move, but moving 3 to a 3.1 Rebuild after 3.2 would break semver, so I read "newest Rebuild" per line.
  • A dispatch has no "before" commit, so it counts as "Pin unchanged": an existing tag is skipped.
  • Publish runs (push/dispatch on main) queue in one workflow-level concurrency group, so two runs can't both see a new X.Y.Z-rN missing and both push it. PR runs get a group of their own and never wait. Forgejo ignores concurrency on a job, so it has to sit at workflow level.

Evidence

I tested on a throwaway branch (now deleted). Its workflow published to a test package, vicoli-oss/forgejo-mcp-ci-test (now deleted), and treated that branch as main.

Run Scenario Result
#31 first publish, Pin 3.2.0-r1 green, pushed 3.2.0-r1, moved 3.2.0 3.2 3 latest, anonymous inspect lists linux/amd64 + linux/arm64
#33 push with a comment-only Pin edit green, Pin changed: false, "already exists ... nothing to publish"
#34 REBUILD=2, REBUILD_OF=v3.1.0, which still resolves to 3.2.0-r1 failed: "3.2.0-r1 already exists, but this push changed the Pin"
#36 real Rebuild 3.2.0-r2 green, moved 3.2.0 3.2 3 latest to r2
#37 older Upstream Version v3.1.0 green, moved 3.1.0 3.1, "not moving 3/latest: 3.2.0-r2 is newer"
#38 dispatch, 3.1.0-r1 exists green, nothing published
#39 push restoring the r2 Pin (re-publishing r2) failed, nothing published
#43–45 job-level concurrency: a push + 2 dispatches not honoured: all 3 ran side by side
#46–48 workflow-level concurrency: 2 dispatches + a push of a new tag 3.1.0-r2 queued one after another (16:15:38–52, then 16:15:55, then 16:16:15); only #46 pushed
#40 new tag 3.2.0-r3 with a smoke test broken on purpose smoke test failed, publish step never ran, no 3.2.0-r3 in tags/list
  • Before #38/#39 and after, the digests of all 9 tags were identical (diff empty), so nothing was overwritten.
  • imagetools inspect: an index with linux/amd64, linux/arm64 and 2 attestation manifests. The SBOM is SPDX-2.3 with 49 packages per platform. The provenance is SLSA and records the build args and labels.
  • Anonymous docker pull with an empty DOCKER_CONFIG: arm64 on an arm64 Mac, --platform linux/amd64 on the Mac, and on the x86_64 runner (#33). Each ran forgejo-mcp 3.2.0. The labels shown above came from the pulled image.

Merge Danger

Door: one-way

Publishing X.Y.Z-rN is permanent, since the workflow never overwrites it. Right now main's Pin is v3.1.0 (the temporary Renovate test from #8). Merging before the Renovate PR restores v3.2.0 would first publish 3.1.0-r1 and point latest at it. Merge this after the Pin is back on v3.2.0.

Blast Radius: registry

Every push to main now runs with PACKAGE_TOKEN. Publish runs queue behind each other. If a dispatch runs before the push run for the same Pin change (#46–48), the push run goes red with "already exists, but this push changed the Pin", even though the Image was published correctly.

Closes #7 ## Summary A push to `main`, or a dispatch from `main`, now builds, smoke-tests and publishes the multi-arch Image to `code.vicoli.de/vicoli-oss/forgejo-mcp`. PRs and dispatches from other branches still only build and smoke-test. They never log in. ```text ci.yml (one job) make verify-pin make build-multiarch # one amd64+arm64 Image, SBOM + provenance (containerd store) make smoke-test-multiarch # both platforms [main only] did the push change the Pin's values? # comments don't count [main only] scripts/publish.sh <local image> <repo> $(make print-tag) <pin-changed> X.Y.Z-rN exists? yes + Pin unchanged -> publish nothing, exit 0 yes + Pin changed -> fail (REBUILD raised, REBUILD_OF stale) HTTP other than 200/404 -> fail (never mistaken for "missing") docker push X.Y.Z-rN # the exact Image that was smoke-tested imagetools create X.Y.Z, X.Y, X, latest <- X.Y.Z-rN, each only if it is the newest Rebuild in its line anonymous imagetools inspect: amd64 + arm64 present ``` - The tag comes from `make print-tag` (#16). The workflow doesn't work out the Rebuild again. - Labels: `source` is now this repo, `licenses=GPL-3.0`, `version` = the Image tag (`3.2.0-r1`), and CI adds `revision` = this repo's commit. I moved Upstream's source URL and commit to `de.vicoli.upstream.source` and `de.vicoli.upstream.revision`. - Moving tags are handled per line: `3` and `latest` never go back to an older Upstream Version. The ticket says `X.Y.Z`, `X.Y` and `X` "always" move, but moving `3` to a 3.1 Rebuild after 3.2 would break semver, so I read "newest Rebuild" per line. - A dispatch has no "before" commit, so it counts as "Pin unchanged": an existing tag is skipped. - Publish runs (push/dispatch on `main`) queue in one workflow-level `concurrency` group, so two runs can't both see a new `X.Y.Z-rN` missing and both push it. PR runs get a group of their own and never wait. Forgejo ignores `concurrency` on a job, so it has to sit at workflow level. ## Evidence I tested on a throwaway branch (now deleted). Its workflow published to a test package, `vicoli-oss/forgejo-mcp-ci-test` (now deleted), and treated that branch as `main`. | Run | Scenario | Result | |---|---|---| | #31 | first publish, Pin 3.2.0-r1 | green, pushed `3.2.0-r1`, moved `3.2.0 3.2 3 latest`, anonymous inspect lists linux/amd64 + linux/arm64 | | #33 | push with a comment-only Pin edit | green, `Pin changed: false`, "already exists ... nothing to publish" | | #34 | `REBUILD=2`, `REBUILD_OF=v3.1.0`, which still resolves to 3.2.0-r1 | **failed**: "3.2.0-r1 already exists, but this push changed the Pin" | | #36 | real Rebuild 3.2.0-r2 | green, moved `3.2.0 3.2 3 latest` to r2 | | #37 | older Upstream Version v3.1.0 | green, moved `3.1.0 3.1`, "not moving 3/latest: 3.2.0-r2 is newer" | | #38 | dispatch, 3.1.0-r1 exists | green, nothing published | | #39 | push restoring the r2 Pin (re-publishing r2) | **failed**, nothing published | | #43–45 | job-level `concurrency`: a push + 2 dispatches | **not honoured**: all 3 ran side by side | | #46–48 | workflow-level `concurrency`: 2 dispatches + a push of a new tag 3.1.0-r2 | queued one after another (16:15:38–52, then 16:15:55, then 16:16:15); only #46 pushed | | #40 | new tag 3.2.0-r3 with a smoke test broken on purpose | smoke test failed, publish step never ran, no `3.2.0-r3` in `tags/list` | - **Before** #38/#39 and **after**, the digests of all 9 tags were identical (`diff` empty), so nothing was overwritten. - `imagetools inspect`: an index with linux/amd64, linux/arm64 and 2 attestation manifests. The SBOM is SPDX-2.3 with 49 packages per platform. The provenance is SLSA and records the build args and labels. - Anonymous `docker pull` with an empty `DOCKER_CONFIG`: arm64 on an arm64 Mac, `--platform linux/amd64` on the Mac, and on the x86_64 runner (#33). Each ran `forgejo-mcp 3.2.0`. The labels shown above came from the pulled image. ## Merge Danger **Door:** one-way Publishing `X.Y.Z-rN` is permanent, since the workflow never overwrites it. **Right now `main`'s Pin is v3.1.0** (the temporary Renovate test from #8). Merging before the Renovate PR restores v3.2.0 would first publish `3.1.0-r1` and point `latest` at it. Merge this after the Pin is back on v3.2.0. **Blast Radius:** registry Every push to `main` now runs with `PACKAGE_TOKEN`. Publish runs queue behind each other. If a dispatch runs before the push run for the same Pin change (#46–48), the push run goes red with "already exists, but this push changed the Pin", even though the Image was published correctly.
ci: publish the Image on push to main and dispatch (#7)
All checks were successful
ci / build (pull_request) Successful in 11s
d4aa270b7a
ci: queue publish runs so two can't push the same X.Y.Z-rN (#7)
All checks were successful
ci / build (pull_request) Successful in 14s
d9731537fb
piscis force-pushed piscis/forgejo-7-publish-image from d9731537fb
All checks were successful
ci / build (pull_request) Successful in 14s
to e2df5ec827
All checks were successful
ci / build (pull_request) Successful in 11s
2026-09-30 16:17:02 +00:00
Compare
piscis merged commit 7f2e214cae into main 2026-09-30 17:13:22 +00:00
Sign in to join this conversation.
No description provided.