From da36b47925f175b970c52d53714d82a84f4fd844 Mon Sep 17 00:00:00 2001 From: Simon Koudijs Date: Mon, 27 Jul 2026 12:36:58 +0000 Subject: [PATCH] ci(release): publish the Artifact Hub repository metadata to the chart OCI repo Artifact Hub reads a repository's verified-publisher flag and ownership claim from a plain OCI artifact stored in the chart repository under the fixed `artifacthub.io` tag, identified by its layer media type. `helm push` cannot produce that shape, so the publish-helm job now pushes it with oras, straight from the file in Git. The metadata file lives at charts/artifacthub-repo.yml, outside the chart directory, so `helm package` does not bundle it into the chart tarball. oras joins cosign as a tool baked into the ci-image, for the same reason: publish-helm runs inside that image. Co-Authored-By: Claude Opus 5 (1M context) --- .devcontainer/Dockerfile | 17 +++++++++++++++++ .github/workflows/release.yml | 17 +++++++++++++++++ README.md | 1 + charts/artifacthub-repo.yml | 20 ++++++++++++++++++++ docs/ci-overview.md | 1 + 5 files changed, 56 insertions(+) create mode 100644 charts/artifacthub-repo.yml diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index a4d193a7..fae6a095 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -59,6 +59,7 @@ RUN apt-get update \ # HADOLINT_VERSION -> https://github.com/hadolint/hadolint/releases # VALKEY_VERSION -> https://github.com/valkey-io/valkey/releases # COSIGN_VERSION -> https://github.com/sigstore/cosign/releases +# ORAS_VERSION -> https://github.com/oras-project/oras/releases # NODE_MAJOR -> https://github.com/nodejs/node/releases (track a current LTS line) # MARKDOWNLINT_CLI2_VERSION -> https://www.npmjs.com/package/markdownlint-cli2?activeTab=versions # VALE_VERSION -> https://github.com/vale-cli/vale/releases @@ -77,6 +78,7 @@ ENV PATH="/go/bin:/usr/local/go/bin:${PATH}" \ HADOLINT_VERSION=2.14.0 \ VALKEY_VERSION=9.1.0 \ COSIGN_VERSION=v3.1.2 \ + ORAS_VERSION=1.3.3 \ NODE_MAJOR=22 \ MARKDOWNLINT_CLI2_VERSION=0.23.1 \ VALE_VERSION=3.15.1 @@ -182,6 +184,21 @@ RUN asset="cosign-linux-amd64" \ && install -m 0755 "${asset}" /usr/local/bin/cosign \ && rm -rf "${tmpdir}" +# Install oras (OCI registry client). Like cosign above, this lives in the ci +# stage because release.yml's publish-helm job runs inside this image and uses +# oras to push charts/artifacthub-repo.yml to the chart repository under the +# `artifacthub.io` tag — a plain OCI artifact that `helm push` cannot produce. +RUN asset="oras_${ORAS_VERSION}_linux_amd64.tar.gz" \ + && base="https://github.com/oras-project/oras/releases/download/v${ORAS_VERSION}" \ + && tmpdir="$(mktemp -d)" \ + && curl -fsSL "${base}/${asset}" -o "${tmpdir}/${asset}" \ + && curl -fsSL "${base}/oras_${ORAS_VERSION}_checksums.txt" -o "${tmpdir}/checksums.txt" \ + && cd "${tmpdir}" \ + && grep " ${asset}$" checksums.txt | sha256sum -c - \ + && tar -xzf "${asset}" oras \ + && install -m 0755 oras /usr/local/bin/oras \ + && rm -rf "${tmpdir}" + # Install Node.js (provides npm + npx). # # This lives in the ci stage, not dev-only, because markdownlint-cli2 below is a diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ffa192cf..edba580c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -315,6 +315,23 @@ jobs: - name: Sign the Helm chart (cosign keyless) run: cosign sign --yes "${CHART_REGISTRY}/gitops-reverser@${{ steps.chart.outputs.digest }}" + # Artifact Hub reads the repository metadata (verified-publisher flag, + # ownership claim) from a plain OCI artifact stored in the chart + # repository under the fixed `artifacthub.io` tag, identified by the + # layer media type. `helm push` cannot produce that shape, so oras does + # it — see https://artifacthub.io/docs/topics/repositories/helm-charts/. + # Re-pushed on every release so the file in Git stays the source of truth. + - name: Push Artifact Hub repository metadata + working-directory: charts + env: + ORAS_TOKEN: ${{ secrets.GITHUB_TOKEN }} + ORAS_USER: ${{ github.actor }} + run: | + echo "${ORAS_TOKEN}" | oras login "${REGISTRY}" --username "${ORAS_USER}" --password-stdin + oras push "${CHART_REGISTRY}/gitops-reverser:artifacthub.io" \ + --config /dev/null:application/vnd.cncf.artifacthub.config.v1+yaml \ + artifacthub-repo.yml:application/vnd.cncf.artifacthub.repository-metadata.layer.v1.yaml + # The chart is already signed above via its OCI digest, but OpenSSF # Scorecard's Signed-Releases check only looks at the GitHub *release # assets*, not the OCI registry. Sign and attest BOTH installer files diff --git a/README.md b/README.md index 21030941..0c3e398c 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ ![Platforms](https://img.shields.io/badge/platforms-amd64%20%7C%20arm64-2ea44f?logo=docker) [![codecov](https://codecov.io/gh/ConfigButler/gitops-reverser/graph/badge.svg)](https://codecov.io/gh/ConfigButler/gitops-reverser) [![Container](https://img.shields.io/badge/container-ghcr.io%2Fconfigbutler%2Fgitops--reverser-2ea44f?logo=docker)](https://github.com/ConfigButler/gitops-reverser/pkgs/container/gitops-reverser) +[![Artifact Hub](https://img.shields.io/endpoint?url=https://artifacthub.io/badge/repository/gitops-reverser)](https://artifacthub.io/packages/search?repo=gitops-reverser) [![Open Issues](https://img.shields.io/github/issues/ConfigButler/gitops-reverser)](https://github.com/ConfigButler/gitops-reverser/issues) # GitOps Reverser diff --git a/charts/artifacthub-repo.yml b/charts/artifacthub-repo.yml new file mode 100644 index 00000000..1ca144d9 --- /dev/null +++ b/charts/artifacthub-repo.yml @@ -0,0 +1,20 @@ +# Artifact Hub repository metadata file. +# +# This is NOT part of the Helm chart. It describes the *repository* +# oci://ghcr.io/configbutler/charts/gitops-reverser to Artifact Hub, and is +# pushed to that repository under the special `artifacthub.io` tag by the +# `publish-helm` job in .github/workflows/release.yml. It deliberately lives +# outside charts/gitops-reverser/ so `helm package` does not bundle it into the +# chart tarball. +# +# Format: https://github.com/artifacthub/hub/blob/master/docs/metadata/artifacthub-repo.yml + +# Enables the Verified Publisher badge. Taken from the Artifact Hub control +# panel for this repository. +repositoryID: a4a93f29-b67a-4110-bcec-18a4b26fa1a3 + +# Used to claim repository ownership. The email must match an Artifact Hub +# account exactly, or the claim is rejected. +owners: + - name: Simon Koudijs + email: simonkoudijs@gmail.com diff --git a/docs/ci-overview.md b/docs/ci-overview.md index bf1ed753..23f74753 100644 --- a/docs/ci-overview.md +++ b/docs/ci-overview.md @@ -94,6 +94,7 @@ step merges those exact digests into a multi-arch manifest tagged with the semve | --- | --- | --- | | Multi-arch image (`linux/amd64`, `linux/arm64`) | `ghcr.io/configbutler/gitops-reverser` | cosign keyless signature, SLSA build provenance attestation, SPDX SBOM attestation | | Helm chart | `oci://ghcr.io/configbutler/charts/gitops-reverser` | cosign keyless signature | +| Artifact Hub repository metadata (`charts/artifacthub-repo.yml`) | `oci://ghcr.io/configbutler/charts/gitops-reverser:artifacthub.io` | unsigned; plain OCI artifact pushed with `oras` | | `crds.yaml`, `install.yaml`, `sbom.spdx.json` | GitHub release assets | each signed directly (`.sigstore.json`) and SLSA-attested (`.intoto.jsonl`), also uploaded as release assets | The plain-manifest installer ships as **two** files: `crds.yaml` is applied first, then