mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
ci(helm): publish chart to GHCR on chart-v* tags (#1372)
Signed-off-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co> Co-authored-by: Tyler Longwell <tlongwell@block.xyz> Co-authored-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>
This commit is contained in:
co-authored by
Tyler Longwell
npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757
parent
15a73aa27f
commit
2722ce4226
@@ -1,19 +1,21 @@
|
||||
name: Auto-tag on Release PR Merge
|
||||
|
||||
# Three release lanes share this one workflow — adding a lane is one more branch
|
||||
# Four release lanes share this one workflow — adding a lane is one more branch
|
||||
# prefix, never a forked copy:
|
||||
#
|
||||
# version-bump/<v> → tag v<v> → dispatch release.yml (desktop app)
|
||||
# relay-release/<v> → tag relay-v<v> → dispatch docker.yml (relay image)
|
||||
# chart-release/<v> → tag chart-v<v> → dispatch helm-chart.yml (helm chart)
|
||||
# mobile-release/<v> → tag mobile-v<v> → (manual sprout_ref for buzz-releases build — see below)
|
||||
#
|
||||
# Both the desktop and relay lanes dispatch their build workflow rather than
|
||||
# The desktop, relay, and chart lanes dispatch their build workflow rather than
|
||||
# relying on the consumer's `on.push.tags` trigger: auto-tag pushes the tag
|
||||
# with the default GITHUB_TOKEN, which GitHub's recursion guard blocks from
|
||||
# firing any `on: push` trigger. So release.yml and docker.yml both have a
|
||||
# `push.tags` trigger that is dead for auto-pushed tags — the dispatch is the
|
||||
# real path. Each dispatch passes the bare version and the tag ref so the
|
||||
# consumer builds the tagged commit (github.ref on a dispatch is `main`).
|
||||
# firing any `on: push` trigger. So release.yml, docker.yml, and helm-chart.yml
|
||||
# all have a `push.tags` trigger that is dead for auto-pushed tags — the
|
||||
# dispatch is the real path. Each dispatch passes the bare version and the tag
|
||||
# ref so the consumer builds the tagged commit (github.ref on a dispatch is
|
||||
# `main`).
|
||||
#
|
||||
# The mobile lane is push-only by infosec necessity: OSS `block/buzz` CI must
|
||||
# not trigger CI in the private `buzz-releases` repo, so auto-dispatch across
|
||||
@@ -36,6 +38,7 @@ jobs:
|
||||
github.event.pull_request.merged == true &&
|
||||
(startsWith(github.event.pull_request.head.ref, 'version-bump/') ||
|
||||
startsWith(github.event.pull_request.head.ref, 'relay-release/') ||
|
||||
startsWith(github.event.pull_request.head.ref, 'chart-release/') ||
|
||||
startsWith(github.event.pull_request.head.ref, 'mobile-release/')) &&
|
||||
github.event.pull_request.head.repo.full_name == github.repository
|
||||
runs-on: ubuntu-latest
|
||||
@@ -60,6 +63,10 @@ jobs:
|
||||
VERSION="${BRANCH#relay-release/}"
|
||||
TAG_PREFIX="relay-v"
|
||||
DISPATCH="docker" ;;
|
||||
chart-release/*)
|
||||
VERSION="${BRANCH#chart-release/}"
|
||||
TAG_PREFIX="chart-v"
|
||||
DISPATCH="helm-chart" ;;
|
||||
mobile-release/*)
|
||||
VERSION="${BRANCH#mobile-release/}"
|
||||
TAG_PREFIX="mobile-v"
|
||||
@@ -113,6 +120,7 @@ jobs:
|
||||
case "$DISPATCH" in
|
||||
release) WORKFLOW="release.yml" ;;
|
||||
docker) WORKFLOW="docker.yml" ;;
|
||||
helm-chart) WORKFLOW="helm-chart.yml" ;;
|
||||
*)
|
||||
echo "::error::Unhandled dispatch target: '$DISPATCH'"
|
||||
exit 1 ;;
|
||||
|
||||
@@ -1,26 +1,72 @@
|
||||
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/<version>` 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 (same reason as docker.yml):
|
||||
# auto-tag-on-release-pr-merge.yml pushes `chart-v*` with the default
|
||||
# GITHUB_TOKEN, which GitHub's recursion guard blocks from firing any
|
||||
# `on: push` trigger. So the `push.tags` trigger below is dead for
|
||||
# auto-pushed tags — auto-tag instead dispatches this workflow with the bare
|
||||
# version + tag ref, and the publish job checks out inputs.ref and packages at
|
||||
# inputs.version. On a real manual `git push` of a `chart-v*` tag the
|
||||
# push.tags trigger fires and inputs are empty.
|
||||
|
||||
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]
|
||||
paths:
|
||||
- "deploy/charts/buzz/**"
|
||||
- ".github/workflows/helm-chart.yml"
|
||||
- "ct.yaml"
|
||||
tags: ["chart-v[0-9]*"]
|
||||
pull_request:
|
||||
paths:
|
||||
- "deploy/charts/buzz/**"
|
||||
- ".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@v4
|
||||
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
|
||||
@@ -62,10 +108,15 @@ jobs:
|
||||
# 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'
|
||||
if: github.event_name == 'workflow_dispatch' && inputs.version == ''
|
||||
runs-on: ubuntu-latest
|
||||
needs: lint-and-unittest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
@@ -95,3 +146,110 @@ jobs:
|
||||
|
||||
- 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 (manual) or the auto-tag 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@v4
|
||||
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"
|
||||
|
||||
@@ -90,7 +90,24 @@ Save these. Losing any of them is data loss. See NOTES.txt printed by `helm inst
|
||||
currently stands up real Redis and S3 rather than the relay's single-node
|
||||
fallbacks. (Full-text search already runs in Postgres, so no separate search
|
||||
service is provisioned.)
|
||||
- **OCI publish to GHCR + cosign signing** is a follow-up PR. For now, install the chart from source: `helm install buzz ./deploy/charts/buzz` after cloning the repo.
|
||||
- **Cosign signing of the published chart** is a follow-up (the relay image is
|
||||
attested via `actions/attest-build-provenance`; the chart is not yet). The
|
||||
chart itself is published to GHCR — see [Releasing](#releasing).
|
||||
|
||||
## Releasing
|
||||
|
||||
The chart is published to GHCR as an OCI artifact at
|
||||
`oci://ghcr.io/block/buzz/charts/buzz` by the `helm chart` workflow
|
||||
(`.github/workflows/helm-chart.yml`), versioned independently of the desktop app
|
||||
and the relay image via its own `chart-v*` tags. Every PR/`main` push still
|
||||
lints, unit-tests, and render-checks the chart; only a `chart-v*` tag publishes,
|
||||
so an in-progress `main` can never overwrite a released version.
|
||||
|
||||
To cut a release, push a `chart-release/<version>` branch whose `<version>`
|
||||
matches `Chart.yaml`'s `version`; merging it auto-tags `chart-v<version>` and
|
||||
dispatches the publish job (same lane machinery as the desktop and relay
|
||||
releases — see `.github/workflows/auto-tag-on-release-pr-merge.yml`). The publish
|
||||
job fails loudly if the tag version and `Chart.yaml` version disagree.
|
||||
|
||||
## Development
|
||||
|
||||
|
||||
Reference in New Issue
Block a user