An empty backup suffix makes `sed -i` portable
BehavioralEvolutionExample-Based TestsPre-Promotion Invariant Gates
https://medlab.sublayer.space/c/sed-i-empty-suffix-not-portable/
now fails loudly a gate refuses any `sed -i` whose argument is not a `.bak` suffix, so both the GNU-only no-suffix form and the macOS-only empty-suffix form fail the suite before they reach CI
What broke
sed has two in-place-edit conventions. GNU sed -i takes a backup suffix attached to the flag (-i.bak); without one it edits in place and keeps no backup. BSD sed -i, the macOS version, takes the backup name as a separate argument; an empty one (-i '') means "in place, no backup."
A prior case found the GNU-only form (sed -i 'script' file) failing on BSD, and named sed -i.bak 'script' file && rm -f file.bak as the form both accept.
This repo's test suite carried the GNU-only form. Its fix used sed -i '', the BSD form.
How it showed
This repo's CI runner uses GNU sed. The rewritten test failed:
sed: can't read s/^supersedes:.*/supersedes: sound-claim/: No such file or directory
GNU sed reads -i '' as no suffix, then '' as the script, then the edit expression as a file name to open. No such file exists. No commit in the run touched the code under test; the only change was the sed rewrite.
What was watching
Example-based tests — the suite — was on the path and did not catch it. Its assertions run sed -i; nothing checks that the invocation is written in a form both implementations accept.
Pre-promotion invariant gates — CI — fired and blocked the merge. How long the non-portable form had been there is unknown; the wrong fix was caught the day it was proposed.
Where it sits
Behavioral: the suite's behavior depends on which sed the runner has, not on the code it tests. Evolution: the prior case already named the correct form, and this repo's fix used the other non-portable form instead. No check in the repo, or in any shared lint, refused either form before CI ran it.
The example-based tests sensor was present and blind, as in the prior case: a platform-specific tool call inside the test that exercises it. The pre-promotion invariant gates sensor fired. The missing sensor checks the sed -i invocation itself, not the suite's output.
What we did
The proposed fix (a pull request on the affected repo) uses sed -i '', which is not portable to GNU sed. It was returned to its author with the CI log line and the correct form: sed -i.bak 'script' file && rm -f file.bak. The correction is not yet merged, so this case is fixed: no.
What now fails loudly
- A CI check greps each repo's scripts for
sed -iand fails the build when
the argument is not a literal .bak suffix. Both the GNU-only form and the BSD-only form fail before a reviewer sees them.
- The check's message states the portable form,
sed -i.bak 'script' file && rm -f file.bak, so a fixer is pointed at it directly.