Skip to content

feat: add publish-manifests reusable workflow (manifests-only OCI publish)#319

Merged
devantler merged 1 commit into
mainfrom
claude/publish-manifests-workflow
Jun 13, 2026
Merged

feat: add publish-manifests reusable workflow (manifests-only OCI publish)#319
devantler merged 1 commit into
mainfrom
claude/publish-manifests-workflow

Conversation

@devantler

Copy link
Copy Markdown
Contributor

What

Adds publish-manifests.yaml — a reusable workflow that publishes a Kubernetes manifests directory to GHCR as a cosign-signed OCI artifact with no container image build. It is the manifests-only sibling of publish-app.yaml.

This lifts a generic pattern that currently lives inline in two places:

  • devantler-tech/.github cd.yaml — publishes deploy/ (Crossplane GitHub state) via flux push artifact + cosign sign (.github#39).
  • devantler-tech/platform cd.yaml — publishes platform/manifests via ksail workload push + cosign sign.

Per the portfolio principle of lifting generic patterns into shared libraries, the manifests-only publish belongs here so every such repo inherits one vetted, pinned implementation.

Behaviour

Modelled on publish-app.yaml, minus the docker build and image-digest-pin steps:

  1. Harden runner → require a vX.Y.Z tag → checkout.
  2. docker/login-action (GHCR via GITHUB_TOKEN).
  3. Setup Flux CLI + Cosign (same pinned SHAs as publish-app.yaml).
  4. flux push artifact oci://<registry>/<name>/manifests:<version> (version = tag with the v stripped, e.g. 1.2.3) → flux tag artifact … --tag latest.
  5. Resolve the pushed digest (docker buildx imagetools inspect) and cosign sign by digest, keyless (Fulcio/Rekor via GitHub OIDC).

Inputs

Input Default Description
oci-name ${{ github.repository }} OCI name (<owner>/<name>), no registry prefix / /manifests suffix. Override when the repo name is an invalid OCI path component — e.g. .github (leading dot) → devantler-tech/github-config.
deploy-path ./deploy Manifests directory packaged as the artifact.
dry-run false Skip publish (CI interface validation).

Auth uses the GHCR-scoped GITHUB_TOKENno secrets required.

Cosign identity (load-bearing for consumers)

Because signing runs inside this reusable workflow, the cosign certificate identity (OIDC subject) is this workflow's path —
https://github.com/devantler-tech/reusable-workflows/.github/workflows/publish-manifests.yaml@<ref>not the caller's. This matches how publish-app.yaml-signed artifacts already verify in platform (…/publish-app.yaml@.+$). Verifiers (OCIRepository.verify.matchOIDCIdentity) must match the publish-manifests.yaml subject.

Validation

  • actionlint clean on publish-manifests.yaml. (The two pre-existing code-quality permission notes in ci.yaml are unrelated — actionlint's bundled scope list is stale.)
  • Wired into ci.yaml as a dry-run [Test] job and registered in ci-required-checks (needs + job-results). A publish workflow is non-gating, so happy-path coverage is complete per AGENTS.md.
  • README section added (definition of done: docs in the same PR).

Coordinated cutover (do not merge in isolation)

This is the first of three coupled changes for the github-config artifact. The cosign signer and verifier must flip together:

  1. This PR → merge → semantic-release cuts a new reusable-workflows version.
  2. .github#39cd.yaml calls publish-manifests.yaml (pin to the release SHA once cut; @main until then).
  3. platform#2039k8s/providers/hetzner/github/sync.yaml verify.matchOIDCIdentity.subject…/publish-manifests.yaml@.+$.

The github-config artifact is not in production yet (both #39 and #2039 are drafts), so there is no live verification to break — but #39 and #2039 must land together so the verified identity always matches the signer.

Follow-up (separate PR): migrate platform's own cd.yaml (ksail workload push) to this workflow once its platform/manifests OCIRepository verifier is cut over in the same coordinated way. It additionally does SBOM/SLSA attestation, so it's a larger, separate change.


🤖 Generated with Claude Code

…lish)

Lift the generic "publish a Kubernetes manifests directory as a
cosign-signed OCI artifact, with no container image build" pattern into a
shared reusable workflow, so manifests-only repos stop carrying it inline.

It is the manifests-only sibling of publish-app.yaml: checkout → flux push
artifact (semver tag, e.g. 1.2.3 from v1.2.3) → tag latest → resolve the
pushed digest → keyless cosign sign by digest (Fulcio/Rekor via GitHub
OIDC). Inputs: oci-name (override the github.repository-derived OCI name
for repos whose name is an invalid OCI path component, e.g. .github →
devantler-tech/github-config), deploy-path (default ./deploy), and dry-run
(CI interface validation). Auth uses the GHCR-scoped GITHUB_TOKEN — no
secrets required.

Because signing runs inside this reusable workflow, the cosign certificate
identity (OIDC subject) is this workflow's path
(.../publish-manifests.yaml@<ref>), not the caller's — verifiers must match
that. Documented in the README and reflected in the first consumer
(devantler-tech/.github cd.yaml) and its platform OCIRepository verifier.

Wired into ci.yaml as a dry-run [Test] job (non-gating publish workflow ⇒
happy-path coverage is complete) and registered in ci-required-checks.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@devantler devantler marked this pull request as ready for review June 13, 2026 11:46
@devantler devantler merged commit e5eea90 into main Jun 13, 2026
52 checks passed
@devantler devantler deleted the claude/publish-manifests-workflow branch June 13, 2026 19:18
@github-project-automation github-project-automation Bot moved this from 🫴 Ready to ✅ Done in 🌊 Project Board Jun 13, 2026
@botantler-1

botantler-1 Bot commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 5.6.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

@botantler-1 botantler-1 Bot added the released label Jun 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Status: ✅ Done

Development

Successfully merging this pull request may close these issues.

1 participant