deploy: re-exec from a copy, so the script cannot rewrite itself mid-run
deploy.sh lives in the repo it updates. `git reset --hard` therefore replaced the file bash was reading; bash keeps a byte offset into the script and resumed at that offset in the new file, skipping whatever the diff had shifted. That is how the previous commit's renewal hook ended up present on the server and never executed: `grep` found it in deploy.sh, the hook directory was empty. Stage one now updates the checkout and re-execs a copy under /tmp, which no later `git reset` can touch; stage two does the work and removes the copy. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 file changed
+25
-5
+25
-5
@@ -26,11 +26,31 @@ warn() { printf '\033[1;33m !! %s\033[0m\n' "$*"; }
|
|||||||
cd "$REPO_DIR"
|
cd "$REPO_DIR"
|
||||||
|
|
||||||
# ---- 1. code ---------------------------------------------------------------
|
# ---- 1. code ---------------------------------------------------------------
|
||||||
say "Fetching $BRANCH from $(git remote get-url origin)"
|
# This script lives in the repo it updates, so `git reset --hard` rewrites the
|
||||||
git fetch --prune origin
|
# file bash is in the middle of reading. Bash reads a script incrementally and
|
||||||
git checkout -q "$BRANCH"
|
# keeps a byte offset, so after the reset it resumes at that offset in the *new*
|
||||||
git reset --hard "origin/$BRANCH"
|
# file — silently skipping or garbling whatever the diff shifted. (That is not
|
||||||
echo "now at $(git log --oneline -1)"
|
# hypothetical: it is how the certbot renewal hook came to be committed, present
|
||||||
|
# on disk, and never executed.)
|
||||||
|
#
|
||||||
|
# So: stage one updates the checkout and re-execs a *copy* outside the repo,
|
||||||
|
# which nothing can rewrite underneath it. Stage two does the actual work.
|
||||||
|
if [[ ${KADENCE_DEPLOY_STAGE:-} != run ]]; then
|
||||||
|
say "Fetching $BRANCH from $(git remote get-url origin)"
|
||||||
|
git fetch --prune origin
|
||||||
|
git checkout -q "$BRANCH"
|
||||||
|
git reset --hard "origin/$BRANCH"
|
||||||
|
echo "now at $(git log --oneline -1)"
|
||||||
|
|
||||||
|
staged=$(mktemp /tmp/kadence-deploy.XXXXXX.sh)
|
||||||
|
cat "$REPO_DIR/deploy/deploy.sh" > "$staged"
|
||||||
|
chmod +x "$staged"
|
||||||
|
export KADENCE_DEPLOY_STAGE=run KADENCE_DEPLOY_STAGED="$staged"
|
||||||
|
exec "$staged" "$@"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Stage two: started from the copy, so clean it up on the way out.
|
||||||
|
trap '[[ -n ${KADENCE_DEPLOY_STAGED:-} ]] && rm -f "$KADENCE_DEPLOY_STAGED"' EXIT
|
||||||
|
|
||||||
# ---- 2. secrets ------------------------------------------------------------
|
# ---- 2. secrets ------------------------------------------------------------
|
||||||
setkv() {
|
setkv() {
|
||||||
|
|||||||
Reference in new issue
Block a user