mirror of
https://github.com/openziti/ziti.git
synced 2026-09-10 08:45:41 +00:00
227 lines
9.8 KiB
YAML
227 lines
9.8 KiB
YAML
name: Release Quickstart Workflow
|
|
# Publishes the openziti/quickstart Docker image and deploys the get.openziti.io
|
|
# CloudFront function for a given release tag.
|
|
#
|
|
# Triggers:
|
|
# - release: published (new release published -> build + push the versioned image, move :latest, deploy CloudFront)
|
|
# - release: edited (e.g. "Latest release" moved -> re-point :latest and CloudFront only; the image is NOT rebuilt)
|
|
# - workflow_dispatch (manual; required `release_tag` input)
|
|
#
|
|
# Both jobs are idempotent and independently re-runnable from the Actions UI.
|
|
on:
|
|
release:
|
|
types: [published, edited]
|
|
workflow_dispatch:
|
|
inputs:
|
|
release_tag:
|
|
description: 'Release tag to (re)publish, e.g. v1.6.7'
|
|
required: true
|
|
type: string
|
|
force_latest:
|
|
description: 'Force-move :latest to this tag even if GitHub does not mark this release as latest'
|
|
required: false
|
|
type: boolean
|
|
default: false
|
|
force_cloudfront:
|
|
description: 'Force-deploy the get.openziti.io CloudFront function even if GitHub does not mark this release as latest'
|
|
required: false
|
|
type: boolean
|
|
default: false
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
jobs:
|
|
resolve-tag:
|
|
name: Resolve Release Tag
|
|
# only run on the official upstream repo, or on a fork that overrides the
|
|
# quickstart image repo to a destination it can push to
|
|
if: github.repository_owner == 'openziti' || vars.ZITI_QUICKSTART_IMAGE != ''
|
|
runs-on: ubuntu-24.04
|
|
outputs:
|
|
tag: ${{ steps.resolve.outputs.tag }}
|
|
sha: ${{ steps.resolve.outputs.sha }}
|
|
is_latest: ${{ steps.resolve.outputs.is_latest }}
|
|
eligible: ${{ steps.resolve.outputs.eligible }}
|
|
steps:
|
|
- name: Resolve release tag and commit SHA
|
|
id: resolve
|
|
env:
|
|
DISPATCH_TAG: ${{ github.event.inputs.release_tag }}
|
|
RELEASE_TAG: ${{ github.event.release.tag_name }}
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
shell: bash
|
|
run: |
|
|
set -o errexit
|
|
set -o pipefail
|
|
|
|
if [[ -n "${DISPATCH_TAG:-}" ]]; then
|
|
TAG="${DISPATCH_TAG}"
|
|
elif [[ -n "${RELEASE_TAG:-}" ]]; then
|
|
TAG="${RELEASE_TAG}"
|
|
else
|
|
echo "ERROR: no release tag provided (neither dispatch input nor release event)" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if ! [[ "${TAG}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
|
echo "INFO: '${TAG}' is not a release semver (prerelease/draft/other edit); skipping image + CloudFront."
|
|
echo "eligible=false" | tee -a "$GITHUB_OUTPUT"
|
|
echo "is_latest=false" | tee -a "$GITHUB_OUTPUT"
|
|
exit 0
|
|
fi
|
|
echo "eligible=true" | tee -a "$GITHUB_OUTPUT"
|
|
|
|
# resolve the commit the tag points at; this is the snapshot we will
|
|
# check out for both jobs so the Dockerfile, routes.yml, and python
|
|
# deploy script all come from the released revision rather than main
|
|
SHA="$(gh api "repos/${GITHUB_REPOSITORY}/git/refs/tags/${TAG}" --jq '.object.sha')"
|
|
if [[ -z "${SHA}" || "${SHA}" == "null" ]]; then
|
|
echo "ERROR: failed to resolve commit SHA for tag '${TAG}'" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# if the tag is annotated, dereference one level to the commit object
|
|
OBJ_TYPE="$(gh api "repos/${GITHUB_REPOSITORY}/git/tags/${SHA}" --jq '.object.type' 2>/dev/null || echo "commit")"
|
|
if [[ "${OBJ_TYPE}" == "commit" ]]; then
|
|
SHA="$(gh api "repos/${GITHUB_REPOSITORY}/git/tags/${SHA}" --jq '.object.sha' 2>/dev/null || echo "${SHA}")"
|
|
fi
|
|
|
|
echo "tag=${TAG}" | tee -a "$GITHUB_OUTPUT"
|
|
echo "sha=${SHA}" | tee -a "$GITHUB_OUTPUT"
|
|
|
|
# Determine whether this tag is the repo's "Latest release" (the GitHub
|
|
# latest-release endpoint excludes prereleases and drafts). The
|
|
# CloudFront job gates on this so an older maintenance release does not
|
|
# re-point the shared get.openziti.io function.
|
|
LATEST_TAG="$(gh release view --repo "${GITHUB_REPOSITORY}" --json tagName --jq '.tagName' 2>/dev/null || true)"
|
|
if [[ "${TAG}" == "${LATEST_TAG}" ]]; then
|
|
echo "INFO: ${TAG} is GitHub's latest release."
|
|
echo "is_latest=true" | tee -a "$GITHUB_OUTPUT"
|
|
else
|
|
echo "INFO: GitHub's latest release is '${LATEST_TAG:-<unknown>}', not ${TAG}."
|
|
echo "is_latest=false" | tee -a "$GITHUB_OUTPUT"
|
|
fi
|
|
|
|
release-quickstart-image:
|
|
# NOTE: the job display name "Release Quickstart Job" is referenced by name
|
|
# in .github/workflows/promote-downstreams.yml's `ignore-checks`. Don't
|
|
# rename without updating that file too.
|
|
name: Release Quickstart Job
|
|
needs: resolve-tag
|
|
# eligible, AND (a manual dispatch, OR a publish, OR it's now the latest).
|
|
# A non-latest edit (e.g. a typo fix on an old release) skips the job entirely.
|
|
if: >-
|
|
needs.resolve-tag.outputs.eligible == 'true'
|
|
&& (github.event_name != 'release'
|
|
|| github.event.action == 'published'
|
|
|| needs.resolve-tag.outputs.is_latest == 'true')
|
|
runs-on: ubuntu-24.04
|
|
env:
|
|
ZITI_QUICKSTART_IMAGE: ${{ vars.ZITI_QUICKSTART_IMAGE || 'docker.io/openziti/quickstart' }}
|
|
steps:
|
|
- name: Debug action
|
|
uses: hmarr/debug-action@v3.0.0
|
|
|
|
# Dual checkout: workflow-src/ holds the orchestrator script (may not
|
|
# exist on older tags); release-src/ holds the Dockerfile + image
|
|
# assets at the release commit.
|
|
# ref: on release events github.ref is the tag (would defeat the
|
|
# dual-checkout), so force the default branch. On workflow_dispatch
|
|
# use github.ref so a human can dispatch from a feature branch to
|
|
# test orchestrator changes.
|
|
- name: Checkout Workflow Scripts (current branch)
|
|
uses: actions/checkout@v6
|
|
with:
|
|
ref: ${{ github.event_name == 'release' && github.event.repository.default_branch || github.ref }}
|
|
path: workflow-src
|
|
|
|
- name: Checkout Release Snapshot
|
|
uses: actions/checkout@v6
|
|
with:
|
|
ref: ${{ needs.resolve-tag.outputs.sha }}
|
|
path: release-src
|
|
|
|
- name: Set Up QEMU
|
|
# only needed to build the multi-arch image; an edit just re-points :latest
|
|
if: ${{ !(github.event_name == 'release' && github.event.action == 'edited') }}
|
|
uses: docker/setup-qemu-action@v4
|
|
with:
|
|
platforms: amd64,arm64
|
|
|
|
- name: Set Up Docker BuildKit
|
|
uses: docker/setup-buildx-action@v4
|
|
|
|
- name: Login to Docker Hub
|
|
uses: docker/login-action@v4
|
|
with:
|
|
username: ${{ vars.DOCKER_HUB_API_USER || secrets.DOCKER_HUB_API_USER }}
|
|
password: ${{ secrets.DOCKER_HUB_API_TOKEN }}
|
|
|
|
# By default the script moves :latest only if this tag is GitHub's
|
|
# "Latest release". The workflow_dispatch input `force_latest`
|
|
# translates to FORCE_LATEST_FLAG below, which conditionally passes
|
|
# --force-latest to bypass that check.
|
|
- name: Build & Push Multi-Platform Quickstart Image
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
GITHUB_REPOSITORY: ${{ github.repository }}
|
|
shell: bash
|
|
run: |
|
|
FORCE_LATEST_FLAG=""
|
|
if [[ "${{ github.event.inputs.force_latest }}" == "true" ]]; then
|
|
FORCE_LATEST_FLAG="--force-latest"
|
|
fi
|
|
SKIP_BUILD_FLAG=""
|
|
if [[ "${{ github.event_name }}" == "release" && "${{ github.event.action }}" == "edited" ]]; then
|
|
# an edit (e.g. moving "Latest release") must not rebuild the image;
|
|
# only re-point :latest to the now-latest tag
|
|
SKIP_BUILD_FLAG="--skip-build"
|
|
fi
|
|
bash workflow-src/dist/scripts/release-quickstart-image.sh \
|
|
--tag "${{ needs.resolve-tag.outputs.tag }}" \
|
|
--image-repo "${ZITI_QUICKSTART_IMAGE}" \
|
|
--context-dir release-src/quickstart/docker/image \
|
|
${FORCE_LATEST_FLAG} \
|
|
${SKIP_BUILD_FLAG}
|
|
|
|
release-quickstart-cloudfront:
|
|
name: Deploy get.openziti.io CloudFront Function
|
|
needs: resolve-tag
|
|
# Gated on two conditions:
|
|
# 1. Official upstream repo only -- forks could push to their own DockerHub
|
|
# repo but cannot deploy to someone else's CloudFront distribution.
|
|
# 2. This tag is GitHub's "Latest release" (or force_cloudfront was set on a
|
|
# manual dispatch). get.openziti.io is a single shared function; only the
|
|
# newest release should re-point it. Older maintenance releases skip.
|
|
if: >-
|
|
github.repository_owner == 'openziti'
|
|
&& needs.resolve-tag.outputs.eligible == 'true'
|
|
&& (needs.resolve-tag.outputs.is_latest == 'true' || github.event.inputs.force_cloudfront == 'true')
|
|
runs-on: ubuntu-24.04
|
|
steps:
|
|
# Single checkout at the release tag: python, routes.yml, and the
|
|
# Jinja template all come from the release snapshot.
|
|
- name: Checkout Workspace at Release Commit
|
|
uses: actions/checkout@v6
|
|
with:
|
|
ref: ${{ needs.resolve-tag.outputs.sha }}
|
|
|
|
- name: Configure Python
|
|
shell: bash
|
|
run: |
|
|
pip install --requirement ./dist/cloudfront/get.openziti.io/requirements.txt
|
|
python --version
|
|
|
|
# GITHUB_SHA must be the resolved tag's commit, not github.sha
|
|
# (which on workflow_dispatch is the default branch HEAD). The
|
|
# python embeds it into routes.yml's templated raw-GitHub URLs.
|
|
- name: Deploy CloudFront Function
|
|
shell: bash
|
|
env:
|
|
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
|
AWS_REGION: ${{ vars.AWS_REGION || secrets.AWS_REGION }}
|
|
GITHUB_SHA: ${{ needs.resolve-tag.outputs.sha }}
|
|
run: python ./dist/cloudfront/get.openziti.io/deploy-cloudfront-function.py
|