From db72aad73f86b95e8203884b8ca948311ba7efad Mon Sep 17 00:00:00 2001 From: Nikolai Emil Damm Date: Sat, 13 Jun 2026 11:08:21 +0200 Subject: [PATCH 1/6] feat: declarative GitHub state in deploy/ published as a signed OCI artifact MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add deploy/ holding the devantler-tech org's GitHub configuration as Crossplane managed resources (provider-upjet-github, namespaced v2 repo.github.m.upbound.io). The platform cluster onboards this repo as the github-config tenant: it verifies the cosign signature and reconciles the live org to match these manifests. - deploy/repositories/{platform,ksail}.yaml โ€” first repos, Observe-first (read-only adoption; no recreation/deletion risk) - .github/workflows/cd.yaml โ€” on v* tags, publishes deploy/ as a cosign-signed OCI artifact to ghcr.io/devantler-tech/github-config/manifests (manifests-only, no container image; name is github-config because .github is an invalid OCI path component) See devantler-tech/platform docs/github-management.md for the architecture and the GitHub App credential setup (maintainer-gated). Co-Authored-By: Claude Fable 5 --- .github/workflows/cd.yaml | 70 ++++++++++++++++++++++++++ deploy/README.md | 22 ++++++++ deploy/kustomization.yaml | 10 ++++ deploy/repositories/ksail.yaml | 15 ++++++ deploy/repositories/kustomization.yaml | 12 +++++ deploy/repositories/platform.yaml | 17 +++++++ 6 files changed, 146 insertions(+) create mode 100644 .github/workflows/cd.yaml create mode 100644 deploy/README.md create mode 100644 deploy/kustomization.yaml create mode 100644 deploy/repositories/ksail.yaml create mode 100644 deploy/repositories/kustomization.yaml create mode 100644 deploy/repositories/platform.yaml diff --git a/.github/workflows/cd.yaml b/.github/workflows/cd.yaml new file mode 100644 index 0000000..ee2780c --- /dev/null +++ b/.github/workflows/cd.yaml @@ -0,0 +1,70 @@ +name: ๐Ÿš€ CD + +# Publishes the org's declarative GitHub state (deploy/) as a cosign-signed OCI +# artifact consumed by the platform cluster's `github-config` tenant. Unlike an +# application repo, there is NO container image here โ€” only manifests โ€” so this +# pushes a manifests-only artifact (no docker build). The OCI path is +# `github-config` rather than the repo name, because `.github` is an invalid OCI +# path component (leading dot). +# +# NOTE: a manifests-only publish is a generic pattern; a follow-up will lift it +# into a devantler-tech/reusable-workflows reusable workflow. Inline for now. + +on: + push: + tags: + - "v*" + +permissions: + contents: read + packages: write # push the OCI artifact to GHCR + id-token: write # mint the OIDC token for cosign keyless signing (Fulcio + Rekor) + +jobs: + publish-manifests: + name: ๐Ÿ—ณ๏ธ Publish manifests + runs-on: ubuntu-latest + env: + OCI_REPO: ghcr.io/devantler-tech/github-config/manifests + steps: + - name: ๐Ÿ“‘ Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: โš™๏ธ Setup Flux + # Same Flux setup action the org's reusable gitops workflows use. + uses: fluxcd/flux2/action@main + + - name: โš™๏ธ Install cosign + env: + # renovate: datasource=github-releases depName=sigstore/cosign extractVersion=^v(?.+)$ + COSIGN_VERSION: "3.0.6" + uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2 + with: + cosign-release: "v${{ env.COSIGN_VERSION }}" + + - name: ๐Ÿ” Configure GHCR auth + # Pre-create ~/.docker/config.json so flux push, cosign, and buildx all + # resolve GHCR credentials through the standard OCI keychain without an + # interactive login (avoids the "credentials stored unencrypted" warning). + env: + GITHUB_ACTOR: ${{ github.actor }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + mkdir -p ~/.docker + auth=$(printf '%s:%s' "$GITHUB_ACTOR" "$GITHUB_TOKEN" | base64 -w0) + printf '{"auths":{"ghcr.io":{"auth":"%s"}}}\n' "$auth" > ~/.docker/config.json + + - name: ๐Ÿ—ณ๏ธ Push & sign manifests artifact + run: | + set -euo pipefail + TAG="${{ github.ref_name }}" + flux push artifact "oci://${OCI_REPO}:${TAG}" \ + --path=./deploy \ + --source="${{ github.repositoryUrl }}" \ + --revision="${TAG}@sha1:${{ github.sha }}" + flux tag artifact "oci://${OCI_REPO}:${TAG}" --tag latest + # Resolve the pushed digest and sign by digest (keyless via OIDC). + DIGEST=$(docker buildx imagetools inspect "${OCI_REPO}:${TAG}" --format '{{ .Manifest.Digest }}') + cosign sign --yes "${OCI_REPO}@${DIGEST}" diff --git a/deploy/README.md b/deploy/README.md new file mode 100644 index 0000000..10eb80b --- /dev/null +++ b/deploy/README.md @@ -0,0 +1,22 @@ +# `deploy/` โ€” declarative GitHub state + +This directory is the source of truth for the devantler-tech org's GitHub +configuration, expressed as [Crossplane](https://crossplane.io) managed +resources (via +[provider-upjet-github](https://github.com/crossplane-contrib/provider-upjet-github)). + +On every `v*` tag, [`cd.yaml`](../.github/workflows/cd.yaml) publishes this +directory as a **cosign-signed OCI artifact** to +`ghcr.io/devantler-tech/github-config/manifests`. The +[platform](https://github.com/devantler-tech/platform) cluster onboards it as +the **`github-config` tenant**: it verifies the signature, then Flux + Crossplane +reconcile the live GitHub org to match these manifests โ€” including reverting +out-of-band changes made in the GitHub UI. + +- `repositories/` โ€” one `Repository` per managed repo. + +See the platform repo's +[`docs/github-management.md`](https://github.com/devantler-tech/platform/blob/main/docs/github-management.md) +for the architecture, the GitHub App credential setup, and the **Observe-first** +adoption flow for bringing an existing repository under management without any +risk of recreating or deleting it. diff --git a/deploy/kustomization.yaml b/deploy/kustomization.yaml new file mode 100644 index 0000000..51ba1ac --- /dev/null +++ b/deploy/kustomization.yaml @@ -0,0 +1,10 @@ +# Root of the manifests artifact published to +# oci://ghcr.io/devantler-tech/github-config/manifests and applied by the +# platform's `github-config` tenant Kustomization (path: .). It carries the +# devantler-tech org's desired GitHub state as Crossplane managed resources. +# The target namespace (github-config) is injected by the platform-side +# Kustomization's targetNamespace, so manifests here stay namespace-agnostic. +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - repositories/ diff --git a/deploy/repositories/ksail.yaml b/deploy/repositories/ksail.yaml new file mode 100644 index 0000000..3336f16 --- /dev/null +++ b/deploy/repositories/ksail.yaml @@ -0,0 +1,15 @@ +apiVersion: repo.github.m.upbound.io/v1alpha1 +kind: Repository +metadata: + name: ksail + annotations: + # Binds this resource to the EXISTING devantler-tech/ksail repository + # (owner comes from the ProviderConfig credentials). + crossplane.io/external-name: ksail +spec: + # Observe-only while adopting โ€” see repositories/kustomization.yaml. + managementPolicies: ["Observe"] + forProvider: {} + providerConfigRef: + kind: ProviderConfig + name: default diff --git a/deploy/repositories/kustomization.yaml b/deploy/repositories/kustomization.yaml new file mode 100644 index 0000000..7788c04 --- /dev/null +++ b/deploy/repositories/kustomization.yaml @@ -0,0 +1,12 @@ +# One file per managed repository. Adoption is Observe-first: a repo starts with +# managementPolicies: ["Observe"] (read-only โ€” Crossplane mirrors live state into +# status.atProvider and never writes), then forProvider is backfilled and the +# policy promoted to the full set EXCEPT Delete. Namespaced managed resources +# have no deletionPolicy; omitting Delete is what guarantees Crossplane can never +# delete a real GitHub repository. See devantler-tech/platform +# docs/github-management.md for the full adoption flow. +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - platform.yaml + - ksail.yaml diff --git a/deploy/repositories/platform.yaml b/deploy/repositories/platform.yaml new file mode 100644 index 0000000..b325825 --- /dev/null +++ b/deploy/repositories/platform.yaml @@ -0,0 +1,17 @@ +apiVersion: repo.github.m.upbound.io/v1alpha1 +kind: Repository +metadata: + name: platform + annotations: + # Binds this resource to the EXISTING devantler-tech/platform repository + # (owner comes from the ProviderConfig credentials). + crossplane.io/external-name: platform +spec: + # Observe-only while adopting: Crossplane fills status.atProvider and never + # writes to GitHub. Backfill forProvider from status.atProvider, then promote + # to ["Observe","Create","Update","LateInitialize"] (everything except Delete). + managementPolicies: ["Observe"] + forProvider: {} + providerConfigRef: + kind: ProviderConfig + name: default From 78e2468fd40bc650710d15897164594238a3b238 Mon Sep 17 00:00:00 2001 From: Nikolai Emil Damm Date: Sat, 13 Jun 2026 11:50:37 +0200 Subject: [PATCH 2/6] =?UTF-8?q?ci:=20satisfy=20zizmor=20=E2=80=94=20pin=20?= =?UTF-8?q?flux2=20action=20+=20pass=20github=20context=20via=20env?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - pin fluxcd/flux2/action to a commit SHA (v2.8.8) per the blanket hash-pinning policy (was @main) - move github.ref_name/sha/repositoryUrl into step env so they are never expanded into the shell script body (template-injection finding) Co-Authored-By: Claude Fable 5 --- .github/workflows/cd.yaml | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/.github/workflows/cd.yaml b/.github/workflows/cd.yaml index ee2780c..806a285 100644 --- a/.github/workflows/cd.yaml +++ b/.github/workflows/cd.yaml @@ -33,8 +33,8 @@ jobs: persist-credentials: false - name: โš™๏ธ Setup Flux - # Same Flux setup action the org's reusable gitops workflows use. - uses: fluxcd/flux2/action@main + # renovate: datasource=github-releases depName=fluxcd/flux2 + uses: fluxcd/flux2/action@1fd61a06264d71cf445ed55c4f14d401d26a1c64 # v2.8.8 - name: โš™๏ธ Install cosign env: @@ -57,13 +57,18 @@ jobs: printf '{"auths":{"ghcr.io":{"auth":"%s"}}}\n' "$auth" > ~/.docker/config.json - name: ๐Ÿ—ณ๏ธ Push & sign manifests artifact + # github.* values are passed via env (never interpolated into the script + # body) so an attacker-controllable expansion can't inject shell code. + env: + TAG: ${{ github.ref_name }} + SRC_URL: ${{ github.repositoryUrl }} + COMMIT_SHA: ${{ github.sha }} run: | set -euo pipefail - TAG="${{ github.ref_name }}" flux push artifact "oci://${OCI_REPO}:${TAG}" \ --path=./deploy \ - --source="${{ github.repositoryUrl }}" \ - --revision="${TAG}@sha1:${{ github.sha }}" + --source="${SRC_URL}" \ + --revision="${TAG}@sha1:${COMMIT_SHA}" flux tag artifact "oci://${OCI_REPO}:${TAG}" --tag latest # Resolve the pushed digest and sign by digest (keyless via OIDC). DIGEST=$(docker buildx imagetools inspect "${OCI_REPO}:${TAG}" --format '{{ .Manifest.Digest }}') From b011474285ac462d84b4ab6223cc8b8b78ed3edc Mon Sep 17 00:00:00 2001 From: Nikolai Emil Damm Date: Sat, 13 Jun 2026 11:55:55 +0200 Subject: [PATCH 3/6] ci: publish manifests via shared publish-manifests reusable workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the inline flux-push + cosign-sign implementation with a call to devantler-tech/reusable-workflows/.github/workflows/publish-manifests.yaml, the generic manifests-only OCI publish lifted into the shared library (reusable-workflows#319). Keeps the github-config OCI name override and the v* tag trigger; drops the duplicated checkout/flux/cosign/auth steps. Signing now happens inside the reusable workflow, so the cosign OIDC subject becomes the reusable workflow's path โ€” the platform github-config OCIRepository verifier is updated to match in platform#2039. Pinned @main until reusable-workflows cuts a release including the new workflow; then pin to the release SHA (Renovate-managed). Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/cd.yaml | 87 ++++++++++++--------------------------- 1 file changed, 26 insertions(+), 61 deletions(-) diff --git a/.github/workflows/cd.yaml b/.github/workflows/cd.yaml index 806a285..f6bdd02 100644 --- a/.github/workflows/cd.yaml +++ b/.github/workflows/cd.yaml @@ -1,75 +1,40 @@ name: ๐Ÿš€ CD # Publishes the org's declarative GitHub state (deploy/) as a cosign-signed OCI -# artifact consumed by the platform cluster's `github-config` tenant. Unlike an -# application repo, there is NO container image here โ€” only manifests โ€” so this -# pushes a manifests-only artifact (no docker build). The OCI path is -# `github-config` rather than the repo name, because `.github` is an invalid OCI -# path component (leading dot). +# artifact consumed by the platform cluster's `github-config` tenant. There is +# NO container image here โ€” only manifests โ€” so it delegates to the shared +# manifests-only publish workflow in devantler-tech/reusable-workflows (the +# manifests-only sibling of publish-app.yaml). # -# NOTE: a manifests-only publish is a generic pattern; a follow-up will lift it -# into a devantler-tech/reusable-workflows reusable workflow. Inline for now. +# The OCI path is `github-config` (not the repo name) because `.github` is an +# invalid OCI path component (leading dot) โ€” hence the oci-name override. +# +# Because signing runs INSIDE the reusable workflow, the cosign certificate +# identity (OIDC subject) is that workflow's path โ€” +# https://github.com/devantler-tech/reusable-workflows/.github/workflows/publish-manifests.yaml@ +# โ€” NOT this repo's cd.yaml. The platform `github-config` OCIRepository's +# verify.matchOIDCIdentity.subject must match that (see +# k8s/providers/hetzner/github/sync.yaml in devantler-tech/platform). on: push: tags: - "v*" -permissions: - contents: read - packages: write # push the OCI artifact to GHCR - id-token: write # mint the OIDC token for cosign keyless signing (Fulcio + Rekor) +permissions: {} jobs: publish-manifests: name: ๐Ÿ—ณ๏ธ Publish manifests - runs-on: ubuntu-latest - env: - OCI_REPO: ghcr.io/devantler-tech/github-config/manifests - steps: - - name: ๐Ÿ“‘ Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false - - - name: โš™๏ธ Setup Flux - # renovate: datasource=github-releases depName=fluxcd/flux2 - uses: fluxcd/flux2/action@1fd61a06264d71cf445ed55c4f14d401d26a1c64 # v2.8.8 - - - name: โš™๏ธ Install cosign - env: - # renovate: datasource=github-releases depName=sigstore/cosign extractVersion=^v(?.+)$ - COSIGN_VERSION: "3.0.6" - uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2 - with: - cosign-release: "v${{ env.COSIGN_VERSION }}" - - - name: ๐Ÿ” Configure GHCR auth - # Pre-create ~/.docker/config.json so flux push, cosign, and buildx all - # resolve GHCR credentials through the standard OCI keychain without an - # interactive login (avoids the "credentials stored unencrypted" warning). - env: - GITHUB_ACTOR: ${{ github.actor }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - mkdir -p ~/.docker - auth=$(printf '%s:%s' "$GITHUB_ACTOR" "$GITHUB_TOKEN" | base64 -w0) - printf '{"auths":{"ghcr.io":{"auth":"%s"}}}\n' "$auth" > ~/.docker/config.json - - - name: ๐Ÿ—ณ๏ธ Push & sign manifests artifact - # github.* values are passed via env (never interpolated into the script - # body) so an attacker-controllable expansion can't inject shell code. - env: - TAG: ${{ github.ref_name }} - SRC_URL: ${{ github.repositoryUrl }} - COMMIT_SHA: ${{ github.sha }} - run: | - set -euo pipefail - flux push artifact "oci://${OCI_REPO}:${TAG}" \ - --path=./deploy \ - --source="${SRC_URL}" \ - --revision="${TAG}@sha1:${COMMIT_SHA}" - flux tag artifact "oci://${OCI_REPO}:${TAG}" --tag latest - # Resolve the pushed digest and sign by digest (keyless via OIDC). - DIGEST=$(docker buildx imagetools inspect "${OCI_REPO}:${TAG}" --format '{{ .Manifest.Digest }}') - cosign sign --yes "${OCI_REPO}@${DIGEST}" + permissions: + contents: read # checkout + packages: write # push the OCI artifact to GHCR + id-token: write # keyless cosign signing (Fulcio + Rekor via GitHub OIDC) + # NOTE: pinned to @main until reusable-workflows publishes a release that + # includes publish-manifests.yaml; then pin to the release commit SHA + # (Renovate manages this pin, as it does for publish-app.yaml elsewhere). + # The platform verifier matches `@.+$`, so the ref does not affect cosign + # verification. + uses: devantler-tech/reusable-workflows/.github/workflows/publish-manifests.yaml@main + with: + oci-name: devantler-tech/github-config From c8c27cbd6f2c01d03e6ec3867bec17a33a969662 Mon Sep 17 00:00:00 2001 From: Nikolai Emil Damm Date: Sat, 13 Jun 2026 12:04:10 +0200 Subject: [PATCH 4/6] ci: pin publish-manifests reusable workflow to a SHA (zizmor blanket policy) The .github repo's zizmor blanket policy requires hash-pinned `uses:`, so `@main` is rejected. Pin to the reusable-workflows#319 branch head SHA that introduces publish-manifests.yaml. Re-pin to the release commit SHA once #319 merges and cuts a release (Renovate-managed thereafter). The platform verifier matches `@.+$`, so the ref does not affect cosign verification. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/cd.yaml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/cd.yaml b/.github/workflows/cd.yaml index f6bdd02..ef0a27f 100644 --- a/.github/workflows/cd.yaml +++ b/.github/workflows/cd.yaml @@ -30,11 +30,12 @@ jobs: contents: read # checkout packages: write # push the OCI artifact to GHCR id-token: write # keyless cosign signing (Fulcio + Rekor via GitHub OIDC) - # NOTE: pinned to @main until reusable-workflows publishes a release that - # includes publish-manifests.yaml; then pin to the release commit SHA - # (Renovate manages this pin, as it does for publish-app.yaml elsewhere). + # Pinned to a commit SHA (zizmor blanket policy requires a hash). This is the + # reusable-workflows#319 branch head that introduces publish-manifests.yaml; + # re-pin to the release commit SHA once #319 merges and cuts a release + # (Renovate then manages this pin, as it does for publish-app.yaml elsewhere). # The platform verifier matches `@.+$`, so the ref does not affect cosign # verification. - uses: devantler-tech/reusable-workflows/.github/workflows/publish-manifests.yaml@main + uses: devantler-tech/reusable-workflows/.github/workflows/publish-manifests.yaml@44742c4f2093025a3dd2261479ccff9c8c893418 # reusable-workflows#319 (pre-release) with: oci-name: devantler-tech/github-config From fa420128e6b7d0752750f6d8d0dc9b8a059a47e5 Mon Sep 17 00:00:00 2001 From: Nikolai Emil Damm Date: Sun, 14 Jun 2026 21:27:44 +0200 Subject: [PATCH 5/6] ci: add PR validation + CI - Required Checks gate The org ruleset requires the `CI - Required Checks` status on every PR, but this repo only had cd.yaml (tag-only), so nothing produced that context and PRs stayed merge-BLOCKED. Add a CI workflow that validates the deploy/ kustomize build on pull_request/merge_group and ends with the required gate job (aggregate-job-checks). Co-Authored-By: Claude Fable 5 --- .github/workflows/ci.yaml | 43 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 .github/workflows/ci.yaml diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..31f48cc --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,43 @@ +name: CI + +# Validates the declarative GitHub state in deploy/ on every PR, and reports the +# `CI - Required Checks` status the org ruleset requires before merge. (cd.yaml +# only runs on v* tags, so without this no PR-time required check is produced and +# the PR stays merge-BLOCKED.) + +on: + pull_request: + merge_group: + +permissions: {} + +jobs: + validate-manifests: + name: ๐Ÿงน Validate manifests + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: ๐Ÿ“‘ Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: ๐Ÿงน Validate deploy/ kustomize build + # kubectl (with built-in kustomize) is preinstalled on the runner. A + # successful build proves the manifests are well-formed; the Crossplane + # CRDs are applied/validated on-cluster, not here. + run: kubectl kustomize deploy/ > /dev/null + + ci-required-checks: + name: CI - Required Checks + runs-on: ubuntu-latest + needs: [validate-manifests] + if: always() + permissions: {} + steps: + - name: ๐Ÿ“Š Evaluate job results + uses: devantler-tech/actions/aggregate-job-checks@8ce19528eda1ef50f3d028b685c53aebaed6ae62 # v5.4.1 + with: + job-results: >- + ${{ needs.validate-manifests.result }} From 311e7d9232e419c0e84010d685e393b846a2007d Mon Sep 17 00:00:00 2001 From: Nikolai Emil Damm Date: Mon, 15 Jun 2026 20:06:12 +0200 Subject: [PATCH 6/6] ci: re-pin publish-manifests to released v5.6.1 SHA (fix impostor-commit) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cd.yaml pinned the reusable workflow to 44742c4f โ€” the pre-release branch head of reusable-workflows#319. That PR is now merged and released (v5.6.1), so the old SHA is unreachable from any branch/tag (zizmor impostor-commit alert #15, and it would fail to resolve at runtime). Re-pin to the v5.6.1 release commit f974f62a, which is reachable from the tag. Co-Authored-By: Claude Fable 5 --- .github/workflows/cd.yaml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/cd.yaml b/.github/workflows/cd.yaml index ef0a27f..dbe4778 100644 --- a/.github/workflows/cd.yaml +++ b/.github/workflows/cd.yaml @@ -30,12 +30,11 @@ jobs: contents: read # checkout packages: write # push the OCI artifact to GHCR id-token: write # keyless cosign signing (Fulcio + Rekor via GitHub OIDC) - # Pinned to a commit SHA (zizmor blanket policy requires a hash). This is the - # reusable-workflows#319 branch head that introduces publish-manifests.yaml; - # re-pin to the release commit SHA once #319 merges and cuts a release - # (Renovate then manages this pin, as it does for publish-app.yaml elsewhere). + # Pinned to the released commit SHA (zizmor blanket policy requires a hash; + # the SHA must be reachable from a tag, or zizmor flags it as an impostor). + # Renovate manages this pin, as it does for publish-app.yaml elsewhere. # The platform verifier matches `@.+$`, so the ref does not affect cosign # verification. - uses: devantler-tech/reusable-workflows/.github/workflows/publish-manifests.yaml@44742c4f2093025a3dd2261479ccff9c8c893418 # reusable-workflows#319 (pre-release) + uses: devantler-tech/reusable-workflows/.github/workflows/publish-manifests.yaml@f974f62a8cb396130fa084d209ad1fc17e99b9fc # v5.6.1 with: oci-name: devantler-tech/github-config