From 236b6539c88f7d2a4a96248da8d4f9676cdf2b8b Mon Sep 17 00:00:00 2001 From: fullsend-code <278716306+fullsend-ai-coder[bot]@users.noreply.github.com> Date: Fri, 18 Sep 2026 08:31:48 +0000 Subject: [PATCH 01/11] fix(#111): replace deprecated Homebrew cask postflight stanza Homebrew 7.0 deprecated the `postflight` block in favour of the declarative `postflight_steps` stanza. GoReleaser's `hooks.post.install` emits the deprecated form and does not yet support `postflight_steps` natively (planned for v2.19 via goreleaser/goreleaser#6873). Use GoReleaser's `custom_block` to inject the correct `postflight_steps` stanza directly, replacing `hooks.post.install`. The new stanza uses Homebrew's install-steps DSL: `on_macos do` replaces `if OS.mac?`, `run` replaces `system_command`, and `{{staged_path}}` replaces Ruby interpolation `#{staged_path}`. Updated the test fixture to match the new stanza syntax. All cask integrity regression tests pass. Closes #111 --- .github/scripts/testdata/replicator-v0.5.0.rb | 6 +++--- .goreleaser.yaml | 13 +++++++------ 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/.github/scripts/testdata/replicator-v0.5.0.rb b/.github/scripts/testdata/replicator-v0.5.0.rb index 5c6923c..51ae0a7 100644 --- a/.github/scripts/testdata/replicator-v0.5.0.rb +++ b/.github/scripts/testdata/replicator-v0.5.0.rb @@ -30,9 +30,9 @@ binary "replicator" - postflight do - if OS.mac? - system_command "/usr/bin/xattr", args: ["-dr", "com.apple.quarantine", "#{staged_path}/replicator"] + postflight_steps do + on_macos do + run "/usr/bin/xattr", args: ["-dr", "com.apple.quarantine", "{{staged_path}}/replicator"] end end diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 79034e0..f59106c 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -57,12 +57,13 @@ homebrew_casks: homepage: "https://github.com/unbound-force/replicator" directory: Casks skip_upload: true - hooks: - post: - install: | - if OS.mac? - system_command "/usr/bin/xattr", args: ["-dr", "com.apple.quarantine", "#{staged_path}/replicator"] - end + custom_block: | + postflight_steps do + on_macos do + run "/usr/bin/xattr", + args: ["-dr", "com.apple.quarantine", "{{ "{{staged_path}}" }}/replicator"] + end + end repository: owner: unbound-force name: homebrew-tap From 58f82f4aefab04368cd04b9694b43399e33a9230 Mon Sep 17 00:00:00 2001 From: fullsend-fix <278716306+fullsend-ai-coder[bot]@users.noreply.github.com> Date: Fri, 18 Sep 2026 08:56:55 +0000 Subject: [PATCH 02/11] fix: use Ruby interpolation syntax for staged_path in cask MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace {{staged_path}} with #{staged_path} in both the GoReleaser custom_block and the testdata fixture. The {{}} syntax is not Ruby string interpolation — Ruby uses #{} — so the previous change caused xattr to target a nonexistent literal path, silently failing to remove macOS quarantine. Add a regression assertion to the cask test suite verifying that the generated cask uses #{staged_path} (Ruby interpolation) rather than {{staged_path}}. Addresses #112 --- .github/scripts/patch-homebrew-cask_test.sh | 2 ++ .github/scripts/testdata/replicator-v0.5.0.rb | 2 +- .goreleaser.yaml | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/scripts/patch-homebrew-cask_test.sh b/.github/scripts/patch-homebrew-cask_test.sh index bd8a7c3..fe05edc 100755 --- a/.github/scripts/patch-homebrew-cask_test.sh +++ b/.github/scripts/patch-homebrew-cask_test.sh @@ -54,6 +54,8 @@ assert_success sed "7c\\ sha256 \"$ARCHIVE_SHA\"" "$FIXTURE" > "$CASE_DIR/expected.rb" cmp -s "$CASE_DIR/expected.rb" "$CASE_DIR/replicator.rb" || \ fail "happy: patched cask differs from exact expected fixture" +grep -q '#{staged_path}' "$CASE_DIR/replicator.rb" || \ + fail "happy: cask uses {{staged_path}} instead of Ruby interpolation #{staged_path}" new_case stray-comment printf '\n# note: darwin_arm64 builds are notarized\n' >> "$CASE_DIR/replicator.rb" diff --git a/.github/scripts/testdata/replicator-v0.5.0.rb b/.github/scripts/testdata/replicator-v0.5.0.rb index 51ae0a7..1a873ac 100644 --- a/.github/scripts/testdata/replicator-v0.5.0.rb +++ b/.github/scripts/testdata/replicator-v0.5.0.rb @@ -32,7 +32,7 @@ postflight_steps do on_macos do - run "/usr/bin/xattr", args: ["-dr", "com.apple.quarantine", "{{staged_path}}/replicator"] + run "/usr/bin/xattr", args: ["-dr", "com.apple.quarantine", "#{staged_path}/replicator"] end end diff --git a/.goreleaser.yaml b/.goreleaser.yaml index f59106c..3d30b74 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -61,7 +61,7 @@ homebrew_casks: postflight_steps do on_macos do run "/usr/bin/xattr", - args: ["-dr", "com.apple.quarantine", "{{ "{{staged_path}}" }}/replicator"] + args: ["-dr", "com.apple.quarantine", "#{staged_path}/replicator"] end end repository: From 199159de58dbef035c440a490a2226390f68d7d0 Mon Sep 17 00:00:00 2001 From: fullsend-fix <278716306+fullsend-ai-coder[bot]@users.noreply.github.com> Date: Fri, 18 Sep 2026 10:50:51 +0000 Subject: [PATCH 03/11] fix: unify run call format in goreleaser custom_block Combine the split `run` call in .goreleaser.yaml onto a single line to match the test fixture format in testdata/replicator-v0.5.0.rb. Since GoReleaser injects custom_block verbatim, the generated cask now matches the fixture exactly. Addresses #112 --- .goreleaser.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 3d30b74..9788879 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -60,8 +60,7 @@ homebrew_casks: custom_block: | postflight_steps do on_macos do - run "/usr/bin/xattr", - args: ["-dr", "com.apple.quarantine", "#{staged_path}/replicator"] + run "/usr/bin/xattr", args: ["-dr", "com.apple.quarantine", "#{staged_path}/replicator"] end end repository: From 5f0e27fd58b6a7570e070321c2d9f062d6c43195 Mon Sep 17 00:00:00 2001 From: fullsend-fix <278716306+fullsend-ai-coder[bot]@users.noreply.github.com> Date: Mon, 28 Sep 2026 16:40:39 +0000 Subject: [PATCH 04/11] fix: address review feedback on PR #112 - Add maintenance comment above custom_block in .goreleaser.yaml noting that native postflight_steps support is not yet available in GoReleaser DSL (tracking goreleaser/goreleaser#6873) - Rephrase test assertion message in patch-homebrew-cask_test.sh to describe absence rather than presupposing substitution cause Addresses #112 --- .github/scripts/patch-homebrew-cask_test.sh | 2 +- .goreleaser.yaml | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/scripts/patch-homebrew-cask_test.sh b/.github/scripts/patch-homebrew-cask_test.sh index fe05edc..8dcc950 100755 --- a/.github/scripts/patch-homebrew-cask_test.sh +++ b/.github/scripts/patch-homebrew-cask_test.sh @@ -55,7 +55,7 @@ sed "7c\\ sha256 \"$ARCHIVE_SHA\"" "$FIXTURE" > "$CASE_DIR/expected.rb" cmp -s "$CASE_DIR/expected.rb" "$CASE_DIR/replicator.rb" || \ fail "happy: patched cask differs from exact expected fixture" grep -q '#{staged_path}' "$CASE_DIR/replicator.rb" || \ - fail "happy: cask uses {{staged_path}} instead of Ruby interpolation #{staged_path}" + fail "happy: patched cask is missing Ruby interpolation #{staged_path}" new_case stray-comment printf '\n# note: darwin_arm64 builds are notarized\n' >> "$CASE_DIR/replicator.rb" diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 9788879..2b9734e 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -57,6 +57,8 @@ homebrew_casks: homepage: "https://github.com/unbound-force/replicator" directory: Casks skip_upload: true + # custom_block: native postflight_steps not yet supported by GoReleaser DSL; + # migrate when available (tracking: goreleaser/goreleaser#6873) custom_block: | postflight_steps do on_macos do From 7d47ecfc4c278015c995a6ed7e644af74e5a7312 Mon Sep 17 00:00:00 2001 From: fullsend-fix <278716306+fullsend-ai-coder[bot]@users.noreply.github.com> Date: Tue, 29 Sep 2026 11:07:10 +0000 Subject: [PATCH 05/11] fix: add OpenSpec artifacts and rendered-cask regression test for PR #112 Add tactical OpenSpec change (fix-cask-postflight-steps) documenting the Homebrew postflight migration: proposal, design, tasks, and coverage strategy. Add a deterministic rendered-cask regression test that extracts the custom_block from .goreleaser.yaml and verifies postflight_steps is present, legacy postflight is absent, and #{staged_path} remains valid Ruby interpolation. The test requires no network access or goreleaser binary. Addresses #112 --- .github/scripts/patch-homebrew-cask_test.sh | 60 +++++++++++ .../fix-cask-postflight-steps/design.md | 90 ++++++++++++++++ .../fix-cask-postflight-steps/proposal.md | 101 ++++++++++++++++++ .../specs/coverage-strategy.md | 45 ++++++++ .../fix-cask-postflight-steps/tasks.md | 51 +++++++++ 5 files changed, 347 insertions(+) create mode 100644 openspec/changes/fix-cask-postflight-steps/design.md create mode 100644 openspec/changes/fix-cask-postflight-steps/proposal.md create mode 100644 openspec/changes/fix-cask-postflight-steps/specs/coverage-strategy.md create mode 100644 openspec/changes/fix-cask-postflight-steps/tasks.md diff --git a/.github/scripts/patch-homebrew-cask_test.sh b/.github/scripts/patch-homebrew-cask_test.sh index 8dcc950..c312573 100755 --- a/.github/scripts/patch-homebrew-cask_test.sh +++ b/.github/scripts/patch-homebrew-cask_test.sh @@ -112,4 +112,64 @@ new_case mismatched-manifest printf '%064d %s\n' 0 "$ARCHIVE_NAME" > "$CASE_DIR/checksums.txt" assert_failure_preserves_cask "mismatched manifest entry" +# --------------------------------------------------------------------------- +# Rendered-cask regression: validate the custom_block in .goreleaser.yaml +# emits correct Homebrew postflight_steps DSL without running goreleaser. +# --------------------------------------------------------------------------- +GORELEASER="$SCRIPT_DIR/../../.goreleaser.yaml" +if [ ! -f "$GORELEASER" ]; then + fail "rendered-cask: .goreleaser.yaml not found at $GORELEASER" +fi + +# Extract the custom_block literal block scalar value from .goreleaser.yaml. +# The block starts on the line after "custom_block: |" and continues while +# lines are indented deeper than the key's column. +CUSTOM_BLOCK=$(awk ' + /^[[:space:]]*custom_block:[[:space:]]*\|/ { + # Determine the indentation of the key itself + match($0, /^[[:space:]]*/); key_indent = RLENGTH + capturing = 1 + next + } + capturing { + # Lines in the block must be indented more than the key + match($0, /^[[:space:]]*/); line_indent = RLENGTH + if (line_indent > key_indent && $0 !~ /^[[:space:]]*$/ || (capturing && $0 ~ /^[[:space:]]*$/)) { + if (line_indent > key_indent || $0 ~ /^[[:space:]]*$/) { + print + } else { + exit + } + } else { + exit + } + } +' "$GORELEASER") + +if [ -z "$CUSTOM_BLOCK" ]; then + fail "rendered-cask: custom_block not found or empty in .goreleaser.yaml" +fi + +echo "$CUSTOM_BLOCK" | grep -q 'postflight_steps do' || \ + fail "rendered-cask: custom_block is missing 'postflight_steps do' (new Homebrew DSL)" + +# Check that legacy 'postflight do' (without _steps) is absent. +# Use word-boundary matching: 'postflight do' but NOT 'postflight_steps do'. +if echo "$CUSTOM_BLOCK" | grep -qP '(?/dev/null; then + # Perl regex available — use it for precise matching + if echo "$CUSTOM_BLOCK" | grep -P '^\s*postflight\s+do' | grep -qvP 'postflight_steps'; then + fail "rendered-cask: custom_block contains legacy 'postflight do' (should be 'postflight_steps do')" + fi +else + # Fallback: check that 'postflight do' only appears as 'postflight_steps do' + POSTFLIGHT_LINES=$(echo "$CUSTOM_BLOCK" | grep 'postflight.*do' | grep -cv 'postflight_steps' || true) + if [ "$POSTFLIGHT_LINES" -gt 0 ]; then + fail "rendered-cask: custom_block contains legacy 'postflight do' (should be 'postflight_steps do')" + fi +fi + +echo "$CUSTOM_BLOCK" | grep -q '#{staged_path}' || \ + fail "rendered-cask: custom_block is missing Ruby interpolation '#{staged_path}'" + +echo "PASS: Rendered-cask regression (postflight_steps DSL validated from .goreleaser.yaml)" echo "PASS: Homebrew cask integrity regression suite" diff --git a/openspec/changes/fix-cask-postflight-steps/design.md b/openspec/changes/fix-cask-postflight-steps/design.md new file mode 100644 index 0000000..bb3a05b --- /dev/null +++ b/openspec/changes/fix-cask-postflight-steps/design.md @@ -0,0 +1,90 @@ +## Context + +Homebrew 7.0 deprecated the `postflight do ... end` cask stanza in favor +of the declarative `postflight_steps` DSL. The deprecation will become a +hard error after 2027-12-11. + +GoReleaser generates the Homebrew cask from `.goreleaser.yaml`. The +`hooks.post.install` key maps to the deprecated `postflight do` block. +GoReleaser does not yet support native `postflight_steps` generation +(tracking: goreleaser/goreleaser#6873), but the `custom_block` key injects +arbitrary Ruby verbatim into the generated cask. + +The existing test infrastructure (`patch-homebrew-cask_test.sh`) validates +SHA patching logic and fixture integrity but does not exercise the +goreleaser configuration's cask stanza content. A reversion of the +`custom_block` to legacy `hooks.post.install` would pass all existing +tests. + +## Goals / Non-Goals + +### Goals +- Replace `hooks.post.install` with `custom_block` containing + `postflight_steps` DSL +- Update the test fixture to match the new DSL +- Add a deterministic regression test that validates the `custom_block` + content against three invariants: `postflight_steps do` present, legacy + `postflight do` absent, `#{staged_path}` Ruby interpolation intact +- All tests pass without network access or goreleaser binary + +### Non-Goals +- Native GoReleaser `postflight_steps` support (blocked on upstream) +- Changes to quarantine removal behavior +- Changes to Go source code +- Changes to the job graph, permissions, or secrets + +## Decisions + +**D1: Use `custom_block` as the migration vehicle.** GoReleaser's +`custom_block` injects content verbatim into the generated cask. This is +the documented escape hatch for DSL features that GoReleaser does not yet +model natively. A comment above the stanza tracks the upstream issue for +future migration when native support ships. + +**D2: Validate the goreleaser config, not the rendered cask.** Since the +`custom_block` is injected verbatim, its content in `.goreleaser.yaml` is +byte-identical to what appears in the generated cask. Extracting and +validating it at test time is equivalent to validating the rendered output, +without requiring goreleaser or network access. This satisfies the +"deterministic, no network" constraint. + +**D3: Use awk for `custom_block` extraction.** The `custom_block` value in +`.goreleaser.yaml` is a YAML literal block scalar (`|`). Its content starts +on the line after `custom_block: |` and continues while lines are indented +deeper than the `custom_block` key. Awk can reliably extract this without +a YAML parser, avoiding new dependencies. The extraction is validated by +the assertions that follow it. + +**D4: Three-invariant assertion set.** The regression test checks: +1. `postflight_steps do` is present (new DSL adopted) +2. `postflight do` without `_steps` is absent (legacy form removed) +3. `#{staged_path}` is present (Ruby interpolation, not Go template + `{{staged_path}}` or literal text) + +These three checks cover the complete DSL transition. If any is violated, +the cask will either emit deprecation warnings, fail on Homebrew 7.0+, +or target a nonexistent path at install time. + +**D5: Add to existing test file.** The rendered-cask test is added to +`patch-homebrew-cask_test.sh` as a separate section. It validates the +goreleaser config that feeds the patcher's fixture, keeping all cask +integrity tests in one file and one CI step. + +## Risks / Trade-offs + +**Risk: `custom_block` extraction relies on YAML indentation.** The awk +extractor assumes the literal block scalar follows standard YAML +indentation rules. If `.goreleaser.yaml` is reformatted with non-standard +indentation, the extraction may fail. This is mitigated by the subsequent +assertions: if extraction produces garbage, the assertions fail. + +**Risk: GoReleaser changes `custom_block` semantics.** If a future +GoReleaser version wraps `custom_block` content in a method or modifies +whitespace, the cask output may diverge from the raw YAML content. The +tracking comment and upstream issue reference (goreleaser/goreleaser#6873) +flag this for future migration. + +**Trade-off: awk over YAML parser.** A YAML parser would be more robust +but would require a new dependency (Python/PyYAML in CI or a Go YAML +library). The awk approach is dependency-free and sufficient for the +literal block scalar pattern used here. diff --git a/openspec/changes/fix-cask-postflight-steps/proposal.md b/openspec/changes/fix-cask-postflight-steps/proposal.md new file mode 100644 index 0000000..74b316a --- /dev/null +++ b/openspec/changes/fix-cask-postflight-steps/proposal.md @@ -0,0 +1,101 @@ +## Why + +`brew install unbound-force/tap/replicator` emits repeated deprecation +warnings on Homebrew 7.0+: + +``` +Warning: Calling `postflight` is deprecated! Use `postflight_steps` instead. +``` + +The `postflight` stanza will become a hard error after 2027-12-11, at which +point the cask will fail to install entirely. + +The root cause is `.goreleaser.yaml`: the `hooks.post.install` key emits a +`postflight do` block in the generated cask. Homebrew 7.0 replaced +`postflight` with the declarative `postflight_steps` DSL. + +GoReleaser does not yet have native `postflight_steps` support (tracking: +goreleaser/goreleaser#6873). The `custom_block` escape hatch is available +and injects content verbatim into the generated cask. + +Fixes: https://github.com/unbound-force/replicator/issues/111 + +## What Changes + +Two changes to the release configuration and one to the test fixture: + +1. **Replace deprecated hooks.** Remove `hooks.post.install` from + `.goreleaser.yaml` and replace it with a `custom_block` containing the + `postflight_steps` DSL (`on_macos do`, `run`, `#{staged_path}`). +2. **Update test fixture.** Update + `.github/scripts/testdata/replicator-v0.5.0.rb` to use + `postflight_steps do`, `on_macos do`, `run`, and `#{staged_path}`. +3. **Add rendered-cask regression test.** Add a deterministic test case + that extracts the `custom_block` from `.goreleaser.yaml` and verifies + `postflight_steps do` is present, legacy `postflight do` is absent, + and `#{staged_path}` remains valid Ruby interpolation. + +## Capabilities + +### New Capabilities +- `postflight_steps cask stanza`: Homebrew cask uses the new declarative + `postflight_steps` DSL instead of the deprecated `postflight` block. +- `rendered-cask regression test`: Deterministic test that validates the + goreleaser configuration emits correct Homebrew syntax without network + access. + +### Modified Capabilities +- `quarantine removal`: Unchanged behavior (still removes + `com.apple.quarantine` from the replicator binary on macOS); only the + Homebrew DSL syntax changes. + +### Removed Capabilities +- None + +## Impact + +- **Files**: `.goreleaser.yaml` (replace `hooks.post.install` with + `custom_block`), `.github/scripts/testdata/replicator-v0.5.0.rb` + (update fixture stanza syntax), + `.github/scripts/patch-homebrew-cask_test.sh` (add rendered-cask + regression test). +- **Users**: No functional change to quarantine removal behavior. + Deprecation warnings disappear on Homebrew 7.0+. Future-proofs + against 2027-12-11 hard error. +- **No Go source code changes.** Only release configuration and CI test + infrastructure are affected. + +## Constitution Alignment + +### I. Autonomous Collaboration + +**Assessment**: N/A + +No MCP tools, tool output shapes, or inter-agent communication paths are +affected. This change modifies release configuration only. + +### II. Composability First + +**Assessment**: PASS + +Replicator MUST be independently installable via Homebrew. A deprecated +stanza that will become a hard error blocks the distribution channel. +This change restores clean installation on Homebrew 7.0+ and prevents +future breakage. + +### III. Observable Quality + +**Assessment**: PASS + +The regression test deterministically validates the goreleaser +configuration emits correct Homebrew syntax. If the `custom_block` is +reverted to legacy `postflight`, the test catches it in pull-request CI. + +### IV. Testability + +**Assessment**: PASS + +The rendered-cask test extracts the `custom_block` from `.goreleaser.yaml` +at test time, requires no network access, no goreleaser binary, and no +Ruby interpreter. It verifies the DSL transition and Ruby interpolation +syntax deterministically. diff --git a/openspec/changes/fix-cask-postflight-steps/specs/coverage-strategy.md b/openspec/changes/fix-cask-postflight-steps/specs/coverage-strategy.md new file mode 100644 index 0000000..6c6906a --- /dev/null +++ b/openspec/changes/fix-cask-postflight-steps/specs/coverage-strategy.md @@ -0,0 +1,45 @@ +## Coverage Strategy + +### What is being tested + +The Homebrew cask postflight migration from `postflight do` (deprecated) +to `postflight_steps do` (Homebrew 7.0+ declarative DSL). + +### Test surface + +| Invariant | What it catches | How it is tested | +|-----------|----------------|-----------------| +| `postflight_steps do` present in `custom_block` | Missing DSL adoption | Grep extracted `custom_block` for literal `postflight_steps do` | +| `postflight do` (without `_steps`) absent from `custom_block` | Legacy stanza reversion | Negative grep: `postflight do` must NOT appear unless preceded by `_steps` | +| `#{staged_path}` present in `custom_block` | Ruby interpolation syntax intact | Grep for literal `#{staged_path}` — ensures neither Go template `{{staged_path}}` nor plain text was substituted | + +### Test approach + +**Deterministic rendered-cask test:** +- Extracts the `custom_block` literal block scalar from `.goreleaser.yaml` + using awk (no external YAML parser, no network access, no goreleaser + binary) +- Asserts all three invariants against the extracted content +- Fails if `custom_block` is not found or is empty (catches accidental + removal) + +**Existing fixture test (unchanged):** +- The happy-path case in `patch-homebrew-cask_test.sh` already asserts + that `#{staged_path}` survives SHA patching (added in iteration 1) +- The fixture `replicator-v0.5.0.rb` uses `postflight_steps do` and + serves as the expected output shape + +### What is NOT tested + +- Actual GoReleaser rendering (requires goreleaser binary + network) +- Homebrew cask installation (requires macOS + Homebrew) +- Notarization/signing interaction (tested by existing `sign-macos` job) + +### Regression scenarios + +| Scenario | Expected result | +|----------|----------------| +| `custom_block` reverted to `hooks.post.install` | Test fails: `custom_block` not found | +| `postflight_steps do` replaced with `postflight do` | Test fails: legacy stanza detected | +| `#{staged_path}` replaced with `{{staged_path}}` | Test fails: Ruby interpolation missing | +| `custom_block` removed entirely | Test fails: no content extracted | diff --git a/openspec/changes/fix-cask-postflight-steps/tasks.md b/openspec/changes/fix-cask-postflight-steps/tasks.md new file mode 100644 index 0000000..bd0a1f0 --- /dev/null +++ b/openspec/changes/fix-cask-postflight-steps/tasks.md @@ -0,0 +1,51 @@ + + +## 1. Replace deprecated Homebrew cask stanza + +- [x] 1.1 Remove `hooks.post.install` from `.goreleaser.yaml` and replace + with `custom_block` containing `postflight_steps` DSL using `on_macos do`, + `run`, and `#{staged_path}` Ruby interpolation. + +- [x] 1.2 Add maintenance comment above `custom_block` noting that native + GoReleaser `postflight_steps` support is not yet available, with tracking + reference to goreleaser/goreleaser#6873. + +## 2. Update test fixture + +- [x] 2.1 Update `.github/scripts/testdata/replicator-v0.5.0.rb`: replace + `postflight do` with `postflight_steps do`, `if OS.mac?` with + `on_macos do`, `system_command` with `run`, and verify `#{staged_path}` + is preserved. + +## 3. Add rendered-cask regression test + +- [x] 3.1 Add deterministic test case to + `.github/scripts/patch-homebrew-cask_test.sh` that extracts the + `custom_block` from `.goreleaser.yaml` and verifies: + (a) `postflight_steps do` is present, + (b) legacy `postflight do` is absent, + (c) `#{staged_path}` remains valid Ruby interpolation. + +- [x] 3.2 Add assertion to existing happy-path test case verifying + `#{staged_path}` survives SHA patching. + +## 4. Verification + +- [x] 4.1 Run the Homebrew cask integrity regression suite. + +- [x] 4.2 Run `go vet ./...` and `go test ./... -count=1 -race`. + +- [x] 4.3 Verify constitution alignment: Composability First (Homebrew + install works without deprecation warnings), Observable Quality + (regression test catches reversion), Testability (deterministic, + no network, no goreleaser binary). From 65eda5b47b5e7e24e739215ec1110a5ed2d66403 Mon Sep 17 00:00:00 2001 From: fullsend-fix <278716306+fullsend-ai-coder[bot]@users.noreply.github.com> Date: Tue, 29 Sep 2026 11:36:02 +0000 Subject: [PATCH 06/11] fix: add changelog entry and decouple Perl regex probe from content - Add Fixed entry under ## Unreleased in CHANGELOG.md for the Homebrew cask postflight_steps migration (DR-003). References #111. - Replace content-dependent Perl regex detection with a content- independent probe (`echo "probe" | grep -qP 'probe'`) so the grep -P availability check does not depend on the custom_block content shape. Addresses #112 --- .github/scripts/patch-homebrew-cask_test.sh | 10 ++++++---- CHANGELOG.md | 6 ++++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/.github/scripts/patch-homebrew-cask_test.sh b/.github/scripts/patch-homebrew-cask_test.sh index c312573..5139970 100755 --- a/.github/scripts/patch-homebrew-cask_test.sh +++ b/.github/scripts/patch-homebrew-cask_test.sh @@ -154,10 +154,12 @@ echo "$CUSTOM_BLOCK" | grep -q 'postflight_steps do' || \ fail "rendered-cask: custom_block is missing 'postflight_steps do' (new Homebrew DSL)" # Check that legacy 'postflight do' (without _steps) is absent. -# Use word-boundary matching: 'postflight do' but NOT 'postflight_steps do'. -if echo "$CUSTOM_BLOCK" | grep -qP '(?/dev/null; then - # Perl regex available — use it for precise matching - if echo "$CUSTOM_BLOCK" | grep -P '^\s*postflight\s+do' | grep -qvP 'postflight_steps'; then +# Detect Perl regex support with a content-independent probe, then use +# the appropriate matching strategy. +if echo "probe" | grep -qP 'probe' 2>/dev/null; then + # Perl regex available — use negative lookbehind for precise matching + if echo "$CUSTOM_BLOCK" | grep -qP '^\s*postflight\s+do' && \ + echo "$CUSTOM_BLOCK" | grep -P '^\s*postflight\s+do' | grep -qvP 'postflight_steps'; then fail "rendered-cask: custom_block contains legacy 'postflight do' (should be 'postflight_steps do')" fi else diff --git a/CHANGELOG.md b/CHANGELOG.md index d7a80f0..309472a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,12 @@ this project adheres to [Semantic Versioning](https://semver.org/). ## Unreleased ### Fixed +- Homebrew cask `postflight` deprecation warning on Homebrew 7.0+. Replaced + the deprecated `postflight do` stanza with the new `postflight_steps` + declarative DSL in the GoReleaser cask configuration via `custom_block`. + Eliminates deprecation warnings during `brew install` and prevents a hard + error after 2027-12-11. + (Fixes [#111](https://github.com/unbound-force/replicator/issues/111)) - Homebrew cask published with checksums on the wrong stanza. The `publish-cask` job scanned forward from the `darwin_arm64` marker for the next `sha256` line, but the cask emits each stanza's `sha256` before its From d5851472c490796ee800b159fb2d80efb94edfad Mon Sep 17 00:00:00 2001 From: fullsend-fix <278716306+fullsend-ai-coder[bot]@users.noreply.github.com> Date: Tue, 29 Sep 2026 13:10:20 +0000 Subject: [PATCH 07/11] fix: remove dead code and extra PASS echo in cask test Remove unreachable else branch in awk custom_block extractor (the outer condition guarantees the inner condition is always true) and remove section-level PASS echo to match the file's convention of silent-on-success with a single suite-level PASS. Addresses #112 --- .github/scripts/patch-homebrew-cask_test.sh | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/.github/scripts/patch-homebrew-cask_test.sh b/.github/scripts/patch-homebrew-cask_test.sh index 5139970..e86051e 100755 --- a/.github/scripts/patch-homebrew-cask_test.sh +++ b/.github/scripts/patch-homebrew-cask_test.sh @@ -134,12 +134,8 @@ CUSTOM_BLOCK=$(awk ' capturing { # Lines in the block must be indented more than the key match($0, /^[[:space:]]*/); line_indent = RLENGTH - if (line_indent > key_indent && $0 !~ /^[[:space:]]*$/ || (capturing && $0 ~ /^[[:space:]]*$/)) { - if (line_indent > key_indent || $0 ~ /^[[:space:]]*$/) { - print - } else { - exit - } + if (line_indent > key_indent || $0 ~ /^[[:space:]]*$/) { + print } else { exit } @@ -173,5 +169,4 @@ fi echo "$CUSTOM_BLOCK" | grep -q '#{staged_path}' || \ fail "rendered-cask: custom_block is missing Ruby interpolation '#{staged_path}'" -echo "PASS: Rendered-cask regression (postflight_steps DSL validated from .goreleaser.yaml)" echo "PASS: Homebrew cask integrity regression suite" From d02160492a8fa553e416ecad5e056fbfcdb24098 Mon Sep 17 00:00:00 2001 From: Yvonne Devlin Date: Fri, 2 Oct 2026 15:11:57 +0000 Subject: [PATCH 08/11] fix(ci): validate rendered Homebrew cask --- .github/scripts/patch-homebrew-cask_test.sh | 42 +++++++-------- .github/workflows/ci.yml | 11 +++- .../fix-cask-postflight-steps/design.md | 20 ++++--- .../specs/coverage-strategy.md | 45 ---------------- .../specs/homebrew-cask/spec.md | 52 +++++++++++++++++++ .../fix-cask-postflight-steps/tasks.md | 10 ++++ 6 files changed, 100 insertions(+), 80 deletions(-) delete mode 100644 openspec/changes/fix-cask-postflight-steps/specs/coverage-strategy.md create mode 100644 openspec/changes/fix-cask-postflight-steps/specs/homebrew-cask/spec.md diff --git a/.github/scripts/patch-homebrew-cask_test.sh b/.github/scripts/patch-homebrew-cask_test.sh index e86051e..1836353 100755 --- a/.github/scripts/patch-homebrew-cask_test.sh +++ b/.github/scripts/patch-homebrew-cask_test.sh @@ -4,6 +4,7 @@ set -euo pipefail SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd) PATCHER="$SCRIPT_DIR/patch-homebrew-cask.sh" FIXTURE="$SCRIPT_DIR/testdata/replicator-v0.5.0.rb" +RENDERED_CASK=${1:-} ARCHIVE_NAME="replicator_0.5.0_darwin_arm64.tar.gz" LINUX_ARM64_SHA="d35cf51192f4bc3eb92d32c2a63304fdbc561243a2bb8e406d0a5c7f9d1a83f1" @@ -142,31 +143,26 @@ CUSTOM_BLOCK=$(awk ' } ' "$GORELEASER") -if [ -z "$CUSTOM_BLOCK" ]; then - fail "rendered-cask: custom_block not found or empty in .goreleaser.yaml" -fi +assert_current_postflight_dsl() { + local cask=$1 + local source=$2 -echo "$CUSTOM_BLOCK" | grep -q 'postflight_steps do' || \ - fail "rendered-cask: custom_block is missing 'postflight_steps do' (new Homebrew DSL)" - -# Check that legacy 'postflight do' (without _steps) is absent. -# Detect Perl regex support with a content-independent probe, then use -# the appropriate matching strategy. -if echo "probe" | grep -qP 'probe' 2>/dev/null; then - # Perl regex available — use negative lookbehind for precise matching - if echo "$CUSTOM_BLOCK" | grep -qP '^\s*postflight\s+do' && \ - echo "$CUSTOM_BLOCK" | grep -P '^\s*postflight\s+do' | grep -qvP 'postflight_steps'; then - fail "rendered-cask: custom_block contains legacy 'postflight do' (should be 'postflight_steps do')" - fi -else - # Fallback: check that 'postflight do' only appears as 'postflight_steps do' - POSTFLIGHT_LINES=$(echo "$CUSTOM_BLOCK" | grep 'postflight.*do' | grep -cv 'postflight_steps' || true) - if [ "$POSTFLIGHT_LINES" -gt 0 ]; then - fail "rendered-cask: custom_block contains legacy 'postflight do' (should be 'postflight_steps do')" + [ -s "$cask" ] || fail "$source: cask is missing or empty" + grep -q 'postflight_steps do' "$cask" || \ + fail "$source: cask is missing 'postflight_steps do' (new Homebrew DSL)" + if grep -Eq '^[[:space:]]*postflight[[:space:]]+do' "$cask"; then + fail "$source: cask contains legacy 'postflight do' (should be 'postflight_steps do')" fi -fi + grep -q '#{staged_path}' "$cask" || \ + fail "$source: cask is missing Ruby interpolation '#{staged_path}'" +} + +CONFIG_CASK="$WORK/custom-block.rb" +printf '%s\n' "$CUSTOM_BLOCK" > "$CONFIG_CASK" +assert_current_postflight_dsl "$CONFIG_CASK" "goreleaser custom_block" -echo "$CUSTOM_BLOCK" | grep -q '#{staged_path}' || \ - fail "rendered-cask: custom_block is missing Ruby interpolation '#{staged_path}'" +if [ -n "$RENDERED_CASK" ]; then + assert_current_postflight_dsl "$RENDERED_CASK" "rendered cask" +fi echo "PASS: Homebrew cask integrity regression suite" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7f8c2f5..5241ac6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,8 +39,17 @@ jobs: go install golang.org/x/vuln/cmd/govulncheck@3e6f44f962742443c11ae2261f02e0c917aeb2bc # v1.5.0 govulncheck ./... + - name: Install GoReleaser + uses: goreleaser/goreleaser-action@e435ccd777264be153ace6237001ef4d979d3a7a # v6.4.0 + with: + distribution: goreleaser + version: v2.18.2 + + - name: Render Homebrew cask + run: goreleaser release --snapshot --clean --skip=announce + - name: Test Homebrew cask integrity patching - run: .github/scripts/patch-homebrew-cask_test.sh + run: .github/scripts/patch-homebrew-cask_test.sh dist/homebrew/Casks/replicator.rb - name: Test run: go test ./... -count=1 -race -coverprofile=coverage.out diff --git a/openspec/changes/fix-cask-postflight-steps/design.md b/openspec/changes/fix-cask-postflight-steps/design.md index bb3a05b..1b6eb40 100644 --- a/openspec/changes/fix-cask-postflight-steps/design.md +++ b/openspec/changes/fix-cask-postflight-steps/design.md @@ -41,12 +41,11 @@ the documented escape hatch for DSL features that GoReleaser does not yet model natively. A comment above the stanza tracks the upstream issue for future migration when native support ships. -**D2: Validate the goreleaser config, not the rendered cask.** Since the -`custom_block` is injected verbatim, its content in `.goreleaser.yaml` is -byte-identical to what appears in the generated cask. Extracting and -validating it at test time is equivalent to validating the rendered output, -without requiring goreleaser or network access. This satisfies the -"deterministic, no network" constraint. +**D2: Validate both the GoReleaser configuration and rendered cask.** The +configuration assertions fail quickly for accidental DSL changes. CI also +renders a snapshot with a pinned GoReleaser version and passes the generated +cask to the regression suite. This verifies that GoReleaser preserves the +custom block when it produces the release artifact. **D3: Use awk for `custom_block` extraction.** The `custom_block` value in `.goreleaser.yaml` is a YAML literal block scalar (`|`). Its content starts @@ -78,11 +77,10 @@ indentation rules. If `.goreleaser.yaml` is reformatted with non-standard indentation, the extraction may fail. This is mitigated by the subsequent assertions: if extraction produces garbage, the assertions fail. -**Risk: GoReleaser changes `custom_block` semantics.** If a future -GoReleaser version wraps `custom_block` content in a method or modifies -whitespace, the cask output may diverge from the raw YAML content. The -tracking comment and upstream issue reference (goreleaser/goreleaser#6873) -flag this for future migration. +**Risk: GoReleaser changes `custom_block` semantics.** A pinned GoReleaser +version renders the cask in every pull request, so CI fails if its output no +longer contains the required DSL. The tracking comment and upstream issue +reference (goreleaser/goreleaser#6873) flag this for future migration. **Trade-off: awk over YAML parser.** A YAML parser would be more robust but would require a new dependency (Python/PyYAML in CI or a Go YAML diff --git a/openspec/changes/fix-cask-postflight-steps/specs/coverage-strategy.md b/openspec/changes/fix-cask-postflight-steps/specs/coverage-strategy.md deleted file mode 100644 index 6c6906a..0000000 --- a/openspec/changes/fix-cask-postflight-steps/specs/coverage-strategy.md +++ /dev/null @@ -1,45 +0,0 @@ -## Coverage Strategy - -### What is being tested - -The Homebrew cask postflight migration from `postflight do` (deprecated) -to `postflight_steps do` (Homebrew 7.0+ declarative DSL). - -### Test surface - -| Invariant | What it catches | How it is tested | -|-----------|----------------|-----------------| -| `postflight_steps do` present in `custom_block` | Missing DSL adoption | Grep extracted `custom_block` for literal `postflight_steps do` | -| `postflight do` (without `_steps`) absent from `custom_block` | Legacy stanza reversion | Negative grep: `postflight do` must NOT appear unless preceded by `_steps` | -| `#{staged_path}` present in `custom_block` | Ruby interpolation syntax intact | Grep for literal `#{staged_path}` — ensures neither Go template `{{staged_path}}` nor plain text was substituted | - -### Test approach - -**Deterministic rendered-cask test:** -- Extracts the `custom_block` literal block scalar from `.goreleaser.yaml` - using awk (no external YAML parser, no network access, no goreleaser - binary) -- Asserts all three invariants against the extracted content -- Fails if `custom_block` is not found or is empty (catches accidental - removal) - -**Existing fixture test (unchanged):** -- The happy-path case in `patch-homebrew-cask_test.sh` already asserts - that `#{staged_path}` survives SHA patching (added in iteration 1) -- The fixture `replicator-v0.5.0.rb` uses `postflight_steps do` and - serves as the expected output shape - -### What is NOT tested - -- Actual GoReleaser rendering (requires goreleaser binary + network) -- Homebrew cask installation (requires macOS + Homebrew) -- Notarization/signing interaction (tested by existing `sign-macos` job) - -### Regression scenarios - -| Scenario | Expected result | -|----------|----------------| -| `custom_block` reverted to `hooks.post.install` | Test fails: `custom_block` not found | -| `postflight_steps do` replaced with `postflight do` | Test fails: legacy stanza detected | -| `#{staged_path}` replaced with `{{staged_path}}` | Test fails: Ruby interpolation missing | -| `custom_block` removed entirely | Test fails: no content extracted | diff --git a/openspec/changes/fix-cask-postflight-steps/specs/homebrew-cask/spec.md b/openspec/changes/fix-cask-postflight-steps/specs/homebrew-cask/spec.md new file mode 100644 index 0000000..e8e45f6 --- /dev/null +++ b/openspec/changes/fix-cask-postflight-steps/specs/homebrew-cask/spec.md @@ -0,0 +1,52 @@ +## ADDED Requirements + +### Requirement: Homebrew postflight steps + +The GoReleaser Homebrew cask configuration MUST emit the `postflight_steps` +DSL. The generated cask MUST use `on_macos do` to remove the quarantine +attribute from the staged `replicator` binary, and MUST preserve Ruby +`#{staged_path}` interpolation. + +#### Scenario: Generated cask uses the current Homebrew DSL +- **GIVEN** the release configuration contains the Homebrew cask definition +- **WHEN** GoReleaser renders a snapshot cask +- **THEN** the cask MUST contain `postflight_steps do` and `on_macos do` +- **AND** it MUST contain `#{staged_path}/replicator` + +#### Scenario: Generated cask reintroduces the legacy stanza +- **GIVEN** a GoReleaser render produces a cask containing `postflight do` +- **WHEN** the cask regression suite runs +- **THEN** the suite MUST fail before the change can merge + +### Requirement: Rendered Homebrew cask validation + +The `Build and Test` job MUST render the Homebrew cask with the pinned +GoReleaser version before publishing a release. The cask regression suite MUST +validate the rendered cask contains `postflight_steps do`, does not contain the +legacy `postflight do` stanza, and preserves `#{staged_path}` Ruby +interpolation. + +#### Scenario: Rendered cask uses the current Homebrew DSL +- **GIVEN** the release configuration contains the Homebrew cask definition +- **WHEN** the `Build and Test` job runs the GoReleaser snapshot render +- **THEN** the regression suite MUST validate the generated cask contains + `postflight_steps do` +- **AND** it MUST validate the generated cask retains `#{staged_path}` + +#### Scenario: Missing rendered cask +- **GIVEN** the GoReleaser snapshot command does not produce the expected cask +- **WHEN** the cask regression suite runs +- **THEN** the suite MUST fail rather than validating only the source + configuration + +### Requirement: Configuration-level cask validation + +The repository MUST retain dependency-free checks for the GoReleaser +`custom_block` source configuration so accidental DSL regressions fail before +the snapshot-rendering step. + +#### Scenario: custom_block removes the current DSL +- **GIVEN** the GoReleaser `custom_block` is empty or removes + `postflight_steps do` +- **WHEN** the cask regression suite runs +- **THEN** the suite MUST fail diff --git a/openspec/changes/fix-cask-postflight-steps/tasks.md b/openspec/changes/fix-cask-postflight-steps/tasks.md index bd0a1f0..fa810ae 100644 --- a/openspec/changes/fix-cask-postflight-steps/tasks.md +++ b/openspec/changes/fix-cask-postflight-steps/tasks.md @@ -49,3 +49,13 @@ install works without deprecation warnings), Observable Quality (regression test catches reversion), Testability (deterministic, no network, no goreleaser binary). + +## 5. Validate GoReleaser output + +- [x] 5.1 Add rendered-cask requirements and scenarios to the coverage + strategy delta specification. + +- [x] 5.2 Install a pinned GoReleaser release in `Build and Test`, render a + snapshot cask, and validate its postflight DSL in the regression suite. + +- [x] 5.3 Run OpenSpec validation and the affected cask regression suite. From bd186e26d9858cb443cb8e2e63b2e6d9fed063f4 Mon Sep 17 00:00:00 2001 From: Yvonne Devlin Date: Fri, 2 Oct 2026 15:29:56 +0000 Subject: [PATCH 09/11] fix(ci): provide GoReleaser action arguments --- .github/scripts/patch-homebrew-cask_test.sh | 13 +++++++++++++ .github/workflows/ci.yml | 1 + .../specs/homebrew-cask/spec.md | 9 +++++++++ openspec/changes/fix-cask-postflight-steps/tasks.md | 6 ++++++ 4 files changed, 29 insertions(+) diff --git a/.github/scripts/patch-homebrew-cask_test.sh b/.github/scripts/patch-homebrew-cask_test.sh index 1836353..e481f30 100755 --- a/.github/scripts/patch-homebrew-cask_test.sh +++ b/.github/scripts/patch-homebrew-cask_test.sh @@ -118,9 +118,22 @@ assert_failure_preserves_cask "mismatched manifest entry" # emits correct Homebrew postflight_steps DSL without running goreleaser. # --------------------------------------------------------------------------- GORELEASER="$SCRIPT_DIR/../../.goreleaser.yaml" +CI_WORKFLOW="$SCRIPT_DIR/../../.github/workflows/ci.yml" if [ ! -f "$GORELEASER" ]; then fail "rendered-cask: .goreleaser.yaml not found at $GORELEASER" fi +if [ ! -f "$CI_WORKFLOW" ]; then + fail "rendered-cask: CI workflow not found at $CI_WORKFLOW" +fi + +# goreleaser-action executes the supplied args while installing GoReleaser. +# A version query keeps setup separate from the snapshot render below. +awk ' + /uses: goreleaser\/goreleaser-action@/ { in_action = 1; next } + in_action && /args: --version/ { found = 1 } + in_action && /^[[:space:]]*-[[:space:]]/ { in_action = 0 } + END { exit !found } +' "$CI_WORKFLOW" || fail "rendered-cask: GoReleaser action must receive args: --version" # Extract the custom_block literal block scalar value from .goreleaser.yaml. # The block starts on the line after "custom_block: |" and continues while diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5241ac6..958bfee 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,6 +42,7 @@ jobs: - name: Install GoReleaser uses: goreleaser/goreleaser-action@e435ccd777264be153ace6237001ef4d979d3a7a # v6.4.0 with: + args: --version distribution: goreleaser version: v2.18.2 diff --git a/openspec/changes/fix-cask-postflight-steps/specs/homebrew-cask/spec.md b/openspec/changes/fix-cask-postflight-steps/specs/homebrew-cask/spec.md index e8e45f6..d6ee293 100644 --- a/openspec/changes/fix-cask-postflight-steps/specs/homebrew-cask/spec.md +++ b/openspec/changes/fix-cask-postflight-steps/specs/homebrew-cask/spec.md @@ -26,6 +26,9 @@ validate the rendered cask contains `postflight_steps do`, does not contain the legacy `postflight do` stanza, and preserves `#{staged_path}` Ruby interpolation. +The GoReleaser setup action MUST receive arguments that allow it to complete +before the separate snapshot-rendering command runs. + #### Scenario: Rendered cask uses the current Homebrew DSL - **GIVEN** the release configuration contains the Homebrew cask definition - **WHEN** the `Build and Test` job runs the GoReleaser snapshot render @@ -39,6 +42,12 @@ interpolation. - **THEN** the suite MUST fail rather than validating only the source configuration +#### Scenario: GoReleaser setup action completes +- **GIVEN** the `Build and Test` job installs its pinned GoReleaser version +- **WHEN** the setup action runs +- **THEN** it MUST receive a non-rendering argument and complete before the + snapshot-rendering command runs + ### Requirement: Configuration-level cask validation The repository MUST retain dependency-free checks for the GoReleaser diff --git a/openspec/changes/fix-cask-postflight-steps/tasks.md b/openspec/changes/fix-cask-postflight-steps/tasks.md index fa810ae..03cdec5 100644 --- a/openspec/changes/fix-cask-postflight-steps/tasks.md +++ b/openspec/changes/fix-cask-postflight-steps/tasks.md @@ -59,3 +59,9 @@ snapshot cask, and validate its postflight DSL in the regression suite. - [x] 5.3 Run OpenSpec validation and the affected cask regression suite. + +## 6. Repair CI GoReleaser setup + +- [x] 6.1 Add a regression assertion that the GoReleaser action receives its + required `args` input, then configure the action with a non-rendering + version argument so the snapshot-rendering step can run. From f3798f84bcb81d3f7aae036da0cd81985e6b6d84 Mon Sep 17 00:00:00 2001 From: Yvonne Devlin Date: Fri, 2 Oct 2026 15:48:53 +0000 Subject: [PATCH 10/11] fix(ci): use install-only mode for GoReleaser action The goreleaser-action with args: --version and install-only: false extracts the binary to a temp directory that does not persist on PATH. Subsequent steps fail with 'command not found'. Switch to install-only: true so the binary remains available for the snapshot render step. Update the regression assertion and spec artifacts to match. --- .github/scripts/patch-homebrew-cask_test.sh | 8 ++++---- .github/workflows/ci.yml | 2 +- .../fix-cask-postflight-steps/specs/homebrew-cask/spec.md | 8 ++++---- openspec/changes/fix-cask-postflight-steps/tasks.md | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/scripts/patch-homebrew-cask_test.sh b/.github/scripts/patch-homebrew-cask_test.sh index e481f30..f5bb00a 100755 --- a/.github/scripts/patch-homebrew-cask_test.sh +++ b/.github/scripts/patch-homebrew-cask_test.sh @@ -126,14 +126,14 @@ if [ ! -f "$CI_WORKFLOW" ]; then fail "rendered-cask: CI workflow not found at $CI_WORKFLOW" fi -# goreleaser-action executes the supplied args while installing GoReleaser. -# A version query keeps setup separate from the snapshot render below. +# goreleaser-action must use install-only mode so the binary is available +# on PATH for subsequent steps (the snapshot render below). awk ' /uses: goreleaser\/goreleaser-action@/ { in_action = 1; next } - in_action && /args: --version/ { found = 1 } + in_action && /install-only: true/ { found = 1 } in_action && /^[[:space:]]*-[[:space:]]/ { in_action = 0 } END { exit !found } -' "$CI_WORKFLOW" || fail "rendered-cask: GoReleaser action must receive args: --version" +' "$CI_WORKFLOW" || fail "rendered-cask: GoReleaser action must set install-only: true" # Extract the custom_block literal block scalar value from .goreleaser.yaml. # The block starts on the line after "custom_block: |" and continues while diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 958bfee..15a907e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,7 +42,7 @@ jobs: - name: Install GoReleaser uses: goreleaser/goreleaser-action@e435ccd777264be153ace6237001ef4d979d3a7a # v6.4.0 with: - args: --version + install-only: true distribution: goreleaser version: v2.18.2 diff --git a/openspec/changes/fix-cask-postflight-steps/specs/homebrew-cask/spec.md b/openspec/changes/fix-cask-postflight-steps/specs/homebrew-cask/spec.md index d6ee293..d65497a 100644 --- a/openspec/changes/fix-cask-postflight-steps/specs/homebrew-cask/spec.md +++ b/openspec/changes/fix-cask-postflight-steps/specs/homebrew-cask/spec.md @@ -26,8 +26,8 @@ validate the rendered cask contains `postflight_steps do`, does not contain the legacy `postflight do` stanza, and preserves `#{staged_path}` Ruby interpolation. -The GoReleaser setup action MUST receive arguments that allow it to complete -before the separate snapshot-rendering command runs. +The GoReleaser setup action MUST use `install-only` mode so the binary is +available on PATH for subsequent steps. #### Scenario: Rendered cask uses the current Homebrew DSL - **GIVEN** the release configuration contains the Homebrew cask definition @@ -45,8 +45,8 @@ before the separate snapshot-rendering command runs. #### Scenario: GoReleaser setup action completes - **GIVEN** the `Build and Test` job installs its pinned GoReleaser version - **WHEN** the setup action runs -- **THEN** it MUST receive a non-rendering argument and complete before the - snapshot-rendering command runs +- **THEN** it MUST use `install-only: true` so the binary persists on PATH + for the snapshot-rendering step ### Requirement: Configuration-level cask validation diff --git a/openspec/changes/fix-cask-postflight-steps/tasks.md b/openspec/changes/fix-cask-postflight-steps/tasks.md index 03cdec5..ce8fc78 100644 --- a/openspec/changes/fix-cask-postflight-steps/tasks.md +++ b/openspec/changes/fix-cask-postflight-steps/tasks.md @@ -62,6 +62,6 @@ ## 6. Repair CI GoReleaser setup -- [x] 6.1 Add a regression assertion that the GoReleaser action receives its - required `args` input, then configure the action with a non-rendering - version argument so the snapshot-rendering step can run. +- [x] 6.1 Add a regression assertion that the GoReleaser action uses + `install-only: true`, then configure the action accordingly so the + binary persists on PATH for the snapshot-rendering step. From 648e48e8eb6335538c33550b4def5b02e65b8935 Mon Sep 17 00:00:00 2001 From: Yvonne Devlin Date: Fri, 2 Oct 2026 15:57:06 +0000 Subject: [PATCH 11/11] fix(ci): resolve zizmor artipacked and cache-poisoning findings Set persist-credentials: false on actions/checkout to prevent credential leakage through artifacts (artipacked). Disable Go module caching on actions/setup-go to eliminate the cache-poisoning vector flagged by zizmor when goreleaser-action is present in the same job. --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 15a907e..183bf8e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,10 +26,13 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version-file: go.mod + cache: false - name: Vet run: go vet ./...