Skip to content

fix(deploy): stop the deploy script rewriting itself mid-run - #5

Merged
mattDev0 merged 2 commits into
mainfrom
fix/script-exec-bits
Aug 31, 2026
Merged

fix(deploy): stop the deploy script rewriting itself mid-run#5
mattDev0 merged 2 commits into
mainfrom
fix/script-exec-bits

Conversation

@mattDev0

Copy link
Copy Markdown
Owner

The first SSH deploy failed twice, for two separate reasons. Both are fixed here.

1. exit 126 — Permission denied

All three files in scripts/ are mode 100644 in git:

100644 scripts/backup.sh
100644 scripts/deploy.sh
100644 scripts/health-check.sh

The Azure path invoked the script as bash deploy.sh, so the missing execute bit never mattered. The forced-command wrapper execs it directly and got exit 126. There's also a chicken-and-egg in the script itself — it runs chmod +x scripts/*.sh, which cannot help the one script that has to run first.

Fixed with git update-index --chmod=+x on all three.

2. The rerun executed the old script

After fixing the wrapper, the rerun got all the way through SSH, host-key verification and the git sync — then printed:

Successfully triggered keyless deployment via Azure Run Command.
Logging in to GitHub Container Registry...
username is empty

That text and that unconditional docker login were removed in #4. The checkout had already been updated to the new commit moments earlier.

Cause: deploy.sh replaces its own file via git reset --hard, and bash reads a script incrementally by byte offset. After the file is swapped, execution continues reading the new file from the old offset — so any deploy that changes deploy.sh runs a blend of both versions. It happened to land mid-way through the old logic here, but the failure mode is arbitrary.

This has been latent since the Compose migration; the Azure path hit it too, it just never changed deploy.sh in a way that showed.

Fix

Two stages:

if [ "${DCC_DEPLOY_STAGE:-sync}" = "sync" ]; then
  ...git fetch / reset --hard / chmod...
  export DCC_DEPLOY_STAGE=run
  exec bash scripts/deploy.sh     # fresh copy, clean process
fi
# --- Stage 2: deploy the synced checkout ---

Stage 1 only syncs, then re-execs. Stage 2 is what the updated copy runs. COMMIT_SHA is exported across the re-exec and still takes precedence over the stale value in .env.

Note

The host wrapper was also changed from exec /opt/.../deploy.sh to exec bash /opt/.../deploy.sh, so a missing execute bit can never block a deploy again even if the index mode is lost. docs/adr/adr_02_ssh_deploy.md is updated to match.

The first SSH deploy failed twice, for two separate reasons.

1. exit 126, "Permission denied". All three files in scripts/ are mode
   100644 in git. The Azure path invoked the script as "bash deploy.sh" so
   the missing execute bit never mattered; the forced-command wrapper exec's
   it directly. The script also chmod +x's itself, which it cannot do if it
   cannot run. Sets the execute bit in the index for all three.

2. The rerun then executed the *old* script - it printed "triggered keyless
   deployment via Azure Run Command" and did an unconditional docker login -
   despite the checkout having been updated moments earlier. deploy.sh
   replaces its own file with "git reset --hard", and bash reads a script
   incrementally by byte offset, so execution continues from the new file at
   the old offset. Any deploy that changes deploy.sh runs a blend of both
   versions.

Splits the script into two stages. Stage 1 syncs the checkout and re-execs;
stage 2 does the deploy and is what the fresh copy runs. COMMIT_SHA is
exported across the re-exec and still takes precedence over the stale value
in .env.
All three were mode 100644. The Azure deploy path invoked deploy.sh as
"bash deploy.sh" so it never mattered, but the forced-command wrapper
exec's it directly and got exit 126. deploy.sh also chmod +x's the
scripts at deploy time, which cannot help the script that has to run
first.
@mattDev0
mattDev0 merged commit 83b61a4 into main Aug 31, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant