From 7801e5ba1f67db75edaad7b4355dbbc9f2b30d5a Mon Sep 17 00:00:00 2001 From: Lucas Mundim Date: Mon, 15 Jun 2026 16:39:42 -0300 Subject: [PATCH 1/3] fix(release): reconnect GitHub Releases, enforce tag-parity, fix install docs The chart-publishing migration (Pages -> Docker Hub -> GHCR, PRs #53/#71/#75) moved the artifact but left the release surface disconnected: - No GitHub Release is created on a tag since chart-releaser was removed in #53, so the Releases page is frozen at 0.5.1 while 0.6.x ships to GHCR. - The parity lint only checked version==appVersion and pointed at update-helm-version.yml, a workflow #53 deleted. Changes (no rendered-manifest changes -> zero impact on lab/staging/prod): - docker-publish.yml: create a GitHub Release per tag with the packaged chart .tgz attached (contents: write). - lint.yml: parity check now also asserts Chart.yaml matches the release tag on vX.Y.Z pushes; dropped the dead update-helm-version.yml reference; skip Go lint on tags. - README.md / chart README: GHCR install as the primary path at the correct published version (0.6.1), documented the tag-driven release process. Deliberately NOT bumping Chart.yaml here: version/appVersion feed the app.kubernetes.io/version pod-template label, so an out-of-band bump would roll the controller in every env on the next ArgoCD sync. External GHCR installs are already deterministic because the publish job bakes --app-version from the git tag (values.yaml image.tag is "" -> defaults to appVersion). The parity gate now forces the in-repo bump at actual release time, where a rollout is expected. --- .github/workflows/docker-publish.yml | 18 ++++++++++++- .github/workflows/lint.yml | 23 +++++++++++++++-- README.md | 24 ++++++++++++------ .../openfilter-pipelines-controller/README.md | 25 +++++++++++++++++-- 4 files changed, 78 insertions(+), 12 deletions(-) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 95d9da9..345b7e1 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -159,7 +159,7 @@ jobs: if: startsWith(github.ref, 'refs/tags/v') runs-on: ubuntu-latest permissions: - contents: read + contents: write # create the GitHub Release for the tag packages: write steps: - name: Checkout code @@ -196,6 +196,22 @@ jobs: helm push openfilter-pipelines-controller-${{ steps.version.outputs.version }}.tgz \ oci://ghcr.io/plainsightai/charts + # Publishing to GHCR alone leaves the GitHub Releases page frozen at the + # last chart-releaser release (0.5.1). Recreate a Release per tag so the + # tag is discoverable and the packaged chart is attached as an asset. + - name: Create GitHub Release with chart artifact + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + CHART_TGZ=openfilter-pipelines-controller-${{ steps.version.outputs.version }}.tgz + if gh release view "$GITHUB_REF_NAME" >/dev/null 2>&1; then + gh release upload "$GITHUB_REF_NAME" "$CHART_TGZ" --clobber + else + gh release create "$GITHUB_REF_NAME" "$CHART_TGZ" \ + --title "$GITHUB_REF_NAME" \ + --generate-notes + fi + deploy: needs: merge if: github.ref == 'refs/heads/main' diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index ca14b64..19ed6c5 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -4,6 +4,8 @@ on: push: branches: - main + tags: + - 'v*.*.*' paths-ignore: - 'deployment/**' pull_request: @@ -12,6 +14,8 @@ jobs: lint: name: Run on Ubuntu runs-on: ubuntu-latest + # Go linting adds no signal on a release tag; only the parity gate needs to run there. + if: ${{ !startsWith(github.ref, 'refs/tags/') }} steps: - name: Clone the code uses: actions/checkout@v4 @@ -31,7 +35,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Verify version and appVersion match + - name: Verify version, appVersion, and release tag agree run: | set -eu CHART=deployment/openfilter-pipelines-controller/Chart.yaml @@ -47,6 +51,21 @@ jobs: exit 1 fi if [ "$VERSION" != "$APPVERSION" ]; then - echo "::error file=$CHART::version ($VERSION) and appVersion ($APPVERSION) drifted. The auto-bump workflow keeps these in lockstep on tag-driven releases (.github/workflows/update-helm-version.yml); manual edits must do the same. See PR #48 for the slip this catches." + echo "::error file=$CHART::version ($VERSION) and appVersion ($APPVERSION) drifted. Bump both together in the same commit. See PR #48 for the slip this catches." exit 1 fi + # On a release tag (vX.Y.Z) Chart.yaml must already match the tag. + # The publish-chart job packages with --version derived from the tag + # (.github/workflows/docker-publish.yml), so a stale Chart.yaml ships a + # chart whose source tree disagrees with the version it was published + # under — which is exactly how main drifted to 0.5.1 while 0.6.x shipped. + case "${GITHUB_REF:-}" in + refs/tags/v*) + TAG="${GITHUB_REF_NAME#v}" + echo "release tag=$TAG" + if [ "$VERSION" != "$TAG" ]; then + echo "::error file=$CHART::Chart.yaml version ($VERSION) does not match release tag $GITHUB_REF_NAME (expected $TAG). Bump version and appVersion to $TAG before tagging." + exit 1 + fi + ;; + esac diff --git a/README.md b/README.md index 92cc89f..aa0f7af 100644 --- a/README.md +++ b/README.md @@ -96,13 +96,13 @@ kubectl apply -f https://raw.githubusercontent.com//openfilter-pipelines-co ### Using the Helm Chart -The Helm chart lives in this repository under `deployment/openfilter-pipelines-controller`. Clone the repo and install from the local chart: +The chart is published as an OCI artifact to GitHub Container Registry (GHCR). +Helm 3.8+ supports OCI directly — no `helm repo add` is needed: ```sh -git clone https://github.com/PlainsightAI/openfilter-pipelines-controller -cd openfilter-pipelines-controller - -helm install openfilter-pipelines-controller deployment/openfilter-pipelines-controller \ +helm install openfilter-pipelines-controller \ + oci://ghcr.io/plainsightai/charts/openfilter-pipelines-controller \ + --version 0.6.1 \ --namespace pipelines-system \ --create-namespace ``` @@ -110,7 +110,9 @@ helm install openfilter-pipelines-controller deployment/openfilter-pipelines-con Install with the bundled Valkey subchart enabled (optional): ```sh -helm install openfilter-pipelines-controller deployment/openfilter-pipelines-controller \ +helm install openfilter-pipelines-controller \ + oci://ghcr.io/plainsightai/charts/openfilter-pipelines-controller \ + --version 0.6.1 \ --namespace pipelines-system \ --create-namespace \ --set valkey.enabled=true @@ -119,10 +121,18 @@ helm install openfilter-pipelines-controller deployment/openfilter-pipelines-con To upgrade an existing installation: ```sh -helm upgrade openfilter-pipelines-controller deployment/openfilter-pipelines-controller \ +helm upgrade openfilter-pipelines-controller \ + oci://ghcr.io/plainsightai/charts/openfilter-pipelines-controller \ + --version 0.6.1 \ --namespace pipelines-system ``` +Published chart versions track the git tags on this repository +(`vX.Y.Z` → chart `X.Y.Z`); see the [Releases](https://github.com/PlainsightAI/openfilter-pipelines-controller/releases) +page. Contributors hacking on the chart can also install from the local copy +under `deployment/openfilter-pipelines-controller` — see the +[chart README](deployment/openfilter-pipelines-controller/README.md). + For more configuration options, see the [chart values](deployment/openfilter-pipelines-controller/values.yaml) and the [chart README](deployment/openfilter-pipelines-controller/README.md). ## Contributing diff --git a/deployment/openfilter-pipelines-controller/README.md b/deployment/openfilter-pipelines-controller/README.md index 79ad715..e88c52d 100644 --- a/deployment/openfilter-pipelines-controller/README.md +++ b/deployment/openfilter-pipelines-controller/README.md @@ -25,8 +25,11 @@ Docker Hub has a flat namespace, so the chart (whose OCI repository name is derived from the chart name) would share a repository with the controller image and overwrite the image manifest at each release tag. -Replace `` with the desired release (e.g. `0.5.2`); the published -tags match the git tags on this repository (`vX.Y.Z` → chart `X.Y.Z`). +Replace `` with the desired release (e.g. `0.6.1`); the published +tags match the git tags on this repository (`vX.Y.Z` → chart `X.Y.Z`) and are +listed on the +[Releases](https://github.com/PlainsightAI/openfilter-pipelines-controller/releases) +page. Container images: [`plainsightai/openfilter-pipelines-controller`](https://hub.docker.com/r/plainsightai/openfilter-pipelines-controller) @@ -229,6 +232,24 @@ helm install openfilter-pipelines-controller deployment/openfilter-pipelines-con --dry-run --debug ``` +## Releasing + +Releases are driven entirely by git tags. To cut version `X.Y.Z`: + +1. Bump `version` **and** `appVersion` in `Chart.yaml` to `X.Y.Z` in the same + commit and merge it via PR. The `chart-version-parity` CI check + (`.github/workflows/lint.yml`) keeps the two fields in lockstep and, on a + release tag, asserts they match the tag — so a stale `Chart.yaml` fails CI + before anything is published. +2. Tag the merged commit `vX.Y.Z` and push the tag. + +On the tag push, `.github/workflows/docker-publish.yml`: + +- builds and pushes the controller + claimer images to Docker Hub, +- packages the chart (with `--version`/`--app-version` derived from the tag) + and pushes it to `oci://ghcr.io/plainsightai/charts`, and +- creates a GitHub Release for the tag with the packaged chart `.tgz` attached. + ## License See the [LICENSE](../../LICENSE) file for details. From 19491d6e38188b0bc41218ba0c35d575cf9dcaa8 Mon Sep 17 00:00:00 2001 From: Lucas Mundim Date: Tue, 16 Jun 2026 00:34:44 -0300 Subject: [PATCH 2/3] refactor(ci): make tag-parity an authoritative publish gate Per review: a failing lint job does not block the separate publish workflow, so asserting tag-parity in lint.yml only produced an advisory red check while the mistagged chart still shipped. Move the Chart.yaml-==-tag assertion into the publish-chart job (the only step that runs on the tag AND gates publishing) so a mistagged release fails to publish. lint.yml keeps version==appVersion for pre-merge PR feedback; dropped the tag trigger and tag-only lint skip. --- .github/workflows/docker-publish.yml | 18 ++++++++++++++++++ .github/workflows/lint.yml | 26 ++++++-------------------- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 345b7e1..8b5363b 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -169,6 +169,24 @@ jobs: id: version run: echo "version=${GITHUB_REF_NAME#v}" >> $GITHUB_OUTPUT + # Authoritative release gate: the chart is packaged with --version/--app-version + # derived from the tag, so if Chart.yaml disagrees we'd ship a chart whose + # source tree lies about its own version. Fail here, before publishing, so a + # mistagged release never reaches the registry (this is the only step that + # runs on the tag AND gates the publish). + - name: Assert Chart.yaml matches the release tag + run: | + set -eu + CHART=deployment/openfilter-pipelines-controller/Chart.yaml + TAG="${{ steps.version.outputs.version }}" + VERSION=$(awk '/^version:/ {print $2}' "$CHART" | tr -d '"') + APPVERSION=$(awk '/^appVersion:/ {print $2}' "$CHART" | tr -d '"') + echo "tag=$TAG Chart.yaml version=$VERSION appVersion=$APPVERSION" + if [ "$VERSION" != "$TAG" ] || [ "$APPVERSION" != "$TAG" ]; then + echo "::error file=$CHART::Chart.yaml version/appVersion ($VERSION/$APPVERSION) does not match release tag $GITHUB_REF_NAME (expected $TAG). Bump Chart.yaml to $TAG and re-tag." + exit 1 + fi + - name: Set up Helm uses: azure/setup-helm@v5 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 19ed6c5..acdce9d 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -4,8 +4,6 @@ on: push: branches: - main - tags: - - 'v*.*.*' paths-ignore: - 'deployment/**' pull_request: @@ -14,8 +12,6 @@ jobs: lint: name: Run on Ubuntu runs-on: ubuntu-latest - # Go linting adds no signal on a release tag; only the parity gate needs to run there. - if: ${{ !startsWith(github.ref, 'refs/tags/') }} steps: - name: Clone the code uses: actions/checkout@v4 @@ -35,7 +31,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Verify version, appVersion, and release tag agree + - name: Verify version and appVersion match run: | set -eu CHART=deployment/openfilter-pipelines-controller/Chart.yaml @@ -50,22 +46,12 @@ jobs: echo "::error file=$CHART::could not parse version/appVersion from $CHART" exit 1 fi + # version==appVersion is checked here (pre-merge, on PRs) for fast + # developer feedback. The stricter "Chart.yaml must equal the release + # tag" gate lives in the publish-chart job (docker-publish.yml), which + # is the only place the tag exists AND the only place we publish — so a + # mistagged release fails to publish rather than just flagging a check. if [ "$VERSION" != "$APPVERSION" ]; then echo "::error file=$CHART::version ($VERSION) and appVersion ($APPVERSION) drifted. Bump both together in the same commit. See PR #48 for the slip this catches." exit 1 fi - # On a release tag (vX.Y.Z) Chart.yaml must already match the tag. - # The publish-chart job packages with --version derived from the tag - # (.github/workflows/docker-publish.yml), so a stale Chart.yaml ships a - # chart whose source tree disagrees with the version it was published - # under — which is exactly how main drifted to 0.5.1 while 0.6.x shipped. - case "${GITHUB_REF:-}" in - refs/tags/v*) - TAG="${GITHUB_REF_NAME#v}" - echo "release tag=$TAG" - if [ "$VERSION" != "$TAG" ]; then - echo "::error file=$CHART::Chart.yaml version ($VERSION) does not match release tag $GITHUB_REF_NAME (expected $TAG). Bump version and appVersion to $TAG before tagging." - exit 1 - fi - ;; - esac From 8668ee4297e49a3edfcdf40b69b31fca6cf43fd4 Mon Sep 17 00:00:00 2001 From: Lucas Mundim Date: Tue, 16 Jun 2026 11:14:54 -0300 Subject: [PATCH 3/3] docs(release): credit the publish-chart gate, not lint.yml, for tag-parity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per review (leandrobmarinho): the Releasing section said lint.yml's chart-version-parity 'on a release tag, asserts they match the tag' — but that check only does version==appVersion on PRs and never sees the tag. The actual tag-parity gate is the 'Assert Chart.yaml matches the release tag' step in docker-publish.yml's publish-chart job, which exits non-zero before package/push. Re-point the prose so a future maintainer doesn't remove the real gate believing lint.yml covers it. --- deployment/openfilter-pipelines-controller/README.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/deployment/openfilter-pipelines-controller/README.md b/deployment/openfilter-pipelines-controller/README.md index e88c52d..357b0e3 100644 --- a/deployment/openfilter-pipelines-controller/README.md +++ b/deployment/openfilter-pipelines-controller/README.md @@ -237,15 +237,19 @@ helm install openfilter-pipelines-controller deployment/openfilter-pipelines-con Releases are driven entirely by git tags. To cut version `X.Y.Z`: 1. Bump `version` **and** `appVersion` in `Chart.yaml` to `X.Y.Z` in the same - commit and merge it via PR. The `chart-version-parity` CI check - (`.github/workflows/lint.yml`) keeps the two fields in lockstep and, on a - release tag, asserts they match the tag — so a stale `Chart.yaml` fails CI - before anything is published. + commit and merge it via PR. The `chart-version-parity` check in + `.github/workflows/lint.yml` runs on the PR and keeps `version` and + `appVersion` in lockstep (it does **not** see the tag). 2. Tag the merged commit `vX.Y.Z` and push the tag. On the tag push, `.github/workflows/docker-publish.yml`: - builds and pushes the controller + claimer images to Docker Hub, +- in the `publish-chart` job, the **`Assert Chart.yaml matches the release tag`** + step verifies `version == appVersion == X.Y.Z` and exits non-zero on a + mismatch **before** the chart is packaged or pushed — so a stale `Chart.yaml` + fails the publish rather than shipping a chart whose source tree disagrees with + the version it was published under, - packages the chart (with `--version`/`--app-version` derived from the tag) and pushes it to `oci://ghcr.io/plainsightai/charts`, and - creates a GitHub Release for the tag with the packaged chart `.tgz` attached.