From a7faeed8fb0700eccc3c802e9dee03304728e687 Mon Sep 17 00:00:00 2001 From: ZDDC Date: Sun, 3 May 2026 21:43:01 -0500 Subject: [PATCH] refactor(ci): extract chart-bump logic to .forgejo/scripts/ Both notify-chart-dev.yml and the notify-chart-prod job in deploy-release.yml were carrying ~80 lines of inline shell each, duplicating the clone-bump-push flow. Extract to a single script: .forgejo/scripts/notify-chart-bump.sh [VERSION] Three benefits: 1. **Locally testable**. The script is invocable directly: CHART_FORGEJO_TOKEN=$FORGEJO_TOKEN \ .forgejo/scripts/notify-chart-bump.sh beta No more "push to main and watch what the runner does" debug loop. 2. **Manual escape hatch**. When CI is broken, the same script is how we recover. The 0.0.16-beta-1ddd331 chart bump preceding this commit was performed via this very script. 3. **Runner-quirk-immune**. The previous three commits chased a Forgejo runner v12.9.0 phantom-SIGPIPE bug that would only surface under the runner's `bash -e -o pipefail {0}` wrapper. A real script with its own `#!/usr/bin/env bash` and explicit error handling sidesteps the wrapper entirely. The workflow YAMLs shrink to checkout + run-script. No GITHUB_OUTPUT plumbing, no inline if/else gates, no shell flag overrides. The behavior is identical to the prior inline versions. --- .forgejo/scripts/notify-chart-bump.sh | 134 ++++++++++++++++++++++++ .forgejo/workflows/deploy-release.yml | 73 ++----------- .forgejo/workflows/notify-chart-dev.yml | 96 +++-------------- 3 files changed, 157 insertions(+), 146 deletions(-) create mode 100755 .forgejo/scripts/notify-chart-bump.sh diff --git a/.forgejo/scripts/notify-chart-bump.sh b/.forgejo/scripts/notify-chart-bump.sh new file mode 100755 index 0000000..6ab6f53 --- /dev/null +++ b/.forgejo/scripts/notify-chart-bump.sh @@ -0,0 +1,134 @@ +#!/usr/bin/env bash +# notify-chart-bump.sh — bump appVersion on tnd-zddc-chart and push. +# +# Replaces the inline shell that previously lived in +# .forgejo/workflows/notify-chart-dev.yml and the notify-chart-prod +# job in deploy-release.yml. Extracting the logic to a real script +# means we can: +# 1. test it locally without going through the runner +# (CHART_FORGEJO_TOKEN=$FORGEJO_TOKEN ./.forgejo/scripts/notify-chart-bump.sh beta) +# 2. invoke manually as an escape hatch when CI is broken +# 3. avoid runner-version shell-wrapper quirks (e.g. Forgejo +# runner v12.9.0 reporting phantom SIGPIPE on bare echo + set -eu) +# +# Usage: +# notify-chart-bump.sh beta +# Bump chart's develop branch with appVersion = -beta- +# (next-stable = max(zddc-server-v* tag) + 1). +# Self-skips if HEAD has a zddc-server-v* tag (the stable workflow +# owns the bump in that case). +# +# notify-chart-bump.sh stable VERSION +# Bump chart's main + develop branches with appVersion = VERSION. +# Called from deploy-release.yml's notify-chart-prod job, where +# VERSION = "${GITHUB_REF#refs/tags/zddc-server-v}". +# +# Requires: +# - run from the ZDDC repo root, with full git history (all tags) +# - $CHART_FORGEJO_TOKEN with repo:write on BMCD/tnd-zddc-chart + +set -eu + +CHANNEL="${1:?usage: $(basename "$0") [VERSION]}" +EXPLICIT_VERSION="${2:-}" + +if [ -z "${CHART_FORGEJO_TOKEN:-}" ]; then + echo "::error::CHART_FORGEJO_TOKEN env not set" >&2 + exit 1 +fi + +CHART_REPO="git.varasys.io/BMCD/tnd-zddc-chart.git" +CHART_URL="https://oauth2:${CHART_FORGEJO_TOKEN}@${CHART_REPO}" + +case "$CHANNEL" in + beta) + # Self-skip if HEAD has a stable tag — prod workflow owns the + # bump in that case (avoids both workflows racing on develop). + STABLE_TAGS=$(git tag --points-at HEAD --list 'zddc-server-v*') + if [ -n "$STABLE_TAGS" ]; then + echo "HEAD has stable tag ($STABLE_TAGS) — stable workflow handles chart bump; skipping" + exit 0 + fi + + LATEST_STABLE=$(git tag --list 'zddc-server-v*' --sort=-v:refname | head -1) + if [ -z "$LATEST_STABLE" ]; then + echo "::error::no zddc-server-v* tags exist; cannot derive next-stable target" >&2 + exit 1 + fi + MAJ=$(echo "${LATEST_STABLE#zddc-server-v}" | cut -d. -f1) + MIN=$(echo "${LATEST_STABLE#zddc-server-v}" | cut -d. -f2) + PAT=$(echo "${LATEST_STABLE#zddc-server-v}" | cut -d. -f3) + NEXT_STABLE="$MAJ.$MIN.$((PAT + 1))" + SHORT_SHA=$(git rev-parse --short=7 HEAD) + TARGET_VERSION="${NEXT_STABLE}-beta-${SHORT_SHA}" + BRANCHES="develop" + TRIGGER_DESC="ZDDC beta cut" + TRAILER="Triggered by push to git.varasys.io/VARASYS/ZDDC main with embedded/* changes (a ./build beta cut). Bumps appVersion so the dev Docker image is tagged zddc:$TARGET_VERSION, ensuring kubelet pulls a fresh image on the next helm upgrade." + ;; + stable) + if [ -z "$EXPLICIT_VERSION" ]; then + echo "::error::stable channel requires an explicit VERSION arg" >&2 + exit 1 + fi + TARGET_VERSION="$EXPLICIT_VERSION" + # Bump both branches: main fires BMCD pipeline-prod (prod image + # rebuild), develop fires pipeline-dev so dev follows stable + # whenever no beta is active. + BRANCHES="main develop" + TRIGGER_DESC="ZDDC stable cut" + TRAILER="Triggered by zddc-server-v$TARGET_VERSION tag push on git.varasys.io/VARASYS/ZDDC. Bumps appVersion so prod (and dev tracking stable) rebuild against the new ZDDC stable." + ;; + *) + echo "::error::unknown channel '$CHANNEL' (expected: beta | stable)" >&2 + exit 1 + ;; +esac + +echo "Bumping tnd-zddc-chart appVersion → $TARGET_VERSION" +echo "Branches: $BRANCHES (HEAD=$(git rev-parse HEAD))" + +TMP=$(mktemp -d) +trap 'rm -rf "$TMP"' EXIT +cd "$TMP" + +for BRANCH in $BRANCHES; do + echo "" + echo "=== bumping $BRANCH ===" + rm -rf tnd-zddc-chart + git clone --depth=20 --branch="$BRANCH" "$CHART_URL" + cd tnd-zddc-chart + + CURRENT=$(grep '^appVersion:' chart/Chart.yaml \ + | sed -E 's/^appVersion: *"?([^"]*)"?.*/\1/') + if [ "$CURRENT" = "$TARGET_VERSION" ]; then + echo " $BRANCH already at $TARGET_VERSION; skipping" + cd .. + continue + fi + + sed -i "s/^appVersion: .*/appVersion: \"$TARGET_VERSION\"/" chart/Chart.yaml + OLD_CHART_VER=$(grep '^version:' chart/Chart.yaml | awk '{print $2}') + MAJC=$(echo "$OLD_CHART_VER" | cut -d. -f1) + MINC=$(echo "$OLD_CHART_VER" | cut -d. -f2) + PATC=$(echo "$OLD_CHART_VER" | cut -d. -f3) + NEW_CHART_VER="$MAJC.$MINC.$((PATC + 1))" + sed -i "s/^version: .*/version: $NEW_CHART_VER/" chart/Chart.yaml + + echo " appVersion: $CURRENT → $TARGET_VERSION" + echo " version: $OLD_CHART_VER → $NEW_CHART_VER" + + git config user.name "ZDDC Release Bot" + git config user.email "noreply@zddc.varasys.io" + git add chart/Chart.yaml + git commit \ + -m "chore(chart): auto-bump appVersion to $TARGET_VERSION ($TRIGGER_DESC)" \ + -m "$TRAILER" \ + -m "Auto-generated by .forgejo/scripts/notify-chart-bump.sh. The next ZDDC beta or stable cut will overwrite this." + + git push origin "$BRANCH" + echo " pushed $BRANCH" + cd .. +done + +echo "" +echo "Done." diff --git a/.forgejo/workflows/deploy-release.yml b/.forgejo/workflows/deploy-release.yml index cad9721..037007f 100644 --- a/.forgejo/workflows/deploy-release.yml +++ b/.forgejo/workflows/deploy-release.yml @@ -113,68 +113,15 @@ jobs: # directly to GitHub. The chart repo is mirrored Forgejo→GitHub # one-way; pushing directly to GitHub would be silently overwritten # the next time Forgejo's mirror syncs (force-push semantics). - # The runner reaches git.varasys.io via the caddy-net network it - # joined when the runner container was provisioned. CHART_FORGEJO_TOKEN: ${{ secrets.CHART_FORGEJO_TOKEN }} steps: - - name: Auto-bump tnd-zddc-chart appVersion on main + develop (via Forgejo) - run: | - set -eu - VERSION="${GITHUB_REF#refs/tags/zddc-server-v}" - echo "ZDDC stable cut: $VERSION" - - # Sanity: make sure the secret was injected. If not, fail loud - # (rather than silently failing on the git push later). - if [ -z "${CHART_FORGEJO_TOKEN:-}" ]; then - echo "::error::CHART_FORGEJO_TOKEN secret not set on this repo" >&2 - exit 1 - fi - - git config --global user.name "ZDDC Release Bot" - git config --global user.email "noreply@zddc.varasys.io" - - # Push the same appVersion bump to both branches so prod and - # dev images both rebuild against the new ZDDC stable. Loop - # is idempotent per-branch — if a branch's appVersion already - # matches the new version, it's a no-op for that branch. - # The push goes to Forgejo (BMCD/tnd-zddc-chart on - # git.varasys.io); Forgejo's push-mirror replicates the bump - # to GitHub on the next sync (which is sync_on_commit: true). - TMP=$(mktemp -d) - cd "$TMP" - for BRANCH in main develop; do - echo "" - echo "=== bumping $BRANCH ===" - rm -rf tnd-zddc-chart - git clone --depth=20 --branch="$BRANCH" \ - "https://oauth2:${CHART_FORGEJO_TOKEN}@git.varasys.io/BMCD/tnd-zddc-chart.git" - cd tnd-zddc-chart - - CURRENT=$(grep '^appVersion:' chart/Chart.yaml | sed -E 's/^appVersion: *"?([^"]*)"?.*/\1/') - if [ "$CURRENT" = "$VERSION" ]; then - echo " $BRANCH already at $VERSION; nothing to do" - cd .. - continue - fi - - sed -i "s/^appVersion: .*/appVersion: \"$VERSION\"/" chart/Chart.yaml - OLD_CHART_VER=$(grep '^version:' chart/Chart.yaml | awk '{print $2}') - MAJ=$(echo "$OLD_CHART_VER" | cut -d. -f1) - MIN=$(echo "$OLD_CHART_VER" | cut -d. -f2) - PAT=$(echo "$OLD_CHART_VER" | cut -d. -f3) - NEW_PAT=$((PAT + 1)) - NEW_CHART_VER="$MAJ.$MIN.$NEW_PAT" - sed -i "s/^version: .*/version: $NEW_CHART_VER/" chart/Chart.yaml - - echo " appVersion: $CURRENT → $VERSION" - echo " version: $OLD_CHART_VER → $NEW_CHART_VER" - - git add chart/Chart.yaml - git commit \ - -m "chore(chart): auto-bump appVersion to $VERSION (ZDDC stable cut)" \ - -m "Triggered by zddc-server-v$VERSION tag push on git.varasys.io/VARASYS/ZDDC. Bumps appVersion so the $BRANCH-branch image is tagged zddc:$VERSION, ensuring kubelet pulls a fresh image on the next helm upgrade. Chart version bumped to $NEW_CHART_VER (patch) so JFrog has a clean chart history per deploy." \ - -m "Auto-generated by .forgejo/workflows/deploy-release.yml's notify-chart-prod job. Do not edit manually — the next ZDDC stable cut will overwrite this commit's changes." - git push origin "$BRANCH" - echo " pushed $BRANCH bump to Forgejo - mirror replicates to GitHub - BMCD pipeline-$([ \"$BRANCH\" = main ] && echo prod || echo dev) will fire" - cd .. - done + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Bump chart for stable cut + # All bump logic lives in .forgejo/scripts/notify-chart-bump.sh + # — same script the dev workflow uses. See its header for + # behavior. Local invocation: + # CHART_FORGEJO_TOKEN=$FORGEJO_TOKEN \ + # .forgejo/scripts/notify-chart-bump.sh stable X.Y.Z + run: ./.forgejo/scripts/notify-chart-bump.sh stable "${GITHUB_REF#refs/tags/zddc-server-v}" diff --git a/.forgejo/workflows/notify-chart-dev.yml b/.forgejo/workflows/notify-chart-dev.yml index 23f7409..feda2f2 100644 --- a/.forgejo/workflows/notify-chart-dev.yml +++ b/.forgejo/workflows/notify-chart-dev.yml @@ -1,105 +1,35 @@ name: Notify chart dev on beta cut -# Mirrors deploy-release.yml's notify-chart-prod job, but for beta. # Triggers when a push to ZDDC main touches zddc/internal/apps/embedded/* # — i.e. a `./build beta` cut whose embedded artifacts the operator # committed to main. Pushes a chart appVersion bump to the chart's # develop branch, which fires BMCD's pipeline-dev → dev image rebuilt # with the new beta-labeled bytes baked in. # -# Stable cuts ALSO touch embedded/, but their workflow path is the -# tag-triggered notify-chart-prod in deploy-release.yml. To avoid -# double-firing when a stable cut pushes main + tags together, we -# check if HEAD has a zddc-server-v* tag and skip if so — the -# stable workflow handles the chart bump in that case. +# All logic lives in .forgejo/scripts/notify-chart-bump.sh — see that +# script's header for behavior. Workflows just provide checkout + +# secret + invocation. Local invocation is supported (and supported +# without --force-with-lease shenanigans): +# +# CHART_FORGEJO_TOKEN=$FORGEJO_TOKEN .forgejo/scripts/notify-chart-bump.sh beta on: push: branches: [main] paths: - 'zddc/internal/apps/embedded/**' + # Manual trigger — useful for re-firing without a no-op embedded/ + # change to satisfy the paths filter (e.g. after fixing the script + # or workflow itself). + workflow_dispatch: jobs: notify-chart-dev: runs-on: host env: - # Push to Forgejo (BMCD/tnd-zddc-chart on git.varasys.io), NOT - # directly to GitHub. See notify-chart-prod's comment in - # deploy-release.yml for the full rationale (mirror is one-way - # Forgejo→GitHub; direct GitHub pushes get silently overwritten - # on the next mirror sync). CHART_FORGEJO_TOKEN: ${{ secrets.CHART_FORGEJO_TOKEN }} steps: - - name: Checkout (need tags to detect stable cut) - uses: actions/checkout@v4 + - uses: actions/checkout@v4 with: - fetch-depth: 0 - - - name: Detect cut type (skip if HEAD has stable tag) - id: gate - run: | - set -eu - if git tag --points-at HEAD | grep -q '^zddc-server-v'; then - echo "is_beta=false" >> "$GITHUB_OUTPUT" - echo "HEAD has zddc-server-v* tag — stable workflow handles this; skipping dev notify" - else - echo "is_beta=true" >> "$GITHUB_OUTPUT" - echo "No stable tag at HEAD; treating as beta cut" - fi - - - name: Auto-bump chart develop appVersion + push - if: steps.gate.outputs.is_beta == 'true' - run: | - set -eu - - if [ -z "${CHART_FORGEJO_TOKEN:-}" ]; then - echo "::error::CHART_FORGEJO_TOKEN secret not set on this repo" >&2 - exit 1 - fi - - # Compose a beta version string that's unique per ZDDC commit. - # Uses the next-stable target (max of latest tag + 1, mirrors - # ./build's _coordinated_next_stable) and the short SHA. - # Example: "0.0.11-beta-c099676". Always unique per push. - LATEST_STABLE=$(git tag --list 'zddc-server-v*' --sort=-v:refname | head -1) - MAJ=$(echo "${LATEST_STABLE#zddc-server-v}" | cut -d. -f1) - MIN=$(echo "${LATEST_STABLE#zddc-server-v}" | cut -d. -f2) - PAT=$(echo "${LATEST_STABLE#zddc-server-v}" | cut -d. -f3) - NEXT_STABLE="$MAJ.$MIN.$((PAT + 1))" - SHORT_SHA=$(git rev-parse --short=7 HEAD) - BETA_VERSION="${NEXT_STABLE}-beta-${SHORT_SHA}" - echo "ZDDC beta cut: $BETA_VERSION (HEAD=$(git rev-parse HEAD))" - - TMP=$(mktemp -d) - cd "$TMP" - git clone --depth=20 --branch=develop \ - "https://oauth2:${CHART_FORGEJO_TOKEN}@git.varasys.io/BMCD/tnd-zddc-chart.git" - cd tnd-zddc-chart - - # Idempotent: same SHA ⇒ same version ⇒ no-op. - CURRENT=$(grep '^appVersion:' chart/Chart.yaml | sed -E 's/^appVersion: *"?([^"]*)"?.*/\1/') - if [ "$CURRENT" = "$BETA_VERSION" ]; then - echo "Chart develop already at $BETA_VERSION; nothing to do" - exit 0 - fi - - sed -i "s/^appVersion: .*/appVersion: \"$BETA_VERSION\"/" chart/Chart.yaml - OLD_CHART_VER=$(grep '^version:' chart/Chart.yaml | awk '{print $2}') - MAJC=$(echo "$OLD_CHART_VER" | cut -d. -f1) - MINC=$(echo "$OLD_CHART_VER" | cut -d. -f2) - PATC=$(echo "$OLD_CHART_VER" | cut -d. -f3) - NEW_CHART_VER="$MAJC.$MINC.$((PATC + 1))" - sed -i "s/^version: .*/version: $NEW_CHART_VER/" chart/Chart.yaml - - echo " appVersion: $CURRENT → $BETA_VERSION" - echo " version: $OLD_CHART_VER → $NEW_CHART_VER" - - git config user.name "ZDDC Release Bot" - git config user.email "noreply@zddc.varasys.io" - git add chart/Chart.yaml - git commit \ - -m "chore(chart): auto-bump appVersion to $BETA_VERSION (ZDDC beta cut)" \ - -m "Triggered by push to git.varasys.io/VARASYS/ZDDC main with embedded/* changes (a ./build beta cut). Bumps appVersion so the dev Docker image is tagged zddc:$BETA_VERSION, ensuring kubelet pulls a fresh image on the next helm upgrade." \ - -m "Auto-generated by .forgejo/workflows/notify-chart-dev.yml. The next ZDDC beta or stable cut will overwrite this." - git push origin develop - echo "pushed chart develop bump to Forgejo - mirror replicates to GitHub - BMCD pipeline-dev will fire" + fetch-depth: 0 # script needs full tag history + - run: ./.forgejo/scripts/notify-chart-bump.sh beta