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:
@@ -17,7 +17,30 @@ rem the commits keeps the decision explicit and per change.
|
|||||||
rem
|
rem
|
||||||
rem It does NOT push. Read what it prints, then run tools\git-push.cmd main.
|
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"
|
set "DRYRUN=0"
|
||||||
if "%~1"=="--dry-run" (
|
if "%~1"=="--dry-run" (
|
||||||
|
|||||||
@@ -26,6 +26,27 @@
|
|||||||
# main yourself.
|
# main yourself.
|
||||||
set -euo pipefail
|
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)"
|
cd "$(git rev-parse --show-toplevel)"
|
||||||
|
|
||||||
DRYRUN=0
|
DRYRUN=0
|
||||||
|
|||||||
Reference in New Issue
Block a user