Skip to content

JWKS rotation, identifier gates, and authserver 0.2.0 alignment (#28) #20

JWKS rotation, identifier gates, and authserver 0.2.0 alignment (#28)

JWKS rotation, identifier gates, and authserver 0.2.0 alignment (#28) #20

name: Release tooling
# Catches workflow YAML / shell-in-`run:` regressions at PR time so a
# typo can't reach a release tag and surface only when a publish run
# fails. The shell scripts under scripts/ are in the same category — a
# break in them surfaces only when someone reaches for them after a
# release, which is the worst moment to discover it — so they are linted
# and tested here too. Scoped to a few paths to keep CI overhead off
# unrelated PRs.
#
# `.github/scripts/**` is in scope on the same argument. Those scripts stopped
# being thin fetch wrappers once the conformance case-body drift check landed
# there, and they run only on a weekly schedule, so a break in them surfaces
# late and quietly. The trigger stays narrow: it matches PRs touching those
# scripts, not every PR touching `.github/**`.
#
# Linting alone would not have been enough for them, so they carry their own
# tests here as well, next to backport-fixes.test.sh.
#
# Every scripts trigger is `<dir>/**`, not `<dir>/*.sh`: a single-level
# glob would leave a future scripts/lib/*.sh both untriggered here and
# unlinted below, in each case silently.
on:
pull_request:
paths:
- ".github/workflows/**"
- ".github/scripts/**"
- "scripts/**"
push:
branches:
- main
paths:
- ".github/workflows/**"
- ".github/scripts/**"
- "scripts/**"
permissions:
contents: read
jobs:
actionlint:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
# Pulls the matching actionlint binary release from GitHub Releases
# via the upstream download script. The script is fetched by commit
# SHA (not a mutable tag) and sha256-verified before it runs — this
# closes Scorecard's "downloadThenRun not pinned by hash" gap. The
# script then checksum-verifies the actionlint binary it pulls from
# the matching release.
#
# To bump: change ACTIONLINT_VERSION, set ACTIONLINT_SCRIPT_SHA to the
# commit the new tag points at (`gh api repos/rhysd/actionlint/commits/vX.Y.Z -q .sha`),
# and update ACTIONLINT_SCRIPT_SHA256 to that file's sha256.
#
# Install dir is passed explicitly as the script's second positional
# arg so the workflow doesn't couple to the script's internal default
# of $PWD (which happens to be $GITHUB_WORKSPACE after checkout —
# a coincidence, not a contract).
- name: Install actionlint
env:
ACTIONLINT_VERSION: "1.7.7"
ACTIONLINT_SCRIPT_SHA: "03d0035246f3e81f36aed592ffb4bebf33a03106"
ACTIONLINT_SCRIPT_SHA256: "221d1d16c03e4e4fcd867de34104e8d479bdce20ccdfa553b9a5c0dc29bf6af2"
ACTIONLINT_INSTALL_DIR: ${{ runner.temp }}/actionlint
run: |
mkdir -p "${ACTIONLINT_INSTALL_DIR}"
script="${ACTIONLINT_INSTALL_DIR}/download-actionlint.bash"
curl -fsSL -o "${script}" \
"https://raw.githubusercontent.com/rhysd/actionlint/${ACTIONLINT_SCRIPT_SHA}/scripts/download-actionlint.bash"
echo "${ACTIONLINT_SCRIPT_SHA256} ${script}" | sha256sum -c -
bash "${script}" "${ACTIONLINT_VERSION}" "${ACTIONLINT_INSTALL_DIR}"
echo "${ACTIONLINT_INSTALL_DIR}" >> "${GITHUB_PATH}"
"${ACTIONLINT_INSTALL_DIR}/actionlint" -version
# Both steps below resolve `shellcheck` off the runner image's $PATH —
# actionlint via `-shellcheck=shellcheck`, the script lint directly.
# Asserting it once, up front, is what makes that dependency explicit:
# naming the binary in actionlint's flag only changes which lookup
# fails, and neither step announces the version it linted with. If the
# Ubuntu image ever drops shellcheck, this fails first and says so,
# rather than actionlint quietly degrading to no shell analysis.
- name: Check shellcheck is available
run: shellcheck --version
- name: Run actionlint
run: actionlint -color -shellcheck=shellcheck
- name: Shellcheck the release scripts
# find, not `scripts/*.sh`: the single-level glob would silently skip
# a future scripts/lib/*.sh, the same blind spot the path trigger had.
# An empty result is an error rather than a green no-op, so a moved or
# renamed directory cannot pass as a clean lint.
run: |
mapfile -d '' -t sh_files < <(find scripts -type f -name '*.sh' -print0)
if [[ ${#sh_files[@]} -eq 0 ]]; then
echo "error: no shell scripts found under scripts/" >&2
exit 1
fi
printf 'shellcheck: %s\n' "${sh_files[@]}"
shellcheck "${sh_files[@]}"
- name: Shellcheck the workflow support scripts
# A second step rather than a second root on the find above, so an
# empty result names the directory that went missing. Merged, a renamed
# .github/scripts/ would still be covered by whatever scripts/ returned.
run: |
mapfile -d '' -t sh_files < <(find .github/scripts -type f -name '*.sh' -print0)
if [[ ${#sh_files[@]} -eq 0 ]]; then
echo "error: no shell scripts found under .github/scripts/" >&2
exit 1
fi
printf 'shellcheck: %s\n' "${sh_files[@]}"
shellcheck "${sh_files[@]}"
# backport-fixes.sh accepts a branch or a tag as --from, and only the
# branch form has a remote-tracking ref. The tag form is what the release
# flow tells you to use once release.yml has deleted the branch, so it is
# the form least likely to be exercised before it is needed.
- name: Test backport-fixes.sh
run: scripts/backport-fixes.test.sh
# Shellcheck above is the only other gate on the conformance drift
# scripts, and it cannot see the way they break: a loosened id regex that
# drops cases, or a `diff` whose exit code stops being read, is valid
# shell. The result is a check that reports green while guarding nothing,
# on a weekly schedule where nobody is watching — and it would stay
# unnoticed until a real re-tightening slipped through, which is the
# failure that check exists to prevent. These controls pin the detection
# itself. They need neither the catalog clone nor the SDK's dependencies,
# so they run here.
- name: Test conformance-case-body-drift.sh
run: .github/scripts/conformance-case-body-drift.test.sh