From 73f2f1f5bece5beb23e3a5ed2c02ceee5ae4be22 Mon Sep 17 00:00:00 2001 From: Jon Langevin Date: Sun, 3 May 2026 05:05:32 -0400 Subject: [PATCH 1/2] fix(release): wrap before.hooks in sh -c for shell builtins/operators MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit goreleaser v2 exec's each hook string via shlex.Split rather than passing it to a shell, so shell builtins (export), operators (&&, ;), and command substitution ($(...)) only work when wrapped in `sh -c "<...>"`. The wfctl strict-contracts hook used `export` and failed v0.9.0's release-r2 (run 25274980854) with `exec: "export": executable file not found in $PATH`. The two sed hooks above only worked accidentally: goreleaser passed everything past the first whitespace as positional args to `sed`, which silently absorbs `&&` and `rm` as positional args. The mutations to plugin.json may not actually have been happening as intended in releases — wrapping all three in `sh -c` makes the behaviour match the apparent intent. Pair with #57 — both unblock the v0.9.0 release pipeline. Co-Authored-By: Claude Opus 4.7 (1M context) --- .goreleaser.yaml | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 9b0ef7f..7145e31 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -3,10 +3,19 @@ version: 2 project_name: workflow-plugin-digitalocean before: + # goreleaser v2 exec's each hook string via shlex.Split rather than + # passing it to a shell, so shell builtins (export), shell operators + # (&&, ;), and command substitution ($(...)) only work when wrapped in + # `sh -c "<...>"`. The two sed lines below only worked accidentally: + # goreleaser passed everything past the first whitespace as args to + # `sed`, which silently absorbs `&&` and `rm` as positional args. The + # wfctl-validate line below contains `export` (a builtin) so was the + # first to actually fail; wrapping all three in `sh -c` makes the + # behaviour match the apparent intent. hooks: - - "sed -i.bak 's/\"version\": \".*\"/\"version\": \"{{ .Version }}\"/' plugin.json && rm -f plugin.json.bak" - - "sed -i.bak 's|/releases/download/v[^/]*/|/releases/download/{{ .Tag }}/|g' plugin.json && rm -f plugin.json.bak" - - "export GOPRIVATE=github.com/GoCodeAlone/*; WFCTL_VERSION=$(go list -m github.com/GoCodeAlone/workflow | awk '{print $2}') && GOWORK=off go run github.com/GoCodeAlone/workflow/cmd/wfctl@${WFCTL_VERSION} plugin validate --file plugin.json --strict-contracts" + - sh -c "sed -i.bak 's/\"version\": \".*\"/\"version\": \"{{ .Version }}\"/' plugin.json && rm -f plugin.json.bak" + - sh -c "sed -i.bak 's|/releases/download/v[^/]*/|/releases/download/{{ .Tag }}/|g' plugin.json && rm -f plugin.json.bak" + - sh -c "export GOPRIVATE=github.com/GoCodeAlone/*; WFCTL_VERSION=$(go list -m github.com/GoCodeAlone/workflow | awk '{print $2}') && GOWORK=off go run github.com/GoCodeAlone/workflow/cmd/wfctl@${WFCTL_VERSION} plugin validate --file plugin.json --strict-contracts" # NOTE: removed top-level `after:` hook (introduced in #41). goreleaser # v2 has no top-level `after:` hook — it failed v0.9.0's release at From 87b02d5908248ad5ad4f236f1288f5176fabf277 Mon Sep 17 00:00:00 2001 From: Jon Langevin Date: Sun, 3 May 2026 05:07:03 -0400 Subject: [PATCH 2/2] fix(release): YAML-quote sh -c hooks (URL colons confuse YAML mapping) The prior commit's `sh -c "..."` form was a plain (non-quoted) YAML scalar, so the embedded `:` inside URLs (e.g. https://...) was parsed as YAML mapping syntax. yaml errored: `mapping values are not allowed in this context`. Wrap each hook in YAML double-quotes with proper backslash-escaping of the inner shell double-quotes. Validated locally with `goreleaser/goreleaser:v2.15.4 check`. --- .goreleaser.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 7145e31..f7c0499 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -13,9 +13,9 @@ before: # first to actually fail; wrapping all three in `sh -c` makes the # behaviour match the apparent intent. hooks: - - sh -c "sed -i.bak 's/\"version\": \".*\"/\"version\": \"{{ .Version }}\"/' plugin.json && rm -f plugin.json.bak" - - sh -c "sed -i.bak 's|/releases/download/v[^/]*/|/releases/download/{{ .Tag }}/|g' plugin.json && rm -f plugin.json.bak" - - sh -c "export GOPRIVATE=github.com/GoCodeAlone/*; WFCTL_VERSION=$(go list -m github.com/GoCodeAlone/workflow | awk '{print $2}') && GOWORK=off go run github.com/GoCodeAlone/workflow/cmd/wfctl@${WFCTL_VERSION} plugin validate --file plugin.json --strict-contracts" + - "sh -c \"sed -i.bak 's/\\\"version\\\": \\\".*\\\"/\\\"version\\\": \\\"{{ .Version }}\\\"/' plugin.json && rm -f plugin.json.bak\"" + - "sh -c \"sed -i.bak 's|/releases/download/v[^/]*/|/releases/download/{{ .Tag }}/|g' plugin.json && rm -f plugin.json.bak\"" + - "sh -c \"export GOPRIVATE=github.com/GoCodeAlone/*; WFCTL_VERSION=$(go list -m github.com/GoCodeAlone/workflow | awk '{print $2}') && GOWORK=off go run github.com/GoCodeAlone/workflow/cmd/wfctl@${WFCTL_VERSION} plugin validate --file plugin.json --strict-contracts\"" # NOTE: removed top-level `after:` hook (introduced in #41). goreleaser # v2 has no top-level `after:` hook — it failed v0.9.0's release at