Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
19298b0
:page_facing_up: docs(spec): re-measure developer-toolchain-pins and …
konih Oct 16, 2026
5490677
:page_facing_up: docs(spec): start the developer-toolchain-pins loop
konih Oct 16, 2026
d92e187
:page_facing_up: docs(spec): record the spec-set review of developer-…
konih Oct 16, 2026
42f0581
:page_facing_up: docs(spec): apply the spec-set review to developer-t…
konih Oct 16, 2026
f94b155
:page_facing_up: docs(spec): record the spec-set triage for developer…
konih Oct 16, 2026
61ebeb6
:white_check_mark: test(ci): add the toolchain-pins consistency guard
konih Oct 16, 2026
3db034c
:page_facing_up: docs(spec): record task 1.1 of developer-toolchain-pins
konih Oct 16, 2026
96f8136
:white_check_mark: test(ci): fold the mise pin-drift guard into toolc…
konih Oct 16, 2026
d908448
:page_facing_up: docs(spec): record task 1.2 of developer-toolchain-pins
konih Oct 16, 2026
e39bb40
:arrow_up: chore(deps): pin Task 3.52.0 at every site behind a DTP-1 …
konih Oct 16, 2026
0456eba
:page_facing_up: docs(spec): record task 2.1 of developer-toolchain-pins
konih Oct 16, 2026
f88790d
:arrow_up: chore(deps): pin govulncheck once behind a DTP-2 check
konih Oct 16, 2026
bb20cf2
:page_facing_up: docs(spec): record task 2.2 of developer-toolchain-pins
konih Oct 16, 2026
959bfca
:white_check_mark: test(ci): keep versions in mise.toml comments besi…
konih Oct 16, 2026
73fc85b
:page_facing_up: docs(spec): record task 2.3 of developer-toolchain-pins
konih Oct 16, 2026
32ac41c
:white_check_mark: test(ci): check every Renovate manager is alive an…
konih Oct 16, 2026
b545548
:page_facing_up: docs(spec): record tasks 3.1-3.3 of developer-toolch…
konih Oct 16, 2026
74916ff
:page_facing_up: docs(spec): record task group 3 of developer-toolcha…
konih Oct 16, 2026
10eacb4
:lock: fix(hack): verify git-cliff and helm-docs downloads by checksum
konih Oct 16, 2026
9cc42a8
:page_facing_up: docs(hack): say what the installer checksums catch
konih Oct 16, 2026
e6d6a11
:page_facing_up: docs(spec): record task group 4 of developer-toolcha…
konih Oct 16, 2026
1fd68c2
:page_facing_up: docs(spec): record task group 4 of developer-toolcha…
konih Oct 16, 2026
b382b68
:page_facing_up: docs(spec): record stage-B review of developer-toolc…
konih Oct 16, 2026
ea59a68
:page_facing_up: docs(spec): triage stage-B round 1 of developer-tool…
konih Oct 16, 2026
ecb5bf9
:white_check_mark: test(ci): prove a full Renovate bump keeps the too…
konih Oct 16, 2026
9d48735
:construction_worker: ci: run the toolchain-pins self-test in tool-pi…
konih Oct 16, 2026
f9d29d3
:bug: fix(hack): verify installer checksums with shasum on macOS
konih Oct 16, 2026
0363bdd
:bug: fix(ci): reject a Taskfile pin variable that holds latest in CFC-2
konih Oct 16, 2026
d8d8a3e
:bug: fix(ci): read mixed-case uses: refs as the same action
konih Oct 16, 2026
2778a49
:wrench: build(deps): give golangci-lint its own Renovate group
konih Oct 16, 2026
a850e83
:page_facing_up: docs(spec): align developer-toolchain with the stage…
konih Oct 16, 2026
d5bc20e
:bug: fix(ci): require a full version for CFC-2 pins
konih Oct 16, 2026
19ec82f
:white_check_mark: test(ci): read the govulncheck mutant values from …
konih Oct 16, 2026
aca1379
:page_facing_up: docs(spec): record task group 6 of developer-toolcha…
konih Oct 16, 2026
4e508b6
:page_facing_up: docs(spec): record task group 6 of developer-toolcha…
konih Oct 16, 2026
8f7e9d5
:page_facing_up: docs(spec): record stage-B round 2 of developer-tool…
konih Oct 16, 2026
e338ee5
:page_facing_up: docs(spec): triage stage-B round 2 of developer-tool…
konih Oct 16, 2026
63a013d
:wrench: build(deps): keep node and python CI inputs out of the githu…
konih Oct 16, 2026
af9e1c5
:page_facing_up: docs(spec): record task 7.1 of developer-toolchain-pins
konih Oct 16, 2026
672a1f9
:page_facing_up: docs(spec): record G7 and the hand-off notes for dev…
konih Oct 16, 2026
6f5dc8e
:page_facing_up: docs(spec): add the PR description for developer-too…
konih Oct 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/actions/kind-e2e-setup/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ runs:

- uses: go-task/setup-task@a00fbb05ce67b35648be3c78cbc9fd85354c757e # v2.2.0
with:
version: 3.51.1
version: 3.52.0

- name: Restore kind binary cache
id: kind-cache
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/changelog-sync.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ jobs:
persist-credentials: false
- uses: go-task/setup-task@a00fbb05ce67b35648be3c78cbc9fd85354c757e # v2.2.0
with:
version: 3.51.1
version: 3.52.0
- name: Regenerate CHANGELOG.md
run: task changelog:write
env:
Expand Down
69 changes: 43 additions & 26 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,13 @@
#
# The docs-only path is NOT free, and that is a deliberate trade. Where this workflow previously
# was not dispatched at all, a documentation-only PR now runs `changes`, `gitleaks`, `lint` and
# `tool-pins-selftest` (CFC-6, the tool-pins gate's self-test, split out of lint for wall time) --
# `lint` because two existing gates (dist_ci_wiring_test.sh, repo_root_links_wiring_test.sh)
# require it to be unconditional, and because it is where ci_docs_gate_test.sh runs, so leaving
# it ungated is the only thing that exercises the docs-only direction at all. What such a PR no
# longer pays for is the Go suite, the kind clusters, testcontainers, the fuzz matrix and the
# image builds -- and it no longer needs a ruleset bypass to merge.
# `tool-pins-selftest` (CFC-6 and DTP-7: the tool-pins and toolchain-pins self-tests, split out of
# lint for wall time) -- `lint` because two existing gates (dist_ci_wiring_test.sh,
# repo_root_links_wiring_test.sh) require it to be unconditional, and because it is where
# ci_docs_gate_test.sh runs, so leaving it ungated is the only thing that exercises the docs-only
# direction at all. What such a PR no longer pays for is the Go suite, the kind clusters,
# testcontainers, the fuzz matrix and the image builds -- and it no longer needs a ruleset bypass
# to merge.
name: CI

on:
Expand Down Expand Up @@ -236,7 +237,7 @@ jobs:
- uses: ./.github/actions/go-cache
- uses: go-task/setup-task@a00fbb05ce67b35648be3c78cbc9fd85354c757e # v2.2.0
with:
version: 3.51.1
version: 3.52.0
- name: Verify generated artifacts
run: task verify
env:
Expand Down Expand Up @@ -269,7 +270,7 @@ jobs:
- uses: ./.github/actions/go-cache
- uses: go-task/setup-task@a00fbb05ce67b35648be3c78cbc9fd85354c757e # v2.2.0
with:
version: 3.51.1
version: 3.52.0
- name: govulncheck
run: task vulncheck

Expand All @@ -291,7 +292,7 @@ jobs:
- uses: ./.github/actions/go-cache
- uses: go-task/setup-task@a00fbb05ce67b35648be3c78cbc9fd85354c757e # v2.2.0
with:
version: 3.51.1
version: 3.52.0
- name: Check formatting
run: task format:check
- name: Install ShellCheck
Expand Down Expand Up @@ -408,15 +409,6 @@ jobs:
# half; docs.yaml's docs/** filter covers a change to the map itself.
- name: Verify the docs map labels the pages it points at (DOC-MAP-01)
run: bash hack/test/docs_map_contract_test.sh
# DEV-MISE-01: mise.toml restates the task/node/python versions this job installs, and a
# restated literal with no mechanism behind it is how release.yaml reached helm v3.21.4
# while CI stayed on v3.17.3 (see renovate.json's own comment). Renovate's go-task manager
# scans only .github/(actions|workflows)/, and nothing at all manages the node-version /
# python-version inputs, so this comparison is the only thing that would notice. Run it
# here, unconditionally: mise.toml and hack/** are both outside this workflow's
# paths-ignore, so every change class that can break the contract triggers this job.
- name: Verify mise.toml has not drifted from the CI pins (DEV-MISE-01)
run: bash hack/test/dev_mise_pin_drift_test.sh
# DOC-ADRREFS-01: every ADR-\d{4} token in tracked files must resolve to a committed ADR.
# ADRs get renumbered and deleted; code comments, CRD descriptions (user-visible via
# `kubectl explain`), the chart values schema, and docs keep citing the retired numbers
Expand Down Expand Up @@ -562,6 +554,21 @@ jobs:
run: bash hack/test/consistency_metrics_test.sh
- name: Verify the metrics gate can still fail (CFC-6)
run: bash hack/test/consistency_metrics_test.sh --self-test
# DTP-1/DTP-2/DTP-3/DTP-5/DTP-6/DTP-7 (openspec change developer-toolchain-pins): Task (every
# setup-task version: input and mise.toml), gitleaks, git-cliff and shellcheck agree across
# their pin sites and meet their floors, shellcheck has digests for its pin, task vulncheck
# runs the Renovate-annotated GOVULNCHECK_VERSION, and the installer-checksum exemptions
# only shrink. DEV-MISE-01: mise.toml pins task, its
# node/python equal every setup-node/setup-python pin under .github/ and Go stays read from
# go.mod. Renovate's github-actions manager extracts those node/python inputs too, but a
# renovate.json rule disables them (their bump would leave mise.toml behind and red this
# check), so they are bumped by hand and this comparison keeps mise.toml in step; the
# guard's Renovate model checks the rule. DTP-5: a mise.toml or Taskfile.yml comment states
# a version only directly above (or on) the pin it names, at its value, and never counts
# pin sites.
# Its --self-test (DTP-7) runs in the tool-pins-selftest job below.
- name: Verify Task, gitleaks, git-cliff, shellcheck, govulncheck and mise.toml pins agree (DTP-1, DTP-2, DTP-3, DTP-5, DTP-6, DEV-MISE-01)
run: bash hack/test/consistency_toolchain_pins_test.sh
- name: Verify the go-patch-lag sensor verdicts and workflow shape (CFC-5)
run: bash hack/test/go_patch_lag_test.sh
- name: Verify the go-patch-lag test can still fail (CFC-6)
Expand All @@ -587,8 +594,11 @@ jobs:
# verdict: it runs on every PR, docs-only included, and is a required context
# (hack/release/verify-eligibility.sh). hack/test/ci_workflow_security_test.sh (CFC-6 rule)
# requires the self-test in this job and hack/test/ci_docs_gate_test.sh keeps the job
# unconditional. The timeout bounds a stalled module or vuln-DB download; local runs took
# 7 to 17 minutes.
# unconditional. The toolchain-pins self-test (DTP-7, openspec change developer-toolchain-pins)
# runs here too, for the same reason: about 8 minutes, half of it the all-pins bump control's
# nested self-test. It also removes the toolchain-pins guard's steps from a copy and expects
# the CWS-6/CFC-6 meta-test to red, so it needs yq. The timeout bounds a stalled module or
# vuln-DB download; local runs of the two took 15 to 25 minutes.
tool-pins-selftest:
name: tool-pins-selftest
runs-on: ubuntu-latest
Expand All @@ -600,6 +610,13 @@ jobs:
- uses: ./.github/actions/go-cache
- name: Verify the tool-pins gate can still fail (CFC-6)
run: bash hack/test/consistency_tool_pins_test.sh --self-test
- name: Ensure yq is available
run: |
# ubuntu-latest images generally ship yq, but don't assume it (same step as lint's).
command -v yq >/dev/null 2>&1 || sudo snap install yq
yq --version
- name: Verify the toolchain-pins gate can still fail (DTP-7)
run: bash hack/test/consistency_toolchain_pins_test.sh --self-test

# CI-DOCSGATE-01: the REAL suite. It is deliberately NOT named `test` -- the required context
# of that name is produced by the `test` job at the end of this file, which reports on every
Expand All @@ -624,7 +641,7 @@ jobs:
envtest: true
- uses: go-task/setup-task@a00fbb05ce67b35648be3c78cbc9fd85354c757e # v2.2.0
with:
version: 3.51.1
version: 3.52.0
- name: Unit tests and coverage floor
run: task coverage
env:
Expand Down Expand Up @@ -664,7 +681,7 @@ jobs:
- uses: ./.github/actions/go-cache
- uses: go-task/setup-task@a00fbb05ce67b35648be3c78cbc9fd85354c757e # v2.2.0
with:
version: 3.51.1
version: 3.52.0
- name: Build manager
run: task build

Expand All @@ -681,7 +698,7 @@ jobs:
- uses: ./.github/actions/go-cache
- uses: go-task/setup-task@a00fbb05ce67b35648be3c78cbc9fd85354c757e # v2.2.0
with:
version: 3.51.1
version: 3.52.0
- name: Verify Forgejo pre-pull safeguards
run: bash test/ci/forgejo_prepull_test.sh
- name: Pre-pull Forgejo image
Expand All @@ -707,7 +724,7 @@ jobs:
persist-credentials: false
- uses: go-task/setup-task@a00fbb05ce67b35648be3c78cbc9fd85354c757e # v2.2.0
with:
version: 3.51.1
version: 3.52.0
- name: Install Helm
env:
HELM_VERSION: v3.22.0
Expand All @@ -733,7 +750,7 @@ jobs:
envtest: true
- uses: go-task/setup-task@a00fbb05ce67b35648be3c78cbc9fd85354c757e # v2.2.0
with:
version: 3.51.1
version: 3.52.0
- name: Performance snapshot (optional gate)
run: task perf-report
env:
Expand Down Expand Up @@ -857,7 +874,7 @@ jobs:
- uses: ./.github/actions/go-cache
- uses: go-task/setup-task@a00fbb05ce67b35648be3c78cbc9fd85354c757e # v2.2.0
with:
version: 3.51.1
version: 3.52.0
- name: Build controller image
run: task docker:build

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/docs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ jobs:
cache-dependency-path: docs/package-lock.json
- uses: go-task/setup-task@a00fbb05ce67b35648be3c78cbc9fd85354c757e # v2.2.0
with:
version: 3.51.1
version: 3.52.0
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version-file: go.mod
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/e2e-nightly.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ jobs:

- uses: go-task/setup-task@a00fbb05ce67b35648be3c78cbc9fd85354c757e # v2.2.0
with:
version: 3.51.1
version: 3.52.0

- name: Micro-benchmarks
run: task bench
Expand Down Expand Up @@ -140,7 +140,7 @@ jobs:

- uses: go-task/setup-task@a00fbb05ce67b35648be3c78cbc9fd85354c757e # v2.2.0
with:
version: 3.51.1
version: 3.52.0

- name: Unit + envtest under -race
id: race
Expand Down Expand Up @@ -206,7 +206,7 @@ jobs:

- uses: go-task/setup-task@a00fbb05ce67b35648be3c78cbc9fd85354c757e # v2.2.0
with:
version: 3.51.1
version: 3.52.0

- name: Extractor hot-path budget (micro-benchmark, not a scale tier)
run: task extract-budget
Expand All @@ -231,7 +231,7 @@ jobs:

- uses: go-task/setup-task@a00fbb05ce67b35648be3c78cbc9fd85354c757e # v2.2.0
with:
version: 3.51.1
version: 3.52.0

- name: Extended envtest scale (10k)
run: go test -run TestEngine_ScaleEnvtestOptIn -count=1 -timeout=35m ./internal/collect/...
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/preflight.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
- uses: ./.github/actions/go-cache
- uses: go-task/setup-task@a00fbb05ce67b35648be3c78cbc9fd85354c757e # v2.2.0
with:
version: 3.51.1
version: 3.52.0
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: "22"
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/sonarcloud.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
envtest: true
- uses: go-task/setup-task@a00fbb05ce67b35648be3c78cbc9fd85354c757e # v2.2.0
with:
version: 3.51.1
version: 3.52.0
- name: Unit tests and coverage
run: task coverage
env:
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ AGENTS.local.md
.envrc
.envrc.*
# mise's personal-override files. mise.toml itself is committed and mirrors the CI pins
# (see hack/test/dev_mise_pin_drift_test.sh); these two are the per-developer overlay, and
# (see hack/test/consistency_toolchain_pins_test.sh); these two are the per-developer overlay, and
# the natural place someone would put an [env] block next to the .envrc above.
mise.local.toml
.mise.local.toml
Expand Down
4 changes: 3 additions & 1 deletion Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ vars:
MARKDOWNLINT_CONFIG: hack/tooling/markdownlint-cli2.yaml
# renovate: datasource=github-releases depName=fe3dback/go-arch-lint
GO_ARCH_LINT_VERSION: v1.19.0
# renovate: datasource=go depName=golang.org/x/vuln
GOVULNCHECK_VERSION: v1.9.0
# renovate: datasource=github-releases depName=helm/helm
HELM_VERSION: v3.22.0
# renovate: datasource=github-releases depName=norwoodj/helm-docs
Expand Down Expand Up @@ -201,7 +203,7 @@ tasks:
desc: Scan for known Go vulnerabilities (govulncheck)
deps: [install]
cmds:
- go run golang.org/x/vuln/cmd/govulncheck@v1.9.0 ./...
- go run golang.org/x/vuln/cmd/govulncheck@{{.GOVULNCHECK_VERSION}} ./...

test:
desc: Run unit tests with envtest (alias for test:run)
Expand Down
4 changes: 2 additions & 2 deletions docs/development/setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ mise trust && mise install
| Pinned by `mise.toml` | What it mirrors |
| --- | --- |
| Go | `go.mod` — read straight from it, so the two cannot drift |
| Task | `go-task/setup-task` `version: 3.51.1` in `.github/workflows/` |
| Task | the `go-task/setup-task` `version:` input in `.github/workflows/` and `.github/actions/` |
| Node | `node-version: "22"` in the Docs and Preflight workflows (markdownlint) |
| Python | `python-version: "3.12"` in the Docs workflow (MkDocs build) |

Expand All @@ -50,7 +50,7 @@ fetches `shellcheck`, `helm`, `helm-docs`, `git-cliff` and friends. Keeping thos
`mise.toml` keeps them at one source of truth each.

**CI remains the source of truth for every version**, and that is enforced rather than merely
asked for: `hack/test/dev_mise_pin_drift_test.sh` compares `mise.toml`'s `task` / `node` /
asked for: `hack/test/consistency_toolchain_pins_test.sh` compares `mise.toml`'s `task` / `node` /
`python` values against every `setup-task` / `setup-node` / `setup-python` pin under `.github/`
and reds the `lint` job on any disagreement. It also locks the Go arrangement — a `go` entry
added under `[tools]` would override `go.mod`, so the gate rejects one. If `mise.toml` and a
Expand Down
33 changes: 28 additions & 5 deletions hack/install-git-cliff.sh
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,33 @@ mkdir -p "$(dirname "${root}/${OUT}")"
tmpdir="$(mktemp -d)"
trap 'rm -rf "${tmpdir}"' EXIT

# NOTE: this installer verifies no digest -- upstream publishes checksums, but wiring them up is
# a separate change from routing the transport through the shared helper, and hack/test/
# ci_fetch_lib_hardening_test.sh records the gap explicitly so it cannot be forgotten.
fetch_to "${url}" "${tmpdir}/git-cliff.tgz" "git-cliff ${VERSION} tarball"
tar -xzf "${tmpdir}/git-cliff.tgz" -C "${tmpdir}"
# Upstream publishes one `<sha512> <asset>` file per asset and no SHA256, so this compares with
# sha512sum rather than verify_sha256 (stock macOS has no sha512sum, only shasum -a 512; with
# neither it refuses before downloading). The digest is in hand before the tarball is fetched, and
# a failed checksum fetch stops here. The checksum comes from the same release as the tarball, so
# it catches a corrupted or swapped download, not a compromised release.
if command -v sha512sum >/dev/null 2>&1; then
sha512() { sha512sum "$1"; }
elif command -v shasum >/dev/null 2>&1; then
sha512() { shasum -a 512 "$1"; }
else
echo "cannot verify ${asset}: neither sha512sum nor shasum is installed" >&2
exit 1
fi
fetch_to "${url}.sha512" "${tmpdir}/${asset}.sha512" "git-cliff ${VERSION} checksum"
expected="$(awk -v file="${asset}" '$2 == file || $2 == "*" file {print $1}' "${tmpdir}/${asset}.sha512")"
if [[ ! "${expected}" =~ ^[0-9a-f]{128}$ ]]; then
echo "failed to resolve a sha512 for ${asset} from ${url}.sha512" >&2
exit 1
fi
fetch_to "${url}" "${tmpdir}/${asset}" "git-cliff ${VERSION} tarball"
actual="$(sha512 "${tmpdir}/${asset}" | awk '{print $1}')"
if [[ "${actual}" != "${expected}" ]]; then
echo "checksum mismatch for ${asset}" >&2
echo " expected: ${expected}" >&2
echo " actual: ${actual}" >&2
exit 1
fi
tar -xzf "${tmpdir}/${asset}" -C "${tmpdir}"
install -m 0755 "${tmpdir}/git-cliff-${VER}/git-cliff" "${root}/${OUT}"
echo "installed ${OUT} (${VERSION} ${platform})"
21 changes: 17 additions & 4 deletions hack/install-helm-docs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,31 @@ case "$(uname -s)" in
esac

asset="helm-docs_${VER}_${os}_${arch}.tar.gz"
url="https://github.com/norwoodj/helm-docs/releases/download/${VERSION}/${asset}"
base_url="https://github.com/norwoodj/helm-docs/releases/download/${VERSION}"
url="${base_url}/${asset}"

root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
# shellcheck source=lib/verify-sha256.sh
source "${root}/hack/lib/verify-sha256.sh"
# shellcheck source=lib/fetch.sh
source "${root}/hack/lib/fetch.sh"

mkdir -p "$(dirname "${root}/${OUT}")"
tmpdir="$(mktemp -d)"
trap 'rm -rf "${tmpdir}"' EXIT

# NOTE: this installer verifies no digest -- see the matching note in hack/install-git-cliff.sh.
fetch_to "${url}" "${tmpdir}/helm-docs.tgz" "helm-docs ${VERSION} tarball"
tar -xzf "${tmpdir}/helm-docs.tgz" -C "${tmpdir}"
# The digest comes from the release's checksums.txt (`<sha256> <asset>` per asset) and is in hand
# before the tarball is fetched; a failed checksum fetch stops here. Like git-cliff's, it catches a
# corrupted or swapped download, not a compromised release.
fetch_to "${base_url}/checksums.txt" "${tmpdir}/checksums.txt" "helm-docs ${VERSION} checksums"
expected="$(awk -v file="${asset}" '$2 == file {print $1}' "${tmpdir}/checksums.txt")"
# One full SHA256 or nothing: a missing, short or doubled line is refused before any comparison.
if [[ ! "${expected}" =~ ^[0-9a-f]{64}$ ]]; then
echo "failed to resolve a checksum for ${asset} from ${base_url}/checksums.txt" >&2
exit 1
fi
fetch_to "${url}" "${tmpdir}/${asset}" "helm-docs ${VERSION} tarball"
verify_sha256 "${tmpdir}/${asset}" "${expected}"
tar -xzf "${tmpdir}/${asset}" -C "${tmpdir}"
install -m 0755 "${tmpdir}/helm-docs" "${root}/${OUT}"
echo "installed ${OUT} (${VERSION} ${os}/${arch})"
10 changes: 9 additions & 1 deletion hack/lib/verify-sha256.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,15 @@ verify_sha256() {
return 1
fi

actual="$(sha256sum "${file}" | awk '{print $1}')"
# sha256sum is GNU coreutils; stock macOS has only shasum. Neither: refuse, never skip.
if command -v sha256sum >/dev/null 2>&1; then
actual="$(sha256sum "${file}" | awk '{print $1}')"
elif command -v shasum >/dev/null 2>&1; then
actual="$(shasum -a 256 "${file}" | awk '{print $1}')"
else
echo "cannot verify $(basename "${file}"): neither sha256sum nor shasum is installed" >&2
return 1
fi
if [[ "${actual}" != "${expected}" ]]; then
echo "checksum mismatch for $(basename "${file}")" >&2
echo " expected: ${expected}" >&2
Expand Down
Loading
Loading