Developer Journal

Advanced · 7 minute read

Safely Updating a Deployment Script While It Is Running

Why a running shell can mix old and new deployment instructions, and how to bootstrap Drupal releases safely.

Last updated August 6, 2026

A deployment script can update the repository that contains the script itself. That sounds harmless until the running shell continues reading a file whose contents changed underneath it. During this portfolio deployment, the old script fast-forwarded the checkout and then attempted to run setup files that the new commit had already removed.

The confusing failure pattern

Git reported a successful fast-forward, Drupal had not failed, and the missing file was intentionally deleted. The problem was process lifetime: Bash had started with the old deployment instructions, while the filesystem now contained the new release. The running process and the checked-out code no longer described the same deployment.

Bootstrap before executing release logic

The durable solution is to separate repository synchronization from Drupal deployment. The GitHub Actions SSH command first fetches the target branch and performs a fast-forward-only merge. Only after that succeeds does it invoke the deployment script now present in the updated checkout.

git fetch origin "main:refs/remotes/origin/main" git merge --ff-only origin/main DEPLOY_REEXECUTED=1 bash scripts/deploy-hostinger.sh production

The environment flag tells the script that synchronization has already occurred. Manual invocations can still perform the update and then replace themselves with the new version using exec.

Why fast-forward-only matters

A deployment host should not invent merge commits or resolve conflicts. git merge --ff-only succeeds only when the server checkout can move directly to the tested revision. If the server contains divergent history, deployment stops and asks for investigation instead of silently combining code.

Keep the release idempotent

Once the current script is running, repeating the deployment should be safe. Configuration imports can report no changes, cache rebuilds can run again, and health checks should return the same outcome. Idempotency made it safe to rerun staging after correcting its protected-site credentials.

Drupal-specific safeguards

  • Back up the database before changing runtime state.
  • Enter maintenance mode only after code synchronization succeeds.
  • Stop before configuration import when database updates are pending.
  • Use a shell trap to restore the site if a later step fails.
  • Verify Drupal bootstrap, database connectivity, and public routes afterward.

Sources and further reading