fix(actions): invoke publish.sh via bash, not direct exec#28
Conversation
The previous shape ran the script path as a command. That requires the file's exec bit to be set on the runner's action checkout — which is mostly true but not guaranteed: the runner cloning behaviour can drop the file mode in some configurations, and "permission denied" then surfaces as exit 1 with **no script output**, which is exactly the failure mode the stdnum v0.0.1 dispatch hit. Switching to `bash <path>` removes the dependency: bash reads and interprets the file regardless of its mode. Surfaced by stella/stdnum's release dispatch against v0.0.1 — the script's `chmod +x` was preserved in git (tree mode 100755 confirmed) but the runner-side artifact did not honour it.
There was a problem hiding this comment.
Code Review
This pull request updates the npm-publish-hardened action to invoke the publish.sh script explicitly using bash, which prevents potential execution permission issues in the GitHub Actions runner. A review comment suggests adding the -e flag to the bash command to ensure the script fails immediately on errors, as the shebang line is ignored during explicit invocation.
| # fail with "permission denied" while still surfacing as exit 1 | ||
| # with no script output. `bash <path>` removes that dependency | ||
| # entirely — bash reads the file regardless of its mode. | ||
| run: bash "${{ github.action_path }}/publish.sh" |
There was a problem hiding this comment.
When a script is invoked explicitly via bash <path>, the shebang line (e.g., #!/bin/bash -e) is ignored by the shell. To ensure the action fails immediately if any command within publish.sh fails, it is recommended to explicitly pass the -e flag to the bash command. This is especially important for a "hardened" action to prevent silent failures during the publish process.
run: bash -e "${{ github.action_path }}/publish.sh"|
CC on behalf of @jan-kubica Applied in latest commit. Worth noting that publish.sh's second line is `set -euo pipefail`, so the safety settings are already in effect from the script's perspective — only line 1 (the shebang comment) runs without them, and that's a no-op. But passing the flags to the sub-bash is correct defense-in-depth and aligns with the GitHub Actions runner's own default shell config (`bash --noprofile --norc -e -o pipefail {0}`). |
The shebang's flags are ignored when invoked via `bash <path>`.
publish.sh's second line is `set -euo pipefail`, so the safety
settings are in effect from the script's perspective, but making the
sub-bash flags explicit aligns with the runner's own default shell
config (`bash --noprofile --norc -e -o pipefail {0}`) and removes
the empty-but-uncovered window before line 2 executes.
Addresses gemini medium on PR #28.
Symptom
`stella/stdnum#95`'s release dispatch against `v0.0.1` exited 1 with no output and a script runtime of ~200ms. The composite step's env block was emitted to the log, then immediately `##[error]Process completed with exit code 1`. No `::error::` annotation, no `npm view` call, no `set -x` trace, nothing.
That pattern fits "bash failed to exec the script" — `set -e` exits on a failed exec without producing the script's own output.
Root cause
`publish.sh` is committed with git tree mode `100755` (verified). But the runner-side checkout of a composite action does not reliably preserve the executable bit. The action ADR documents this explicitly:
And the community has hit it repeatedly even when the mode IS committed: community/discussions/26239.
Fix
Two documented patterns are correct:
This PR uses option 2.
```yaml
run: bash "${{ github.action_path }}/publish.sh"
```
Test plan
References