Stop deploy.sh from rewriting itself while bash is reading it
`git reset --hard origin/main` replaces this script mid-execution. bash does
not slurp a script — it reads incrementally and remembers a byte OFFSET, so
after the reset it resumes at that offset into different content.
This is not theoretical. The 13dec6b deploy hit it: the new public-origin gate
and the rollback were on disk and never ran, because bash was still executing
the buffered previous version. That deploy exited 0 and the release is healthy,
so it cost nothing this time. The failure mode when it does bite is a spliced
or half-executed line, part way through a deployment.
scripts/autodeploy.sh has always re-exec'd from a mktemp copy for exactly this
reason. deploy.sh needed the same guard.
PIG_REPO_ROOT is resolved before the re-exec and exported across it: after the
re-exec `$0` is the copy in /tmp, so `dirname "$0"` would cd to the wrong tree.
Verified with a harness that rewrites the original mid-run and asserts the
child keeps both its content and its working directory.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+24
-1
@@ -26,7 +26,30 @@
|
|||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
cd "$(dirname "$0")/.."
|
# Resolved BEFORE the re-exec below and carried across it. After the re-exec
|
||||||
|
# `$0` is a copy in /tmp, so `dirname "$0"` would point at the wrong tree.
|
||||||
|
PIG_REPO_ROOT="${PIG_REPO_ROOT:-$(cd "$(dirname "$0")/.." && pwd)}"
|
||||||
|
export PIG_REPO_ROOT
|
||||||
|
cd "$PIG_REPO_ROOT"
|
||||||
|
|
||||||
|
# Run from a copy, because this script rewrites itself.
|
||||||
|
#
|
||||||
|
# `git reset --hard origin/main` below replaces this very file while bash is
|
||||||
|
# still reading it. bash does not slurp a script: it reads incrementally and
|
||||||
|
# remembers a byte OFFSET, so after the reset it resumes at that offset into
|
||||||
|
# different content — which silently skips or splices steps, and at worst
|
||||||
|
# executes a fragment of a line. The 13dec6b deploy hit exactly this: the new
|
||||||
|
# public-origin gate was on disk and never ran, because bash was still
|
||||||
|
# executing the buffered previous version.
|
||||||
|
#
|
||||||
|
# It failed harmlessly that time. It is not guaranteed to. scripts/autodeploy.sh
|
||||||
|
# has always had this guard; deploy.sh needed it for the same reason.
|
||||||
|
if [ "${PIG_DEPLOY_REEXEC:-}" != '1' ]; then
|
||||||
|
_copy=$(mktemp -t pig-deploy.XXXXXX)
|
||||||
|
cat "$0" > "$_copy"
|
||||||
|
PIG_DEPLOY_REEXEC=1 exec bash "$_copy" "$@"
|
||||||
|
fi
|
||||||
|
trap 'rm -f "$0"' EXIT
|
||||||
|
|
||||||
# What compose will run. Must match the `image:` default in docker-compose.yml,
|
# What compose will run. Must match the `image:` default in docker-compose.yml,
|
||||||
# because that is the tag a rollback re-points at the previous image.
|
# because that is the tag a rollback re-points at the previous image.
|
||||||
|
|||||||
Reference in New Issue
Block a user