Stop the promote script from deleting itself mid-run
All checks were successful
CI / naming (push) Successful in 3s
CI / frontend (push) Successful in 13s
CI / migrations-mysql (push) Successful in 42s
CI / backend (push) Successful in 4m15s

promote-to-main.sh checks out main. The tooling lives only on the site branch,
so that checkout removes tools/promote-to-main.sh while the script is still
running - and bash reads a script lazily, by file offset, so it would carry on
executing whatever bytes happen to land there. While manipulating branches.

Not theoretical: main does not have tools/ right now, so the first real use
would have hit it.

Both versions now copy themselves to a temp file and re-exec from there. The
bash copy unlinks itself immediately, since the open handle keeps working. The
Windows copy has the same hazard for the same reason - cmd.exe re-seeks the
batch file after every command - and two extra traps worth naming: the copy runs
from %TEMP%, so the repository root has to travel with it rather than being
derived from %~dp0, and %errorlevel% inside a parenthesised block is expanded
when the block is PARSED, before the call it is meant to report on.

Scope: product
This commit is contained in:
cproudlock
2026-08-30 09:53:04 -04:00
parent 0f2da9beaf
commit 6d42110a91
2 changed files with 45 additions and 1 deletions

View File

@@ -26,6 +26,27 @@
# main yourself.
set -euo pipefail
# RUN FROM A COPY OF OURSELF.
#
# This script checks out main. The tooling may live only on the site branch, in
# which case that checkout DELETES this file while bash is still reading it -
# and bash reads a script lazily, by offset, so it would carry on executing
# whatever bytes happen to be at that position afterwards. While manipulating
# branches. This is not theoretical: main did not have tools/ when this was
# written, so the very first real use would have hit it.
#
# Copy to a temp file, re-exec from there, and delete the copy immediately. On
# Unix the open file handle keeps working after the unlink, so the running
# script survives whatever git does to the tree.
if [ "${PROMOTE_SELFCOPY:-}" != "1" ]; then
SELF="$(mktemp)"
cat "$0" > "$SELF"
chmod 700 "$SELF"
export PROMOTE_SELFCOPY=1
exec bash "$SELF" "$@"
fi
rm -f "$0" 2>/dev/null || true
cd "$(git rev-parse --show-toplevel)"
DRYRUN=0