name: helm chart # Lints + unit-tests + render-checks the chart on every PR/main push, and # PUBLISHES it to GHCR as an OCI artifact on `chart-v*` tags. # # Publishing mirrors the relay image (see docker.yml): the chart is versioned # independently of the desktop app and the relay via its own `chart-v*` tags # (Chart.yaml `version`, cut by merging a `chart-release/` PR). Only # `chart-v*` tags publish — `main` pushes and PRs stay lint/render-only so we # never overwrite a released chart version from an in-progress `main`. # # Why workflow_dispatch carries version/ref inputs: # Normal releases arrive through the `push.tags` trigger below. The inputs are # retained only for an operator to rerun publication manually at an immutable # chart tag; the publish job checks out inputs.ref and packages at # inputs.version. on: workflow_dispatch: inputs: version: description: "Chart semver e.g. 0.1.0 (no chart-v prefix) — for chart-tag rescue dispatch" required: false ref: description: "Chart tag ref to publish, e.g. chart-v0.1.0 (required when version is set)" required: false default: main push: # No `paths` filter here: GitHub applies a push `paths` filter to tag # pushes too, so a `chart-v*` tag whose commit didn't touch a chart file # would be filtered out and never publish. docker.yml / release.yml / sprig # all keep `paths` out of a tag-carrying `push` for exactly this reason — # PR runs stay scoped via `pull_request.paths` below; main pushes lint # unconditionally (cheap), and tag pushes always run so publish can fire. branches: [main] tags: ["chart-v[0-9]*"] pull_request: paths: - "deploy/charts/buzz/**" - "deploy/charts/buzz-push-gateway/**" - ".github/workflows/helm-chart.yml" - "ct.yaml" # Match docker.yml: deny-by-default, each job grants only what it needs. permissions: {} env: # Single source of truth for the OCI chart repository (helm appends the chart # name `buzz`, yielding oci://ghcr.io/block/buzz/charts/buzz, which is exactly # the install ref documented in deploy/charts/buzz/README.md). Set # GHCR_CHART_REPO as a repo variable to override (e.g., forks pushing to their # own namespace without editing this file) — mirrors docker.yml's GHCR_IMAGE. CHART_REPO: ${{ vars.GHCR_CHART_REPO != '' && vars.GHCR_CHART_REPO || 'oci://ghcr.io/block/buzz/charts' }} jobs: lint-and-unittest: name: lint + unittest + render matrix runs-on: ubuntu-latest permissions: contents: read steps: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 with: # On chart-tag rescue dispatch, lint/render the tagged commit that the # publish job will package, not whatever `main` is when the dispatch # runs. Empty string = default ref for push/PR events. ref: ${{ github.event_name == 'workflow_dispatch' && inputs.ref || '' }} fetch-depth: 0 - name: Set up Helm uses: azure/setup-helm@1a275c3b69536ee54be43f2070a358922e12c8d4 # v4.3.1 with: version: v3.16.4 - name: Install helm-unittest plugin run: helm plugin install --version 0.8.2 https://github.com/helm-unittest/helm-unittest - name: Set up Python (for chart-testing) uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 with: python-version: "3.12" - name: Set up chart-testing uses: helm/chart-testing-action@0d28d3144d3a25ea2cc349d6e59901c4ff469b3b # v2.7.0 - name: Build chart dependencies run: helm dependency build deploy/charts/buzz - name: Push gateway chart lint + render guard run: deploy/charts/buzz-push-gateway/tests/render.sh - name: ct lint run: ct lint --config ct.yaml --all - name: helm-unittest run: helm unittest deploy/charts/buzz - name: helm template (render every fixture) run: | set -euo pipefail for f in deploy/charts/buzz/ci/*-values.yaml deploy/charts/buzz/tests/fixtures/*-values.yaml; do echo "::group::render $f" helm template buzz deploy/charts/buzz -f "$f" echo "::endgroup::" done install-on-kind: # Full end-to-end install requires the public ghcr.io/block/buzz image to # exist and to embed Max's startup migrations. Runs only after Sami's # image PR merges (`workflow_dispatch`) or on a schedule once main carries # both prerequisites. Render/lint above is the per-PR signal. # # `inputs.version == ''` excludes the chart-tag rescue dispatch (which # carries a version): that path only packages+publishes, it does not install. name: install on kind (gated) if: github.event_name == 'workflow_dispatch' && inputs.version == '' runs-on: ubuntu-latest needs: lint-and-unittest permissions: contents: read steps: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 with: fetch-depth: 0 - name: Set up Helm uses: azure/setup-helm@1a275c3b69536ee54be43f2070a358922e12c8d4 # v4.3.1 with: version: v3.16.4 - name: Set up Python (for chart-testing) uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 with: python-version: "3.12" - name: Set up chart-testing uses: helm/chart-testing-action@0d28d3144d3a25ea2cc349d6e59901c4ff469b3b # v2.7.0 - name: Create kind cluster uses: helm/kind-action@0025e74a8c7512023d06dc019c617aa3cf561fde # v1.10.0 with: version: v0.24.0 node_image: kindest/node:v1.31.0 - name: Build chart dependencies run: helm dependency build deploy/charts/buzz - name: ct install (quickstart profile) run: ct install --config ct.yaml --charts deploy/charts/buzz --helm-extra-args "--timeout 600s" publish: # Packages the chart and pushes it to GHCR as an OCI artifact. Fires only on # a `chart-v*` tag push or a manual rescue dispatch (which # carries inputs.version) — never on `main` pushes or PRs, so an in-progress # `main` can never overwrite a released chart version. Mirrors docker.yml's # GHCR publish (login with GITHUB_TOKEN, packages: write, fork override var). name: publish chart to GHCR if: > startsWith(github.ref, 'refs/tags/chart-v') || (github.event_name == 'workflow_dispatch' && inputs.version != '') runs-on: ubuntu-latest needs: lint-and-unittest timeout-minutes: 15 permissions: contents: read packages: write # push the chart to GHCR steps: - name: Checkout uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 with: # On the rescue dispatch, build the tagged commit (github.ref is # `main` there); on a tag push, the default ref is already the tag. ref: ${{ github.event_name == 'workflow_dispatch' && inputs.ref || '' }} fetch-depth: 0 persist-credentials: false - name: Set up Helm uses: azure/setup-helm@1a275c3b69536ee54be43f2070a358922e12c8d4 # v4.3.1 with: version: v3.16.4 - name: Resolve chart version id: ver env: # Bare on the rescue dispatch; empty on a tag push (derive from ref). INPUT_VERSION: ${{ inputs.version }} REF_NAME: ${{ github.ref_name }} run: | set -euo pipefail if [ -n "$INPUT_VERSION" ]; then version="$INPUT_VERSION" tag_ref="refs/tags/chart-v${version}" tag_sha="$(git rev-parse -q --verify "${tag_ref}^{commit}" || true)" head_sha="$(git rev-parse HEAD)" if [ -z "$tag_sha" ] || [ "$head_sha" != "$tag_sha" ]; then echo "::error::workflow_dispatch with version '$version' must check out matching tag 'chart-v${version}' (HEAD=$head_sha, tag=${tag_sha:-missing})" exit 1 fi else # refs/tags/chart-v0.1.0 → github.ref_name is `chart-v0.1.0`. version="${REF_NAME#chart-v}" fi if ! echo "$version" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$'; then echo "::error::Resolved chart version '$version' is not valid semver" exit 1 fi # The tag is the source of truth, but the published artifact's version # comes from Chart.yaml — they must agree or we'd publish a mislabeled # chart. Fail loudly on drift rather than silently shipping a mismatch. chart_version="$(helm show chart deploy/charts/buzz | awk '/^version:/ {print $2}')" if [ "$chart_version" != "$version" ]; then echo "::error::Tag version '$version' != Chart.yaml version '$chart_version'. Bump Chart.yaml to match the tag." exit 1 fi echo "version=$version" >> "$GITHUB_OUTPUT" echo "Publishing chart version $version" - name: Log in to GHCR uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 with: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - name: Build chart dependencies run: helm dependency build deploy/charts/buzz - name: Package chart run: helm package deploy/charts/buzz --destination dist - name: Push chart to GHCR env: CHART_REPO: ${{ env.CHART_REPO }} VERSION: ${{ steps.ver.outputs.version }} run: | set -euo pipefail helm push "dist/buzz-${VERSION}.tgz" "$CHART_REPO" - name: Summary env: CHART_REPO: ${{ env.CHART_REPO }} VERSION: ${{ steps.ver.outputs.version }} run: | # CHART_REPO is oci://ghcr.io/block/buzz/charts; helm push appends the # chart name, so the install ref is .../charts/buzz. INSTALL_REF="${CHART_REPO}/buzz" { echo "### Published chart \`buzz\` \`${VERSION}\`" echo echo "**OCI ref:** \`${INSTALL_REF}:${VERSION}\`" echo echo "Install:" echo '```' echo "helm install buzz ${INSTALL_REF} --version ${VERSION}" echo '```' } >> "$GITHUB_STEP_SUMMARY"