Skip to content

Publish the container images through the org's shared pipeline - #120

Merged
Ilyes512 merged 4 commits into
mainfrom
ci/reuse-org-image-workflows
Sep 22, 2026
Merged

Ilyes512 merged 4 commits into
mainfrom
ci/reuse-org-image-workflows

Conversation

@Ilyes512

@Ilyes512 Ilyes512 commented Sep 16, 2026 •

Copy link
Copy Markdown
Member

Summary

  • Replaces the hand-rolled images job with specsnl/github-actions' build-go-cli.yml / merge-go-cli.yml at 2.4.3 — one build per platform on a native runner, then a manifest merge. Login, buildx, the tag policy, OCI labels, provenance: false and a layer cache all come with it.
  • Moves the debian image from the nested ghcr.io/specsnl/labelsync/debian to a -debian tag suffix on the one image name, which is how the shared pipeline publishes a CLI's base-image variants.
  • Guards both stages on every pull request: built on a runner of their own architecture and actually run, through test/image.bats.
  • Adds task lint:docker (hadolint, pinned in compose.yml) to task checkall and to CI, matching specs-cli.
  • Rewrites the workflow assertions in release_test.go for the new shape and adds ci_test.go for the guard.

Notes for review

What stays in this repository is the three things only it can know: the stages to publish (binary, debian), the image name, and version-build-arg: LABELSYNC_VERSION. No version: input — the shared workflow defaults it to the tag without its leading v, which is what GoReleaser injects, so the image and the tarball cut from one tag agree about what they are.

2.4.3 is the floor, not just the newest tag. Up to 2.4.2 the shared merge job carried type=raw,event=workflow_dispatch,value=latest; event= is an attribute of type=ref, so metadata-action dropped it and the directive collapsed to an unconditional :latest on every run. Neither enable=false on the intended latest tag nor flavor: latest=false governs a second raw tag. Every labelsync tag so far is a v0.1.0-rc.N prerelease, so under 2.4.2 the next rc would have pointed :latest at a release candidate — the exact outcome the tag policy is meant to prevent. specs-cli hit it on v0.0.14-rc.2.

The debian rename is a breaking change to an address, deliberately taken now: ghcr.io/specsnl/labelsync/debian is frozen at 0.1.0-rc.4 and gets nothing further. Four release candidates is the whole exposure, and the suffix form is what node, python and postgres do.

The shared pipeline also emits type=ref,event=tag, so v1.2.3 is published as an alias onto the same digest as 1.2.3. Additive; :1.2.3 stays the form the docs and the CI recipe pin.

Native runners, not QEMU. The guard has to run what it builds, so each leg needs a runner of its own architecture. The Dockerfile's cross-compilation still holds and is still tested — it is what keeps a local --platform linux/arm64 build out of emulation.

task image:smoke runs the same bats file CI does, against both images: the injected version (built with a string no fallback can produce, so a renamed build arg is loud rather than silent), the uid, the CA bundle, and a bind-mounted config — valid, which gets as far as failing on the token, and invalid, which comes back with its error_kind.

hadolint had one finding on the existing tree — the base stage kept its apt lists. checkall grew a step, so AGENTS.md's description of the sequence moved with it.

Needs a human

  • The four Image (...) jobs and Dockerfile lint are not in the branch ruleset's required checks, so they run but do not gate. Worth adding if that is wanted.
  • Nothing to do for GHCR visibility: ghcr.io/specsnl/labelsync is already public, and the variant lands inside it.
  • Expect No Docker tag has been generated in the build job logs. 2.4.3 leaves build-image with no tag directive on a tag push; it pushes by digest, so nothing is lost, and the manifest's org.opencontainers.image.version still comes from the merge job.

Verified locally

task checkall (including lint:docker), task image:smoke — 14 assertions across both images — task docs:build, and actionlint over both workflows. All green.

@Ilyes512
Ilyes512 force-pushed the ci/reuse-org-image-workflows branch 2 times, most recently from fe498c9 to ec422e1 Compare September 22, 2026 10:20
Replaces the hand-rolled `images` job with specsnl/github-actions'
build-go-cli.yml and merge-go-cli.yml at 2.4.1: one build job per platform on a
runner of that architecture, then a manifest merge of the digests. Login,
buildx, the tag policy, the OCI labels, `provenance: false` and a layer cache
all come with it.

What stays here is what only this repository can know: that the stages to
publish are `binary` and `debian`, that they publish under
ghcr.io/specsnl/labelsync, and that the version build arg is called
LABELSYNC_VERSION. The version itself is not passed — the shared workflow
defaults it to the tag without its leading v, which is what GoReleaser injects,
so the image and the tarball cut from one tag agree about what they are.

The debian image moves from the nested ghcr.io/specsnl/labelsync/debian to a
`-debian` tag suffix on the one image name, which is how the shared pipeline
publishes a CLI's base-image variants and how node, python and postgres publish
theirs. The nested package is frozen at 0.1.0-rc.4.

The workflow tests follow the new shape: the four jobs and their pairing, the
native runner per platform, the version build arg matching the Dockerfile's ARG,
`packages: write` on each calling job, and one shared-workflow ref across the
whole tree. The tag policy is no longer asserted here — it lives in the shared
workflow now.
A broken Dockerfile should fail on the pull request, not while a tag is being
cut. A matrix over both stages and both architectures builds through
specsnl/github-actions' build-image action and runs test/image.bats against the
result.

The action is called directly rather than through build-go-cli.yml because the
image has to be built and run in the same job: a reusable workflow would load it
into a daemon this job cannot reach. `load: true` builds one platform into the
runner's own daemon and reports the reference to run, which is also why each leg
needs a runner of its own architecture — a foreign build can be compiled but not
executed.

The script asserts the version the binary reports (built with a string no
fallback can produce, so a renamed build arg is loud rather than silent), the
uid it runs as, the CA bundle, and a bind-mounted config — both a valid one,
which gets as far as failing on the token, and an invalid one, which comes back
with its error_kind. The two stage-specific properties, the shell and
/etc/passwd, are skipped on the stage that does not have them.

`task image:smoke` runs the same file locally, through a bats stage in the
Dockerfile and a socket proxy, replacing `task images` and its version-only
check. `task image:build` is the build half on its own.
Distribution gains the four jobs of configuration that are all this repository
still owns, the new tag table including the v-prefixed alias the shared pipeline
emits, why one runner per platform replaced one multi-platform build, and what
the pull-request guard asserts. The manual step is one package now rather than
two.

The usage pages and the README carry the rename: the debian image is
ghcr.io/specsnl/labelsync:0.1-debian, not a package of its own.
`task lint:docker` runs hadolint through a compose service under the lint
profile, the same shape as golangci-lint, and joins `task checkall`. CI runs the
task itself rather than hadolint/hadolint-action, which ships its own hadolint
build and would put the version in a second place to keep in sync.

One finding to clear, which specs-cli had already fixed: the base stage kept its
apt lists after installing.

Version pinning is off in .hadolint.yml: pinning every apt/apk package on top of
an already pinned base image trades a reproducible build for one that breaks the
moment the distro moves a package version.
@Ilyes512
Ilyes512 force-pushed the ci/reuse-org-image-workflows branch from ec422e1 to 72a8b9f Compare September 22, 2026 10:29
@Ilyes512
Ilyes512 merged commit dda864a into main Sep 22, 2026
10 checks passed
@Ilyes512
Ilyes512 deleted the ci/reuse-org-image-workflows branch September 22, 2026 19:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant