ci(workflows): pin flux, yq and helm-unittest installs

Every flux install fetched fluxcd.io/install.sh and ran it as root, so
whatever that host served at the moment executed on runners that later
hold registry credentials. Use the upstream setup action instead, pinned
by commit and given an explicit version: it checks the release tarball
against that release's checksums file and runs no script fetched at run
time.

nightly and the fork e2e lane still took yq from the floating latest
release. They now use the pinned, checksum-verified install the
promotion steps already have.

The helm-unittest plugin embeds its own Helm and was installed at
whatever version was newest, so a plugin release could change chart
test results on a PR that touched no chart. Pin it, and replace a
different version a persistent runner may carry.

A contract test scans every workflow for these three tools, so an
unpinned flux, yq or helm-unittest install copied from an older step
fails the unit lane.

Assisted-by: LLM
Signed-off-by: Aleksei Sviridkin <f@lex.la>
This commit is contained in:
Aleksei Sviridkin committed 2026-10-02 11:02:18 +03:00
1 parent 360f03bb8b
commit 6df5d076b1
12 files changed
+205 -159

No files matched your search

+5 -11
View File
@@ -61,17 +61,11 @@ jobs:
persist-credentials: false
# Ephemeral runners lack the flux CLI the installer's image-packages step
# shells out to; install the pinned version when it is not already present.
- name: Set up build toolchain
env:
FLUX_VERSION: "2.8.6"
run: |
if ! flux version --client 2>/dev/null | grep -Fqx "flux: v${FLUX_VERSION}"; then
install_script="$(mktemp)"
curl -fsSL https://fluxcd.io/install.sh -o "$install_script"
sudo env "FLUX_VERSION=$FLUX_VERSION" bash "$install_script"
rm -f "$install_script"
fi
# shells out to.
- name: Set up flux
uses: fluxcd/flux2/action@04acaec6161ac4fb1a82ffafa88901c03271d34f # v2.8.6
with:
version: "2.8.6"
- name: Set up Docker config
run: |
+5 -11
View File
@@ -107,17 +107,11 @@ jobs:
persist-credentials: false
# Ephemeral runners lack the flux CLI the installer's image-packages step
# shells out to; install the pinned version when it is not already present.
- name: Set up build toolchain
env:
FLUX_VERSION: "2.8.6"
run: |
if ! flux version --client 2>/dev/null | grep -Fqx "flux: v${FLUX_VERSION}"; then
install_script="$(mktemp)"
curl -fsSL https://fluxcd.io/install.sh -o "$install_script"
sudo env "FLUX_VERSION=$FLUX_VERSION" bash "$install_script"
rm -f "$install_script"
fi
# shells out to.
- name: Set up flux
uses: fluxcd/flux2/action@04acaec6161ac4fb1a82ffafa88901c03271d34f # v2.8.6
with:
version: "2.8.6"
- name: Set up Docker config
run: |
+16 -15
View File
@@ -349,22 +349,13 @@ jobs:
outputs:
packages_digest: ${{ steps.push.outputs.packages_digest }}
steps:
- name: Set up toolchain (skopeo, flux)
env:
# Pin the flux CLI instead of install.sh's rolling latest. This is the
# one privileged job that runs a remote installer as root right before
# the OCIR token is loaded, where a pinned installer matters most;
# keep it in step with the flux distro cozystack ships
# (internal/fluxinstall/manifests/fluxcd.yaml, v2.8.0).
FLUX_VERSION: "2.8.6"
- name: Set up flux
uses: fluxcd/flux2/action@04acaec6161ac4fb1a82ffafa88901c03271d34f # v2.8.6
with:
version: "2.8.6"
- name: Set up toolchain (skopeo)
run: |
command -v skopeo >/dev/null || { sudo apt-get update -qq && sudo apt-get install -y -qq skopeo; }
if ! flux version --client 2>/dev/null | grep -Fqx "flux: v${FLUX_VERSION}"; then
s="$(mktemp)"
curl -fsSL https://fluxcd.io/install.sh -o "$s"
sudo env "FLUX_VERSION=$FLUX_VERSION" bash "$s"
rm -f "$s"
fi
# The base repo's own merge ref — safe to fetch, not fork-controlled code
# execution. pr.patch is applied as DATA below; nothing from the tree runs.
- name: Checkout PR merge ref
@@ -581,8 +572,18 @@ jobs:
got="$(git rev-parse HEAD^2)"
[ "$got" = "$EXPECTED_SHA" ] || { echo "::error::merge ref moved: HEAD^2=$got != resolved $EXPECTED_SHA"; exit 1; }
- name: Set up toolchain (yq)
env:
YQ_VERSION: "4.53.3"
YQ_SHA256: "fa52a4e758c63d38299163fbdd1edfb4c4963247918bf9c1c5d31d84789eded4"
run: |
command -v yq >/dev/null || { sudo curl -sSL -o /usr/local/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 && sudo chmod +x /usr/local/bin/yq; }
if ! yq --version 2>/dev/null | grep -q "mikefarah.* version v${YQ_VERSION}\$"; then
yq_bin="$(mktemp)"
curl -fsSL -o "$yq_bin" \
"https://github.com/mikefarah/yq/releases/download/v${YQ_VERSION}/yq_linux_amd64"
printf '%s %s\n' "$YQ_SHA256" "$yq_bin" | sha256sum -c -
sudo install -m 0755 "$yq_bin" /usr/local/bin/yq
rm -f "$yq_bin"
fi
- name: Download pr.patch
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
+19 -17
View File
@@ -52,18 +52,24 @@ jobs:
packages_ref: ${{ steps.resolve.outputs.packages_ref }}
packages_digest: ${{ steps.publish.outputs.packages_digest }}
steps:
- name: Set up toolchain (skopeo, flux, yq, helm)
- name: Set up flux
uses: fluxcd/flux2/action@04acaec6161ac4fb1a82ffafa88901c03271d34f # v2.8.6
with:
version: "2.8.6"
- name: Set up toolchain (skopeo, yq, helm)
env:
FLUX_VERSION: "2.8.6"
YQ_VERSION: "4.53.3"
YQ_SHA256: "fa52a4e758c63d38299163fbdd1edfb4c4963247918bf9c1c5d31d84789eded4"
run: |
command -v skopeo >/dev/null || { sudo apt-get update -qq && sudo apt-get install -y -qq skopeo; }
if ! flux version --client 2>/dev/null | grep -Fqx "flux: v${FLUX_VERSION}"; then
install_script="$(mktemp)"
curl -fsSL https://fluxcd.io/install.sh -o "$install_script"
sudo env "FLUX_VERSION=$FLUX_VERSION" bash "$install_script"
rm -f "$install_script"
if ! yq --version 2>/dev/null | grep -q "mikefarah.* version v${YQ_VERSION}\$"; then
yq_bin="$(mktemp)"
curl -fsSL -o "$yq_bin" \
"https://github.com/mikefarah/yq/releases/download/v${YQ_VERSION}/yq_linux_amd64"
printf '%s %s\n' "$YQ_SHA256" "$yq_bin" | sha256sum -c -
sudo install -m 0755 "$yq_bin" /usr/local/bin/yq
rm -f "$yq_bin"
fi
command -v yq >/dev/null || { sudo curl -sSL -o /usr/local/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 && sudo chmod +x /usr/local/bin/yq; }
command -v helm >/dev/null || curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
- name: Login to registries
@@ -250,16 +256,12 @@ jobs:
ref: ${{ needs.mirror.outputs.sha }}
fetch-depth: 1
persist-credentials: false
- name: Set up toolchain (flux, oras)
env:
FLUX_VERSION: "2.8.6"
- name: Set up flux
uses: fluxcd/flux2/action@04acaec6161ac4fb1a82ffafa88901c03271d34f # v2.8.6
with:
version: "2.8.6"
- name: Set up toolchain (oras)
run: |
if ! flux version --client 2>/dev/null | grep -Fqx "flux: v${FLUX_VERSION}"; then
install_script="$(mktemp)"
curl -fsSL https://fluxcd.io/install.sh -o "$install_script"
sudo env "FLUX_VERSION=$FLUX_VERSION" bash "$install_script"
rm -f "$install_script"
fi
command -v oras >/dev/null || { curl -fsSL https://github.com/oras-project/oras/releases/download/v1.2.0/oras_1.2.0_linux_amd64.tar.gz | sudo tar -xz -C /usr/local/bin oras; }
- name: Login to GHCR
env:
+10 -17
View File
@@ -400,7 +400,7 @@ jobs:
# Promotion publishes one non-consumer-visible packages candidate. Stable
# image/chart tags are still deferred to post-merge finalize.
#
# Both tools are version-pinned. This step arms the only registry write
# Both tools are version-pinned. These steps arm the only registry write
# the dispatch performs, so what performs it has to be a fixed input:
# `latest/download` resolves to whatever mikefarah shipped this morning,
# and a yq that changed how it emits or compares a value would rewrite
@@ -418,26 +418,19 @@ jobs:
# chosen (against this release's own published checksums list) and makes
# any later substitution loud. Bump the version and this together.
#
# The Flux installer is deliberately NOT pinned by content. It verifies
# the flux binary it fetches against flux_${FLUX_VERSION}_checksums.txt
# from the pinned release, so the binary is not the exposure — the ~6 KB
# bootstrap script from fluxcd.io is, and it is mutable by design and
# fetched the same way in ten other workflows here. Pinning its bytes in
# this file alone would break on the next regeneration of a script the
# project does not control, while leaving the identical fetch unpinned
# everywhere else. Named rather than half-fixed.
- name: Set up toolchain (flux, yq)
# flux comes from the upstream setup action, pinned by commit and given
# an explicit version. It checks the release tarball against that
# release's checksums file and runs no script fetched at run time, the
# way fluxcd.io/install.sh did as root.
- name: Set up flux
uses: fluxcd/flux2/action@04acaec6161ac4fb1a82ffafa88901c03271d34f # v2.8.6
with:
version: "2.8.6"
- name: Set up toolchain (yq)
env:
FLUX_VERSION: "2.8.6"
YQ_VERSION: "4.53.3"
YQ_SHA256: "fa52a4e758c63d38299163fbdd1edfb4c4963247918bf9c1c5d31d84789eded4"
run: |
if ! flux version --client 2>/dev/null | grep -qx "flux: v${FLUX_VERSION}"; then
install_script="$(mktemp)"
curl -fsSL https://fluxcd.io/install.sh -o "$install_script"
sudo env "FLUX_VERSION=$FLUX_VERSION" bash "$install_script"
rm -f "$install_script"
fi
if ! yq --version 2>/dev/null | grep -q "mikefarah.* version v${YQ_VERSION}\$"; then
yq_bin="$(mktemp)"
curl -fsSL -o "$yq_bin" \
+9 -11
View File
@@ -115,22 +115,20 @@ jobs:
# this morning. The version tracks
# packages/core/testing/images/e2e-sandbox/Dockerfile. yq is additionally
# pinned by content — it stamps the chart's platformVersion, and this step
# is the last one before write-once names exist. The Flux installer is not,
# because it checksums the binary it fetches and the mutable part is its
# own bootstrap script. promote-rc.yaml's copy carries the full reasoning.
- name: Set up promotion toolchain (flux, skopeo, yq, helm)
# is the last one before write-once names exist. flux comes from the
# commit-pinned upstream setup action, which checksums the release
# tarball. promote-rc.yaml's copy carries the full reasoning.
- name: Set up flux
if: ${{ !contains(steps.get_tag.outputs.tag, '-') }}
uses: fluxcd/flux2/action@04acaec6161ac4fb1a82ffafa88901c03271d34f # v2.8.6
with:
version: "2.8.6"
- name: Set up promotion toolchain (skopeo, yq, helm)
if: ${{ !contains(steps.get_tag.outputs.tag, '-') }}
env:
FLUX_VERSION: "2.8.6"
YQ_VERSION: "4.53.3"
YQ_SHA256: "fa52a4e758c63d38299163fbdd1edfb4c4963247918bf9c1c5d31d84789eded4"
run: |
if ! flux version --client 2>/dev/null | grep -qx "flux: v${FLUX_VERSION}"; then
install_script="$(mktemp)"
curl -fsSL https://fluxcd.io/install.sh -o "$install_script"
sudo env "FLUX_VERSION=$FLUX_VERSION" bash "$install_script"
rm -f "$install_script"
fi
if ! yq --version 2>/dev/null | grep -q "mikefarah.* version v${YQ_VERSION}\$"; then
yq_bin="$(mktemp)"
curl -fsSL -o "$yq_bin" \
+26 -36
View File
@@ -249,22 +249,18 @@ jobs:
# resolves to whatever mikefarah shipped this morning, and a yq that changed
# how it emits or compares a value turns a release gate into a moving one.
# The version tracks packages/core/testing/images/e2e-sandbox/Dockerfile.
# yq is additionally pinned by content; the Flux installer is not, because
# it checksums the binary it fetches and the mutable part is its own
# bootstrap script. promote-rc.yaml's copy of this step carries the full
# reasoning for both.
- name: Set up promotion toolchain (flux, yq)
# yq is additionally pinned by content; flux comes from the commit-pinned
# upstream setup action, which checksums the release tarball.
# promote-rc.yaml's copy of this step carries the full reasoning for both.
- name: Set up flux
uses: fluxcd/flux2/action@04acaec6161ac4fb1a82ffafa88901c03271d34f # v2.8.6
with:
version: "2.8.6"
- name: Set up promotion toolchain (yq)
env:
FLUX_VERSION: "2.8.6"
YQ_VERSION: "4.53.3"
YQ_SHA256: "fa52a4e758c63d38299163fbdd1edfb4c4963247918bf9c1c5d31d84789eded4"
run: |
if ! flux version --client 2>/dev/null | grep -qx "flux: v${FLUX_VERSION}"; then
install_script="$(mktemp)"
curl -fsSL https://fluxcd.io/install.sh -o "$install_script"
sudo env "FLUX_VERSION=$FLUX_VERSION" bash "$install_script"
rm -f "$install_script"
fi
if ! yq --version 2>/dev/null | grep -q "mikefarah.* version v${YQ_VERSION}\$"; then
yq_bin="$(mktemp)"
curl -fsSL -o "$yq_bin" \
@@ -313,11 +309,17 @@ jobs:
with:
fetch-depth: 0
# Ephemeral runners don't carry the helm-unittest plugin the persistent
# self-hosted runner accreted. Install if absent (idempotent).
# self-hosted runner accreted. The plugin embeds its own Helm, so its
# version is pinned: a plugin release must not change test results.
- name: Set up test toolchain
env:
HELM_UNITTEST_VERSION: "1.1.2"
run: |
helm plugin list 2>/dev/null | grep -q unittest \
|| helm plugin install https://github.com/helm-unittest/helm-unittest
installed="$(helm plugin list 2>/dev/null | awk '$1 == "unittest" { print $2 }')"
if [ "$installed" != "$HELM_UNITTEST_VERSION" ]; then
[ -z "$installed" ] || helm plugin uninstall unittest
helm plugin install https://github.com/helm-unittest/helm-unittest --version "v${HELM_UNITTEST_VERSION}"
fi
# The make processes share one four-slot jobserver. Each BATS file is a
# separate target, so the long tail no longer runs as one nine-minute shell
# loop; Helm and Go targets share the same bounded pool. Output stays
@@ -359,17 +361,11 @@ jobs:
fetch-depth: 0
fetch-tags: true
# installer is built in finalize, but some package image targets shell out
# to flux; install the pinned version when it is not already present.
- name: Set up build toolchain
env:
FLUX_VERSION: "2.8.6"
run: |
if ! flux version --client 2>/dev/null | grep -Fqx "flux: v${FLUX_VERSION}"; then
install_script="$(mktemp)"
curl -fsSL https://fluxcd.io/install.sh -o "$install_script"
sudo env "FLUX_VERSION=$FLUX_VERSION" bash "$install_script"
rm -f "$install_script"
fi
# to flux.
- name: Set up flux
uses: fluxcd/flux2/action@04acaec6161ac4fb1a82ffafa88901c03271d34f # v2.8.6
with:
version: "2.8.6"
- name: Set up Docker config
run: |
if [ -d ~/.docker ]; then
@@ -587,16 +583,10 @@ jobs:
with:
fetch-depth: 0
fetch-tags: true
- name: Set up build toolchain
env:
FLUX_VERSION: "2.8.6"
run: |
if ! flux version --client 2>/dev/null | grep -Fqx "flux: v${FLUX_VERSION}"; then
install_script="$(mktemp)"
curl -fsSL https://fluxcd.io/install.sh -o "$install_script"
sudo env "FLUX_VERSION=$FLUX_VERSION" bash "$install_script"
rm -f "$install_script"
fi
- name: Set up flux
uses: fluxcd/flux2/action@04acaec6161ac4fb1a82ffafa88901c03271d34f # v2.8.6
with:
version: "2.8.6"
- name: Download patch fragments
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
+10 -19
View File
@@ -256,18 +256,12 @@ jobs:
persist-credentials: false
# Ephemeral runners lack the flux CLI the installer's image-packages step
# shells out to; install the pinned version when it is not already present.
- name: Set up build toolchain
# shells out to.
- name: Set up flux
if: steps.check_release.outputs.release_exists == 'false'
env:
FLUX_VERSION: "2.8.6"
run: |
if ! flux version --client 2>/dev/null | grep -Fqx "flux: v${FLUX_VERSION}"; then
install_script="$(mktemp)"
curl -fsSL https://fluxcd.io/install.sh -o "$install_script"
sudo env "FLUX_VERSION=$FLUX_VERSION" bash "$install_script"
rm -f "$install_script"
fi
uses: fluxcd/flux2/action@04acaec6161ac4fb1a82ffafa88901c03271d34f # v2.8.6
with:
version: "2.8.6"
- name: Login to GHCR
if: steps.check_release.outputs.release_exists == 'false'
@@ -421,17 +415,14 @@ jobs:
# The stitch and the multi-arch gate read the registry with skopeo and
# the tree with mikefarah yq; the republish needs flux and helm.
- name: Set up flux
if: needs.build-amd64.outputs.release_exists == 'false'
uses: fluxcd/flux2/action@04acaec6161ac4fb1a82ffafa88901c03271d34f # v2.8.6
with:
version: "2.8.6"
- name: Set up build toolchain
if: needs.build-amd64.outputs.release_exists == 'false'
env:
FLUX_VERSION: "2.8.6"
run: |
if ! flux version --client 2>/dev/null | grep -Fqx "flux: v${FLUX_VERSION}"; then
install_script="$(mktemp)"
curl -fsSL https://fluxcd.io/install.sh -o "$install_script"
sudo env "FLUX_VERSION=$FLUX_VERSION" bash "$install_script"
rm -f "$install_script"
fi
command -v skopeo >/dev/null \
|| { sudo apt-get update -qq && sudo apt-get install -y -qq skopeo; }
command -V skopeo jq yq helm
+87
View File
@@ -0,0 +1,87 @@
#!/usr/bin/env bats
# The flux CLI, yq and the helm-unittest plugin are build inputs like any image
# or action, so they are pinned the same way: a fixed version, and for flux and
# yq bytes checked against the release that version names. A floating install
# comes back by copy-paste from an older step rather than by anyone deciding to
# unpin one, which is why this scans every workflow instead of the steps that
# happened to be fixed. Other downloaded tools are not covered here.
REPO_ROOT="$(cd "$(dirname "${BATS_TEST_FILENAME:-$0}")/.." && pwd)"
WORKFLOWS="$REPO_ROOT/.github/workflows"
# Prints file:line:text for every non-comment workflow line matching the ERE.
workflow_code() {
grep --recursive --line-number --extended-regexp "$1" "$WORKFLOWS" \
| grep --invert-match --extended-regexp '^[^:]+:[0-9]+:[[:space:]]*#' || true
}
@test "no workflow executes the fluxcd.io install script" {
# It runs as root whatever fluxcd.io serves at that moment; the binary it
# checksums is not the exposure, the unpinned script is.
hits="$(workflow_code 'fluxcd\.io/install\.sh')"
[ -z "$hits" ] || { printf '%s\n' "$hits"; false; }
}
@test "every use of the flux setup action is SHA-pinned at one version" {
uses="$(workflow_code 'uses: fluxcd/flux2/action@')"
[ -n "$uses" ]
bad="$(printf '%s\n' "$uses" | grep --invert-match --extended-regexp 'action@[0-9a-f]{40} # v[0-9.]+$' || true)"
[ -z "$bad" ] || { printf '%s\n' "$bad"; false; }
# Without a version input the action resolves the latest release itself, and
# a digest bump moves the ref and its comment but not the input, so each
# step's input must sit in its own `with:` and match the tag in the comment.
# One "sha tag input" triple per step; all steps must agree on it.
steps="$(awk '
FNR == 1 { if (pend) print sha, tag, "missing"; pend = 0 }
/^[[:space:]]*#/ { next }
/uses: fluxcd\/flux2\/action@/ {
sha = $0; sub(/.*action@/, "", sha); sub(/ .*/, "", sha)
tag = $0; sub(/.*# v/, "", tag)
pend = 1; next
}
pend && /version: "/ {
v = $0; sub(/.*version: "/, "", v); sub(/".*/, "", v)
print sha, tag, v; pend = 0; next
}
pend && /^[[:space:]]*- / { print sha, tag, "missing"; pend = 0 }
END { if (pend) print sha, tag, "missing" }
' "$WORKFLOWS"/*.yaml "$WORKFLOWS"/*.yml 2>/dev/null)"
[ "$(printf '%s\n' "$steps" | grep --count .)" -eq "$(printf '%s\n' "$uses" | grep --count .)" ]
bad="$(printf '%s\n' "$steps" | awk '$2 != $3' || true)"
[ -z "$bad" ] || { printf '%s\n' "$bad"; false; }
[ "$(printf '%s\n' "$steps" | sort --unique | wc -l | tr -d ' ')" -eq 1 ] \
|| { printf '%s\n' "$steps" | sort --unique; false; }
}
@test "no workflow downloads from a floating latest release" {
hits="$(workflow_code 'releases/latest/download')"
[ -z "$hits" ] || { printf '%s\n' "$hits"; false; }
}
@test "every yq download is checksum-verified at one version" {
[ -n "$(workflow_code 'mikefarah/yq/releases/download')" ]
# Each download, not each file: the check has to follow within three lines.
unchecked="$(awk '
FNR == 1 { if (pend) print pend; pend = "" }
/^[[:space:]]*#/ { next }
/mikefarah\/yq\/releases\/download/ { if (pend) print pend; pend = FILENAME ":" FNR; n = 0; next }
pend && /sha256sum -c -/ && /YQ_SHA256/ { pend = ""; next }
pend && ++n >= 3 { print pend; pend = "" }
END { if (pend) print pend }
' "$WORKFLOWS"/*.yaml "$WORKFLOWS"/*.yml 2>/dev/null)"
[ -z "$unchecked" ] || { printf '%s\n' "$unchecked"; false; }
for key in YQ_VERSION YQ_SHA256; do
values="$(workflow_code "$key: \"" | grep --only-matching "$key: \"[0-9a-f.]*\"" | sort --unique)"
[ "$(printf '%s\n' "$values" | grep --count .)" -eq 1 ] || { printf '%s\n' "$values"; false; }
done
}
@test "every helm plugin install names a version" {
# helm-unittest embeds its own Helm, so an unpinned plugin moves the renderer
# every chart test runs against without any PR asking for it.
installs="$(workflow_code 'helm plugin install')"
[ -n "$installs" ]
bad="$(printf '%s\n' "$installs" | grep --invert-match -- '--version' || true)"
[ -z "$bad" ] || { printf '%s\n' "$bad"; false; }
}
+10 -13
View File
@@ -63,8 +63,7 @@ step_line() {
prepare="$(step_block 'Prepare stable branch' "$PROMOTE")"
printf '%s\n' "$job" | code_lines | grep -qF ' packages: write'
printf '%s\n' "$job" | code_lines | grep -qF 'FLUX_VERSION: "2.8.6"'
printf '%s\n' "$job" | code_lines | grep -qF 'flux version --client'
printf '%s\n' "$job" | code_lines | grep -qF 'uses: fluxcd/flux2/action@'
printf '%s\n' "$login" | code_lines | grep -qF 'DOCKER_CONFIG: ${{ runner.temp }}/.docker'
printf '%s\n' "$prepare" | code_lines | grep -qF 'DOCKER_CONFIG: ${{ runner.temp }}/.docker'
}
@@ -73,16 +72,15 @@ step_line() {
# A release gate whose tooling comes from `releases/latest/download` is not a
# fixed input: the bytes that decide whether vX.Y.Z may be created, and the
# yq that rewrites the installer pin, change whenever upstream publishes.
# Pinned by version, and pinned to STAY pinned — the unpinned one-liner is
# still the idiom in the workflows outside this pipeline, so it comes back by
# copy-paste rather than by anyone deciding to unpin a release step.
# Pinned by version, and pinned to STAY pinned — an unpinned one-liner comes
# back by copy-paste rather than by anyone deciding to unpin a release step.
# flux is pinned by hack/ci-tool-installs-contract.bats across every workflow.
for step_and_file in \
"Set up toolchain (flux, yq)|$PROMOTE" \
"Set up promotion toolchain (flux, yq)|$PULL_REQUESTS" \
"Set up promotion toolchain (flux, skopeo, yq, helm)|$FINALIZE"; do
"Set up toolchain (yq)|$PROMOTE" \
"Set up promotion toolchain (yq)|$PULL_REQUESTS" \
"Set up promotion toolchain (skopeo, yq, helm)|$FINALIZE"; do
block="$(step_block "${step_and_file%%|*}" "${step_and_file#*|}")"
[ -n "$block" ]
printf '%s\n' "$block" | code_lines | grep -qF 'FLUX_VERSION: "'
printf '%s\n' "$block" | code_lines | grep -qF 'YQ_VERSION: "'
count="$(printf '%s\n' "$block" | code_lines | grep -c 'releases/latest/download' || true)"
[ "${count:-0}" -eq 0 ]
@@ -116,9 +114,8 @@ step_line() {
block="$(step_block 'Verify stable packages candidate' "$FINALIZE")"
printf '%s\n' "$block" | code_lines | grep -qF '.release-tooling/hack/verify-promoted-packages.sh "${TAG#v}"'
setup="$(step_block 'Set up promotion toolchain (flux, skopeo, yq, helm)' "$FINALIZE")"
printf '%s\n' "$setup" | code_lines | grep -qF 'FLUX_VERSION: "2.8.6"'
printf '%s\n' "$setup" | code_lines | grep -qF 'flux version --client'
setup="$(step_block 'Set up flux' "$FINALIZE")"
printf '%s\n' "$setup" | code_lines | grep -qF 'uses: fluxcd/flux2/action@'
}
@test "promote rejects an old target base before publishing a candidate" {
@@ -153,7 +150,7 @@ step_line() {
printf '%s\n' "$job" | code_lines | grep -qF "github.event.pull_request.user.login == 'cozystack-ci[bot]'"
printf '%s\n' "$job" | code_lines | grep -qF 'ref: ${{ github.event.pull_request.base.sha }}'
printf '%s\n' "$job" | code_lines | grep -qF '.release-tooling/hack/verify-promoted-packages.sh "$STABLE_VERSION"'
printf '%s\n' "$job" | code_lines | grep -qF 'FLUX_VERSION: "2.8.6"'
printf '%s\n' "$job" | code_lines | grep -qF 'uses: fluxcd/flux2/action@'
report="$(job_block e2e-report "$PULL_REQUESTS")"
printf '%s\n' "$report" | code_lines | grep -qF '"verify-release-candidate"'
+2 -3
View File
@@ -375,10 +375,9 @@ _make_world() {
echo "$tool" | grep -q 'yq --version | grep -q mikefarah'
# prepare-release pushes the packages artifact with flux, so it installs the
# same flux build-amd64 does rather than whatever is latest.
echo "$tool" | grep -qF 'grep -Fqx "flux: v${FLUX_VERSION}"'
want=$(yq -r '.jobs.build-amd64.steps[] | select(.name == "Set up build toolchain") | .env.FLUX_VERSION' "$wf")
want=$(yq -r '.jobs.build-amd64.steps[] | select(.name == "Set up flux") | .with.version' "$wf")
[ -n "$want" ] && [ "$want" != null ]
[ "$(yq -r '.jobs.prepare-release.steps[] | select(.name == "Set up build toolchain") | .env.FLUX_VERSION' "$wf")" = "$want" ]
[ "$(yq -r '.jobs.prepare-release.steps[] | select(.name == "Set up flux") | .with.version' "$wf")" = "$want" ]
}
@test "the amd64 release job rebuilds matchbox as a two-arch index before it captures the tree" {
+6 -6
View File
@@ -109,12 +109,12 @@ fi
#
# This list is a RESTATEMENT of flux's built-in `excludeOCI` set, not a
# derivation of it — the CLI does not expose it — so it is coupled to a flux
# version. That version is pinned as FLUX_VERSION (2.8.6) in all three workflow
# steps that install the toolchain, and the promote-packages contract test pins
# that they agree. If flux ever changes the set, the symptom is a "contain
# different files" failure naming the newly included or excluded class, which
# is loud and lands before any stable name exists; the fix is to update this
# list and FLUX_VERSION together, in that order.
# version. That version is the `version:` input (2.8.6) of every
# fluxcd/flux2/action step in the workflows, and the ci-tool-installs contract
# test pins that they agree. If flux ever changes the set, the symptom is a
# "contain different files" failure naming the newly included or excluded
# class, which is loud and lands before any stable name exists; the fix is to
# update this list and that version together, in that order.
artifact_entries() {
(
cd "$1"