From 053b65fa9b5bccdc829d51f1039cd910c77d8ea5 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 13 Sep 2026 19:23:44 -0700 Subject: [PATCH] chore(release): cut 0.4.11 --- CHANGELOG.md | 4 +++ docs/releasing.md | 2 +- scripts/release-local | 12 +++++++- scripts/test-release-local.sh | 56 ++++++++++++++++++++++++++++++++++- 4 files changed, 71 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cfccf6e..42a314d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## 0.4.11 - 2026-09-13 + +- Release tooling: fix the Developer ID signing canary under the isolated producer home so signed macOS binaries publish again; v0.4.10 was tagged but shipped no binaries, and 0.4.11 contains everything listed under 0.4.10. + ## 0.4.10 - 2026-09-13 **Highlights:** Security: cross-origin API redirects are refused and API keys are redacted from errors; routing across the date line is fixed. diff --git a/docs/releasing.md b/docs/releasing.md index 86a1258..f1683df 100644 --- a/docs/releasing.md +++ b/docs/releasing.md @@ -24,7 +24,7 @@ v0.4.5 and later use a serialized, local, draft-first release flow for notarized ## Prepare Locally -Keep `CHANGELOG.md` at `## X.Y.Z - Unreleased` throughout local preparation. Its section is the only release-note source; release commands read it from the protected commit or fresh tagged checkout, never from mutable maintainer working-tree bytes. +Keep `CHANGELOG.md` at `## X.Y.Z - Unreleased` throughout local preparation. Tagged changelog content is the only release-note source; release commands read it from the protected commit or fresh tagged checkout, never from mutable maintainer working-tree bytes. For the 0.4.11 recovery release, the producer appends the complete dated 0.4.10 section after the 0.4.11 entry because 0.4.10 shipped no binaries. The draft and published notes are verified against that same combined text. Run the full local proof set before requesting either serialized gate: diff --git a/scripts/release-local b/scripts/release-local index 11c2224..0c51880 100755 --- a/scripts/release-local +++ b/scripts/release-local @@ -22,7 +22,7 @@ readonly HOMEBREW_REPOSITORY="Homebrew/brew" readonly EXPECTED_HOMEBREW_ORIGIN_HTTPS="https://github.com/${HOMEBREW_REPOSITORY}" readonly RELEASE_MAC_APP_DEFAULT="/Users/steipete/Projects/agent-scripts/skills/release-mac-app/scripts/mac-release" readonly RELEASE_MAC_APP_EXPECTED_SHA256="e65e06ef89ec90ebfc537d28748a3c4de8ce89bd09b51e4d67ba4bdd95427255" -readonly RELEASE_MAC_APP_LIB_EXPECTED_SHA256="170b05a794e5ade32a603a01c7942f8170d44ccc13b678c5285076510a008baf" +readonly RELEASE_MAC_APP_LIB_EXPECTED_SHA256="0d8e347856c873fd8251e2f594943884f001f26b62da64863c802218c7a4cdd6" readonly CLEAN_SYSTEM_PATH="/opt/homebrew/opt/go@1.26/bin:/usr/local/opt/go@1.26/bin:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin" readonly SECRET_SCOPE_SYSTEM_PATH="/usr/bin:/bin:/usr/sbin:/sbin" readonly SSH_KEYGEN_BIN="/usr/bin/ssh-keygen" @@ -1191,6 +1191,16 @@ extract_release_notes() { END {if (!found) exit 3} ' "$changelog" > "$output" fi + if [[ "$mode" == released && "$version" == 0.4.11 ]]; then + # The prior tag shipped no binaries; include its changes in the recovery release. + header_count="$(grep -Fxc '## 0.4.10 - 2026-09-13' "$changelog" || true)" + [[ "$header_count" == 1 ]] || die "0.4.11 release notes require exactly one dated 0.4.10 section" + /usr/bin/awk ' + $0 == "## 0.4.10 - 2026-09-13" {found=1; print; next} + found && /^## / {exit} + found {print} + ' "$changelog" >> "$output" + fi grep -Eq '^[-*] ' "$output" || die "release notes section has no entries" } diff --git a/scripts/test-release-local.sh b/scripts/test-release-local.sh index 349fa84..e99a977 100755 --- a/scripts/test-release-local.sh +++ b/scripts/test-release-local.sh @@ -82,6 +82,59 @@ source_release() { ensure_git_isolation } +test_release_note_rollup() { + local scratch + scratch="$(mktemp -d "${TMPDIR:-/tmp}/goplaces-notes.XXXXXX")" + cat > "${scratch}/CHANGELOG.md" <<'EOF' +# Changelog + +## 0.4.11 - 2026-09-13 + +- Release tooling: recover signed binaries. + +## 0.4.10 - 2026-09-13 + +**Highlights:** Security and routing fixes. + +- Security: reject cross-origin redirects. + +## 0.4.9 - 2026-08-24 + +- Older changes must not appear. +EOF + cat > "${scratch}/expected.md" <<'EOF' + +- Release tooling: recover signed binaries. + +## 0.4.10 - 2026-09-13 + +**Highlights:** Security and routing fixes. + +- Security: reject cross-origin redirects. + +EOF + ( + source_release + extract_release_notes v0.4.11 released "${scratch}/actual.md" "${scratch}/CHANGELOG.md" + /usr/bin/cmp "${scratch}/expected.md" "${scratch}/actual.md" + extract_release_notes v0.4.10 released "${scratch}/ordinary.md" "${scratch}/CHANGELOG.md" + grep -Fq '**Highlights:** Security and routing fixes.' "${scratch}/ordinary.md" + if grep -Eq '^## |Older changes|recover signed' "${scratch}/ordinary.md"; then + die "ordinary release notes included another version" + fi + /usr/bin/sed '/^## 0.4.10 - /d' "${scratch}/CHANGELOG.md" > "${scratch}/missing.md" + if (extract_release_notes v0.4.11 released "${scratch}/bad.md" "${scratch}/missing.md") >/dev/null 2>&1; then + die "missing recovery section was accepted" + fi + /bin/cp "${scratch}/CHANGELOG.md" "${scratch}/duplicate.md" + printf '\n## 0.4.10 - 2026-09-13\n' >> "${scratch}/duplicate.md" + if (extract_release_notes v0.4.11 released "${scratch}/bad.md" "${scratch}/duplicate.md") >/dev/null 2>&1; then + die "duplicate recovery section was accepted" + fi + ) + rm -rf "$scratch" +} + test_static_contract() { local download_line offline_line brew_test_line post_brew_status_line override_output tap_predispatch_line tap_dispatch_line mutation_shape local mutation_root mutation_gh mutation_sentinel mutation_error @@ -198,7 +251,7 @@ EOF die "exact tap predispatch recheck is not immediately before the workflow POST" grep -Fq 'readonly RELEASE_MAC_APP_DEFAULT="/Users/steipete/Projects/agent-scripts/skills/release-mac-app/scripts/mac-release"' "$release_script" || die "release-mac-app is not pinned independently of HOME" grep -Fq 'readonly RELEASE_MAC_APP_EXPECTED_SHA256="e65e06ef89ec90ebfc537d28748a3c4de8ce89bd09b51e4d67ba4bdd95427255"' "$release_script" || die "release-mac-app entrypoint digest is not pinned" - grep -Fq 'readonly RELEASE_MAC_APP_LIB_EXPECTED_SHA256="170b05a794e5ade32a603a01c7942f8170d44ccc13b678c5285076510a008baf"' "$release_script" || die "release-mac-app library digest is not pinned" + grep -Fq 'readonly RELEASE_MAC_APP_LIB_EXPECTED_SHA256="0d8e347856c873fd8251e2f594943884f001f26b62da64863c802218c7a4cdd6"' "$release_script" || die "release-mac-app library digest is not pinned" grep -Fq '[[ "$source_digest" == "$RELEASE_MAC_APP_EXPECTED_SHA256" ]]' "$release_script" || die "release-mac-app entrypoint is not checked against reviewed bytes" grep -Fq '[[ "$source_lib_digest" == "$RELEASE_MAC_APP_LIB_EXPECTED_SHA256" ]]' "$release_script" || die "release-mac-app library is not checked against reviewed bytes" ! grep -Fq 'RELEASE_MAC_APP_DEFAULT="${HOME}' "$release_script" || die "release-mac-app still trusts ambient HOME" @@ -3145,6 +3198,7 @@ EOF main() { command -v jq >/dev/null 2>&1 || die "jq is required" command -v shellcheck >/dev/null 2>&1 || die "shellcheck is required" + test_release_note_rollup test_static_contract test_govulncheck_build_info_validation test_jq_freeze_survives_command_substitution