Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions scripts/deploy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,23 @@ fi

step "Pulling $BRANCH"
git fetch --prune origin

# `git pull --ff-only` is right — a deploy must never merge or rebase on its own
# — but when the branch has been rewritten upstream it fails with a wall of git
# hints and the word "aborting", which reads like the script is broken rather
# than like the server is one command from fine. Name the situation instead.
if ! git merge-base --is-ancestor HEAD "origin/$BRANCH" 2>/dev/null; then
printf '\033[31mLocal %s has diverged from origin/%s.\033[0m\n' "$BRANCH" "$BRANCH" >&2
printf ' local %s %s\n' "$(git rev-parse --short HEAD)" "$(git log -1 --format=%s)" >&2
printf ' origin %s %s\n' \
"$(git rev-parse --short "origin/$BRANCH")" \
"$(git log -1 --format=%s "origin/$BRANCH")" >&2
printf '\nUsually this means the branch was force-pushed. If this checkout has no\n' >&2
printf 'commits of its own worth keeping — it should not — take the remote as truth:\n\n' >&2
printf ' cd %s && git fetch origin && git reset --hard origin/%s\n\n' "$APP_PATH" "$BRANCH" >&2
fail "refusing to merge or rebase during a deploy"
fi

git pull --ff-only origin "$BRANCH"
CURR_COMMIT="$(git rev-parse HEAD)"
echo " now at $(git rev-parse --short HEAD) $(git log -1 --format=%s)"
Expand Down
27 changes: 27 additions & 0 deletions tests/Feature/DeployScriptTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -185,3 +185,30 @@ function deployMatches(string $flag, string $path): bool
'blocks a stray untracked file' => ['?? .env.backup', true],
'blocks an untracked build directory that is not one of ours' => ['?? public/uploads-old/', true],
]);

it('explains a diverged branch instead of dumping git hints', function (): void {
/*
* `git pull --ff-only` is the right call — a deploy must never merge or
* rebase on its own — but on a force-pushed branch it fails with a wall of
* git advice ending in "aborting", which reads as a broken script rather
* than as a checkout one command from fine. That cost a round trip the
* first time it happened.
*/
$script = file_get_contents(base_path('scripts/deploy.sh'));

expect($script)->toContain('git merge-base --is-ancestor HEAD');

// And it has to name the recovery, not just the diagnosis.
expect($script)->toContain('git reset --hard origin/');

/*
* The check has to come before the pull, or the raw git failure wins the
* race. Compared on the executable lines only — the comment above the check
* names `git pull --ff-only` too, and matching that instead put the guard
* "after" the pull it precedes by twelve lines.
*/
$code = preg_replace('/^\s*#.*$/m', '', $script);

expect(strpos($code, 'git merge-base --is-ancestor HEAD'))
->toBeLessThan(strpos($code, 'git pull --ff-only'));
});
Loading