Compare commits
1 Commits
claude/ama
...
rewrite/cu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9251fc5c0a |
@ -53,6 +53,12 @@ GitHub: **Actions → evo-deploy → Run workflow** (optional `ref`, `dry_run`,
|
||||
|
||||
## Rollback behavior
|
||||
|
||||
### Regression: SHA capture vs. git stdout
|
||||
|
||||
`deploy.sh` captures `previousSha` and `targetSha` via process substitution (`read … < <(git_fetch_and_checkout)`). Git commands such as `reset --hard` print progress to **stdout** (`HEAD is now at …`), which previously polluted that capture and produced invalid `.evo-deploy/state.json` (e.g. `deployedSha: "is now at 84bc45e …"`, `previousSha: "HEAD"`). Rollback then became a no-op.
|
||||
|
||||
**Fix:** all git invocations inside `git_fetch_and_checkout` (and rollback) redirect stdout to stderr; only the final `printf '%s %s\n'` emits SHAs on stdout. `write_state` and rollback reject values that are not 7–40 hex characters.
|
||||
|
||||
1. Before `git fetch`, the script records `previousSha` (current `HEAD`).
|
||||
2. After pull, migrate, and restart, it waits up to `EVO_HEALTH_TIMEOUT_SEC` (default 120s) for:
|
||||
- `EVO_API_HEALTH_URL` (default `http://127.0.0.1:3100/health`)
|
||||
|
||||
40
ops/evo-deploy/deploy-sha-capture.test.sh
Executable file
40
ops/evo-deploy/deploy-sha-capture.test.sh
Executable file
@ -0,0 +1,40 @@
|
||||
#!/usr/bin/env bash
|
||||
# Regression: git_fetch_and_checkout must emit only two SHAs on stdout.
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
TMP="$(mktemp -d)"
|
||||
trap 'rm -rf "$TMP"' EXIT
|
||||
|
||||
git init -q "$TMP/repo"
|
||||
git -C "$TMP/repo" config user.email "test@example.com"
|
||||
git -C "$TMP/repo" config user.name "Test"
|
||||
echo first >"$TMP/repo/file.txt"
|
||||
git -C "$TMP/repo" add file.txt
|
||||
git -C "$TMP/repo" commit -q -m "first"
|
||||
FIRST_SHA="$(git -C "$TMP/repo" rev-parse HEAD)"
|
||||
echo second >"$TMP/repo/file.txt"
|
||||
git -C "$TMP/repo" commit -q -am "second"
|
||||
SECOND_SHA="$(git -C "$TMP/repo" rev-parse HEAD)"
|
||||
|
||||
# Minimal reimplementation of the capture contract under test.
|
||||
git_fetch_and_checkout() {
|
||||
local previous_sha target_sha deploy_dir="$1" ref="$2"
|
||||
previous_sha="$(git -C "$deploy_dir" rev-parse HEAD)"
|
||||
git -C "$deploy_dir" fetch origin --prune >&2 2>/dev/null || true
|
||||
target_sha="$(git -C "$deploy_dir" rev-parse "${ref}^{commit}")"
|
||||
git -C "$deploy_dir" reset --hard "$target_sha" >&2
|
||||
printf '%s %s\n' "$previous_sha" "$target_sha"
|
||||
}
|
||||
|
||||
git -C "$TMP/repo" remote add origin "$TMP/repo"
|
||||
git -C "$TMP/repo" checkout -q "$FIRST_SHA"
|
||||
|
||||
capture="$(git_fetch_and_checkout "$TMP/repo" "$SECOND_SHA")"
|
||||
read -r got_previous got_target <<<"$capture"
|
||||
|
||||
[[ "$got_previous" == "$FIRST_SHA" ]] || { echo "expected previous $FIRST_SHA, got $got_previous"; exit 1; }
|
||||
[[ "$got_target" == "$SECOND_SHA" ]] || { echo "expected target $SECOND_SHA, got $got_target"; exit 1; }
|
||||
[[ "$capture" != *"HEAD is now at"* ]] || { echo "git reset output leaked into capture: $capture"; exit 1; }
|
||||
|
||||
echo "ok: deploy-sha-capture regression passed"
|
||||
@ -31,6 +31,10 @@ DRY_RUN=false
|
||||
log() { printf '[evo-deploy] %s\n' "$*" >&2; }
|
||||
die() { log "ERROR: $*"; exit 1; }
|
||||
|
||||
is_valid_git_sha() {
|
||||
[[ "$1" =~ ^[0-9a-fA-F]{7,40}$ ]]
|
||||
}
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage: deploy.sh [--dry-run] [--ref REF] [--skip-migrate] [--skip-install]
|
||||
@ -214,6 +218,8 @@ health_check() {
|
||||
|
||||
write_state() {
|
||||
local previous_sha="$1" target_sha="$2" status="$3"
|
||||
is_valid_git_sha "$previous_sha" || die "refusing to write state: invalid previousSha: $previous_sha"
|
||||
is_valid_git_sha "$target_sha" || die "refusing to write state: invalid deployedSha: $target_sha"
|
||||
if [[ "$DRY_RUN" == true ]]; then
|
||||
log "[dry-run] would write state to $EVO_STATE_FILE"
|
||||
return 0
|
||||
@ -235,7 +241,7 @@ git_fetch_and_checkout() {
|
||||
previous_sha="$(git -C "$EVO_DEPLOY_DIR" rev-parse HEAD)"
|
||||
log "previous HEAD: $previous_sha"
|
||||
log "fetching $EVO_GIT_REMOTE…"
|
||||
run git -C "$EVO_DEPLOY_DIR" fetch "$EVO_GIT_REMOTE" --prune
|
||||
run git -C "$EVO_DEPLOY_DIR" fetch "$EVO_GIT_REMOTE" --prune >&2
|
||||
if git -C "$EVO_DEPLOY_DIR" rev-parse --verify "${EVO_DEPLOY_REF}^{commit}" >/dev/null 2>&1; then
|
||||
target_sha="$(git -C "$EVO_DEPLOY_DIR" rev-parse "${EVO_DEPLOY_REF}^{commit}")"
|
||||
elif git -C "$EVO_DEPLOY_DIR" rev-parse --verify "${EVO_GIT_REMOTE}/${EVO_DEPLOY_REF}^{commit}" >/dev/null 2>&1; then
|
||||
@ -244,7 +250,7 @@ git_fetch_and_checkout() {
|
||||
die "cannot resolve ref: $EVO_DEPLOY_REF"
|
||||
fi
|
||||
log "checking out $target_sha ($EVO_DEPLOY_REF)"
|
||||
run git -C "$EVO_DEPLOY_DIR" reset --hard "$target_sha"
|
||||
run git -C "$EVO_DEPLOY_DIR" reset --hard "$target_sha" >&2
|
||||
printf '%s %s\n' "$previous_sha" "$target_sha"
|
||||
}
|
||||
|
||||
@ -269,8 +275,9 @@ run_install() {
|
||||
|
||||
rollback() {
|
||||
local previous_sha="$1"
|
||||
is_valid_git_sha "$previous_sha" || die "rollback aborted: invalid previous_sha: $previous_sha"
|
||||
log "ROLLBACK: resetting to $previous_sha and restarting services"
|
||||
run git -C "$EVO_DEPLOY_DIR" reset --hard "$previous_sha"
|
||||
run git -C "$EVO_DEPLOY_DIR" reset --hard "$previous_sha" >&2
|
||||
restart_services
|
||||
write_state "$previous_sha" "$previous_sha" "rolled-back"
|
||||
die "deploy failed; rolled back to $previous_sha"
|
||||
@ -289,6 +296,8 @@ main() {
|
||||
|
||||
local previous_sha target_sha
|
||||
read -r previous_sha target_sha < <(git_fetch_and_checkout)
|
||||
is_valid_git_sha "$previous_sha" || die "invalid previous_sha captured from checkout: $previous_sha"
|
||||
is_valid_git_sha "$target_sha" || die "invalid target_sha captured from checkout: $target_sha"
|
||||
|
||||
if [[ "$previous_sha" == "$target_sha" && "$DRY_RUN" != true ]]; then
|
||||
log "already at $target_sha — continuing with migrate/restart/health"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user