Stop the promote script from deleting itself mid-run

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
(cherry picked from commit 6d42110a91)
This commit is contained in:
cproudlock
2026-08-30 09:53:04 -04:00
parent cd86c27969
commit 03804bb996
2 changed files with 45 additions and 1 deletions

View File

@@ -17,7 +17,30 @@ rem the commits keeps the decision explicit and per change.
rem
rem It does NOT push. Read what it prints, then run tools\git-push.cmd main.
cd /d "%~dp0.."
rem RUN FROM A COPY OF OURSELF.
rem
rem This checks out main. The tooling may live only on the site branch, in which
rem case that checkout DELETES this file while cmd.exe is still reading it - and
rem cmd.exe reads a batch file line by line, by file offset, re-seeking after
rem every command. It would carry on reading whatever bytes are at that offset
rem afterwards, while manipulating branches. The bash version has the same
rem hazard and the same fix.
if not "%PROMOTE_SELFCOPY%"=="1" (
rem The copy runs from %TEMP%, where %~dp0 is the temp directory and not the
rem checkout, so the repository root has to travel with it.
set "PROMOTE_REPO=%~dp0.."
set "PROMOTE_SELFCOPY=1"
copy /y "%~f0" "%TEMP%\promote-to-main-copy.cmd" >nul
call "%TEMP%\promote-to-main-copy.cmd" %*
rem Delayed expansion: a plain %errorlevel% here is expanded when the block is
rem parsed, which is BEFORE the call above has run, so it would always report
rem the code from whatever ran previously.
set "EXITCODE=!errorlevel!"
del /q "%TEMP%\promote-to-main-copy.cmd" >nul 2>&1
exit /b !EXITCODE!
)
cd /d "%PROMOTE_REPO%"
set "DRYRUN=0"
if "%~1"=="--dry-run" (