Skip to content

trigger-release-tasks #823

trigger-release-tasks

trigger-release-tasks #823

name: "trigger-release-tasks"
# When a version's release-tag build (build-test-distribute) completes, fan out
# the per-version post-build release tasks in parallel:
# - smoke: the downstream enterprise smoke-test workflow (cross-repo dispatch)
# - verify: this repo's own release workflow with check=true (same-repo)
# - installer: runs this repo's own installer.sh end-to-end for the version
# (kuma.io here; developer.konghq.com/mesh once synced into kong-mesh)
# Per-branch/event-driven, not a batch fan-out over all versions. A single guard
# job decides "is this a real release tag?" once; all three tasks read it.
on:
workflow_run:
workflows: ["build-test-distribute"]
types: [completed]
# cuts the dominant noise source: every master/release-* push completion
# otherwise creates a (skipped) run before the guard job's `if` even
# evaluates. Tags aren't filterable here (GitHub doesn't expose tag
# matching for workflow_run), so tag-shape validation still lives in guard.
branches-ignore: ["master", "release-*"]
workflow_dispatch:
inputs:
version:
description: "Product version to release-task, e.g. 2.14.1 or v2.14.1"
required: true
type: string
run_smoke:
description: "Dispatch the downstream smoke tests"
required: false
type: boolean
default: true
run_verify:
description: "Dispatch artifact verification (release check=true)"
required: false
type: boolean
default: true
run_installer:
description: "Run the installer.sh verification"
required: false
type: boolean
default: true
permissions: {}
concurrency:
group: ${{ github.workflow }}-${{ github.event.workflow_run.head_branch || inputs.version }}
cancel-in-progress: true
jobs:
guard:
# workflow_run has no reliable tag-level filter at the trigger layer (GitHub
# only supports `branches`/`branches-ignore`, and those are documented as
# branch-only - unreliable/silently-broken for tag-triggered runs). So this
# `if` is the earliest point we can filter, and it excludes the two branch
# names that account for the overwhelming majority of build-test-distribute
# completions (master, release-*) before a runner is even allocated. Actual
# tag-shape validation (vX.Y.Z / 2.9's bare X.Y.Z) still happens in-job below.
if: >-
github.event_name == 'workflow_dispatch' ||
(
github.event.workflow_run.conclusion == 'success' &&
github.event.workflow_run.event == 'push' &&
github.event.workflow_run.head_branch != 'master' &&
!startsWith(github.event.workflow_run.head_branch, 'release-')
)
timeout-minutes: 5
runs-on: ${{ vars.RUNS_ON_MASTER_AMD64 || vars.RUNS_ON_AMD64 || 'ubuntu-24.04' }}
permissions:
contents: read # to confirm the ref is really a tag, via the git refs API
outputs:
should_dispatch: ${{ steps.version.outputs.should_dispatch }}
product_version: ${{ steps.version.outputs.product_version }}
steps:
- name: "Resolve version"
id: version
env:
GH_TOKEN: ${{ github.token }}
MANUAL_VERSION: ${{ inputs.version }}
HEAD_BRANCH: ${{ github.event.workflow_run.head_branch }}
HEAD_SHA: ${{ github.event.workflow_run.head_sha }}
run: |
set -euo pipefail
# v-prefix optional: release-2.9 never got the v-prefix backport and
# tags bare; manual re-runs also conventionally use the bare form.
RAW="${MANUAL_VERSION:-${HEAD_BRANCH}}"
if [[ "${RAW}" =~ ^v?([0-9]+\.[0-9]+\.[0-9]+)$ ]]; then
PRODUCT_VERSION="${BASH_REMATCH[1]}"
else
echo "'${RAW}' is not a recognized release version/tag; skipping release-task dispatch."
echo "should_dispatch=false" >> "$GITHUB_OUTPUT"
exit 0
fi
if [[ -n "${MANUAL_VERSION}" ]]; then
# deliberate operator override; may target an archived version
echo "should_dispatch=true" >> "$GITHUB_OUTPUT"
echo "product_version=${PRODUCT_VERSION}" >> "$GITHUB_OUTPUT"
exit 0
fi
# confirm head_branch is this tag, not a same-named branch, and it
# still points at the built commit, not one it moved to since
TAG="${RAW}"
# keep stderr in the run log: a missing tag (404) is the intended
# clean skip, but a rate-limit/network/auth failure must not vanish
# silently behind the same should_dispatch=false line.
if ! TAG_REF_JSON="$(gh api "repos/${GITHUB_REPOSITORY}/git/ref/tags/${TAG}" 2>/tmp/gh_tag_ref_err)"; then
echo "'${TAG}' is not an existing tag ref (or the lookup failed); skipping release-task dispatch." >&2
cat /tmp/gh_tag_ref_err >&2 || true
echo "should_dispatch=false" >> "$GITHUB_OUTPUT"
exit 0
fi
OBJ_TYPE="$(jq -r '.object.type' <<< "${TAG_REF_JSON}")"
OBJ_SHA="$(jq -r '.object.sha' <<< "${TAG_REF_JSON}")"
# annotated tags point at a tag object, not the commit directly
if [[ "${OBJ_TYPE}" == "tag" ]]; then
OBJ_SHA="$(gh api "repos/${GITHUB_REPOSITORY}/git/tags/${OBJ_SHA}" --jq '.object.sha')"
fi
if [[ "${OBJ_SHA}" != "${HEAD_SHA}" ]]; then
echo "'${TAG}' does not currently point at built commit ${HEAD_SHA}; skipping release-task dispatch."
echo "should_dispatch=false" >> "$GITHUB_OUTPUT"
exit 0
fi
echo "should_dispatch=true" >> "$GITHUB_OUTPUT"
echo "product_version=${PRODUCT_VERSION}" >> "$GITHUB_OUTPUT"
smoke:
needs: guard
# workflow_run carries no dispatch inputs, so run_smoke is empty there and
# must not gate the auto path; only the manual path honours the toggle.
if: >-
needs.guard.outputs.should_dispatch == 'true' &&
(github.event_name != 'workflow_dispatch' || inputs.run_smoke)
timeout-minutes: 5
runs-on: ${{ vars.RUNS_ON_MASTER_AMD64 || vars.RUNS_ON_AMD64 || 'ubuntu-24.04' }}
permissions: {}
steps:
- name: "Trigger downstream smoke test"
env:
# smoke.yaml has no repository_dispatch trigger, so this dispatches
# via repository_dispatch to kong-mesh-smoke instead, which listens
# for KUMA_SMOKE_TEST via dispatch-kuma-smoke.yaml and forwards to
# smoke.yaml with the default GITHUB_TOKEN (same-repo, no cross-org
# auth needed on that side). Needs a classic PAT with `repo` scope,
# SSO-authorized for the Kong org - fine-grained PATs aren't enabled
# there for cross-org repo selection.
GH_TOKEN: ${{ secrets.KONG_MESH_SMOKE_DISPATCH_TOKEN }}
NOTIFY_OWNER: ${{ secrets.NOTIFY_OWNER }}
SMOKE_REPO: kong-mesh-smoke
PRODUCT_VERSION: ${{ needs.guard.outputs.product_version }}
run: |
set -euo pipefail
# product_name is the built edition, which is the repo we run in.
case "${GITHUB_REPOSITORY}" in
*/kong-mesh) PRODUCT_NAME=kong-mesh ;;
*) PRODUCT_NAME=kuma ;;
esac
jq -n \
--arg product_name "${PRODUCT_NAME}" \
--arg product_version "${PRODUCT_VERSION}" \
'{
event_type: "KUMA_SMOKE_TEST",
client_payload: {
product_name: $product_name,
product_version: $product_version,
k8s_provider_k3d: true,
k8s_provider_gke: true,
k8s_provider_eks: true,
uni_provider_gcp: true,
uni_provider_aws: true,
run_tf_provider_tests: false
}
}' | gh api "repos/${NOTIFY_OWNER}/${SMOKE_REPO}/dispatches" --input -
verify:
needs: guard
# see smoke job: run_verify only gates the manual path.
if: >-
needs.guard.outputs.should_dispatch == 'true' &&
(github.event_name != 'workflow_dispatch' || inputs.run_verify)
timeout-minutes: 5
runs-on: ${{ vars.RUNS_ON_MASTER_AMD64 || vars.RUNS_ON_AMD64 || 'ubuntu-24.04' }}
permissions:
# same-repo dispatch needs no cross-org token, unlike smoke; the default
# GITHUB_TOKEN only gains workflow_dispatch rights with actions:write.
actions: write
steps:
- name: "Trigger artifact verification"
env:
GH_TOKEN: ${{ github.token }}
PRODUCT_VERSION: ${{ needs.guard.outputs.product_version }}
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
run: |
set -euo pipefail
# same display name ("release") both sides, but a different filename in
# the enterprise fork; dispatch by filename to stay unambiguous.
case "${GITHUB_REPOSITORY}" in
*/kong-mesh) RELEASE_WORKFLOW=kuma-release.yaml ;;
*) RELEASE_WORKFLOW=release.yaml ;;
esac
gh workflow run "${RELEASE_WORKFLOW}" \
--repo "${GITHUB_REPOSITORY}" \
--ref "${DEFAULT_BRANCH}" \
--field release="${PRODUCT_VERSION}" \
--field check=true
installer:
needs: guard
# see smoke job: run_installer only gates the manual path.
if: >-
needs.guard.outputs.should_dispatch == 'true' &&
(github.event_name != 'workflow_dispatch' || inputs.run_installer)
timeout-minutes: 5
runs-on: ${{ vars.RUNS_ON_MASTER_AMD64 || vars.RUNS_ON_AMD64 || 'ubuntu-24.04' }}
permissions: {}
steps:
- name: "Run installer.sh for the released version"
env:
PRODUCT_VERSION: ${{ needs.guard.outputs.product_version }}
run: |
set -euo pipefail
# developer.konghq.com/mesh/installer.sh is a thin wrapper that pipes
# back through kuma.io/installer.sh with PRODUCT_NAME/REPO overrides,
# so hitting the right host is what actually exercises each product's
# own published binaries.
case "${GITHUB_REPOSITORY}" in
*/kong-mesh) INSTALLER_URL="https://developer.konghq.com/mesh/installer.sh" ;;
*) INSTALLER_URL="https://kuma.io/installer.sh" ;;
esac
# installer.sh itself exits non-zero (via its `err` helper) when the
# version's binary isn't published, so a clean run is the assertion;
# still grep its "has been downloaded!" line as a positive check that
# it actually reached and confirmed PRODUCT_VERSION rather than
# exiting 0 some other way.
if ! OUTPUT="$(curl --fail --silent --show-error --location "${INSTALLER_URL}" | VERSION="${PRODUCT_VERSION}" sh - 2>&1)"; then
echo "${OUTPUT}"
echo "installer.sh failed for version ${PRODUCT_VERSION} via ${INSTALLER_URL}" >&2
exit 1
fi
echo "${OUTPUT}"
if ! grep -qF "${PRODUCT_VERSION} has been downloaded" <<< "${OUTPUT}"; then
echo "installer.sh did not confirm ${PRODUCT_VERSION} was downloaded via ${INSTALLER_URL}" >&2
exit 1
fi