feat(fase-17): deploy script HEAD-tag check + tag column in deploy log

deploy-erosi.sh now runs `git describe --exact-match --tags HEAD` up
front. When HEAD is not at a tag, prints a yellow warning and sleeps
5s so the operator can Ctrl-C and tag before the rsync starts (no
abort — keeps untagged hotfix deploys possible). When a tag is
present, surfaces it in the banner next to the SHA.

`.deploys.log` gains a 4th tab-separated column: the tag (empty when
absent). The 3-column compat for pre-Fase-17 rows is preserved — the
rollback script's existing `cut -f1/-f2/-f3` keeps working, and `cut
-f4` returns the empty string for the older rows.

rollback-erosi.sh:
- `--list` reformats each row through awk so the tag is visible (with
  "(no tag)" placeholder for older rows), falling back to the raw log
  when awk isn't on PATH.
- The target banner shows `git tag: <tag>` when present.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-19 01:21:23 +02:00
parent f549dc53a2
commit 489fc3ec87
2 changed files with 49 additions and 4 deletions

View File

@@ -40,7 +40,26 @@ REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
# rsync without `.git`, so the remote can't run `git rev-parse`. # rsync without `.git`, so the remote can't run `git rev-parse`.
GIT_SHA="$(cd "$REPO_ROOT" && git rev-parse --short HEAD 2>/dev/null || echo dev)" GIT_SHA="$(cd "$REPO_ROOT" && git rev-parse --short HEAD 2>/dev/null || echo dev)"
echo "==> Deploying to $DEPLOY_HOST:$DEPLOY_PATH (commit $GIT_SHA)" # Fase 17.3 — capture the exact-match tag on HEAD (if any). Empty
# string when HEAD is between tags. Warn (don't block) when missing
# so untaggable hotfix deploys still go through, and stamp the tag
# into .deploys.log so the rollback script can surface it.
GIT_TAG="$(cd "$REPO_ROOT" && git describe --exact-match --tags HEAD 2>/dev/null || true)"
if [ -z "$GIT_TAG" ]; then
# ANSI yellow — visible enough to read past a terminal full of
# rsync output, but no exit. Sleep gives the operator a 5s window
# to Ctrl-C and tag HEAD before the rsync starts.
printf '\033[33mWARNING:\033[0m HEAD is not at a tagged commit. Deploys without a tag\n'
printf ' won'\''t map cleanly to CHANGELOG entries. Continue? [5s abort]\n'
sleep 5
fi
if [ -n "$GIT_TAG" ]; then
echo "==> Deploying to $DEPLOY_HOST:$DEPLOY_PATH (commit $GIT_SHA, tag $GIT_TAG)"
else
echo "==> Deploying to $DEPLOY_HOST:$DEPLOY_PATH (commit $GIT_SHA, no tag)"
fi
# ── 1. Ensure target dir exists ──────────────────────────────────────────── # ── 1. Ensure target dir exists ────────────────────────────────────────────
ssh "$DEPLOY_HOST" "sudo mkdir -p $DEPLOY_PATH && sudo chown \$(id -u):\$(id -g) $DEPLOY_PATH" ssh "$DEPLOY_HOST" "sudo mkdir -p $DEPLOY_PATH && sudo chown \$(id -u):\$(id -g) $DEPLOY_PATH"
@@ -154,10 +173,15 @@ REMOTE
# can substitute it into the Dockerfile build-arg (compose reads it from # can substitute it into the Dockerfile build-arg (compose reads it from
# the same shell, not from .env, so a transient export is enough). ssh # the same shell, not from .env, so a transient export is enough). ssh
# doesn't accept VAR=value args directly, so we prefix `env`. # doesn't accept VAR=value args directly, so we prefix `env`.
ssh "$DEPLOY_HOST" env GIT_SHA="$GIT_SHA" bash -s << 'REMOTE' #
# Fase 17.3.2: also forward GIT_TAG so the deploy log gets a 4th tab-
# separated column (empty string when HEAD is not at a tag — preserves
# the 3-column compat the rollback script's `cut -f1,2,3` already reads).
ssh "$DEPLOY_HOST" env GIT_SHA="$GIT_SHA" GIT_TAG="${GIT_TAG:-}" bash -s << 'REMOTE'
set -euo pipefail set -euo pipefail
cd /opt/colectivo cd /opt/colectivo
: "${GIT_SHA:=dev}" : "${GIT_SHA:=dev}"
: "${GIT_TAG:=}"
echo "--- Building app image (commit $GIT_SHA)" echo "--- Building app image (commit $GIT_SHA)"
GIT_SHA="$GIT_SHA" docker compose --env-file .env -f infra/docker-compose.erosi.yml \ GIT_SHA="$GIT_SHA" docker compose --env-file .env -f infra/docker-compose.erosi.yml \
@@ -217,9 +241,13 @@ docker compose --env-file .env -f infra/docker-compose.erosi.yml exec -T db \
-c "NOTIFY pgrst, 'reload schema';" -c "NOTIFY pgrst, 'reload schema';"
# Record the successful deploy in .deploys.log (read by rollback-erosi.sh). # Record the successful deploy in .deploys.log (read by rollback-erosi.sh).
# Format: <iso-ts>\t<sha>\t<backup-file>\t<tag-or-empty>
# The 4th column is Fase 17 — older rows have 3 columns; the rollback
# script's `cut -f4` returns empty for them, which is the right
# "no tag" sentinel.
backup_file=$(cat /tmp/.colectivo-last-backup 2>/dev/null || echo "") backup_file=$(cat /tmp/.colectivo-last-backup 2>/dev/null || echo "")
deploy_ts=$(date -u +"%Y-%m-%dT%H:%M:%SZ") deploy_ts=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
printf '%s\t%s\t%s\n' "$deploy_ts" "$GIT_SHA" "$backup_file" \ printf '%s\t%s\t%s\t%s\n' "$deploy_ts" "$GIT_SHA" "$backup_file" "$GIT_TAG" \
>> /opt/colectivo/.deploys.log >> /opt/colectivo/.deploys.log
rm -f /tmp/.colectivo-last-backup rm -f /tmp/.colectivo-last-backup
echo "--- Recorded deploy in /opt/colectivo/.deploys.log" echo "--- Recorded deploy in /opt/colectivo/.deploys.log"

View File

@@ -71,7 +71,19 @@ fi
if [ "$MODE" = "list" ]; then if [ "$MODE" = "list" ]; then
echo "Recent deploys ($DEPLOY_HOST):" echo "Recent deploys ($DEPLOY_HOST):"
echo "------------------------------------------------------------" echo "------------------------------------------------------------"
# Fase 17.3.3 — re-format each row so the tag (column 4, optional)
# is visible at a glance. Rows from before Fase 17 only have 3
# columns; the 4th `cut` returns empty and we substitute "(no tag)".
# Falls back to the raw log if awk isn't present (it always is on
# the deploy host, but we want the script to be robust on any
# operator laptop).
if command -v awk >/dev/null 2>&1; then
printf '%s\n' "$log" \
| awk -F '\t' '{ tag = ($4 == "" ? "(no tag)" : $4); printf "%s\t%s\t%s\t%s\n", $1, $2, tag, $3 }' \
| nl -ba
else
printf '%s\n' "$log" | nl -ba printf '%s\n' "$log" | nl -ba
fi
exit 0 exit 0
fi fi
@@ -91,10 +103,15 @@ fi
target_ts=$(printf '%s' "$target_row" | cut -f1) target_ts=$(printf '%s' "$target_row" | cut -f1)
target_sha=$(printf '%s' "$target_row" | cut -f2) target_sha=$(printf '%s' "$target_row" | cut -f2)
target_backup=$(printf '%s' "$target_row" | cut -f3) target_backup=$(printf '%s' "$target_row" | cut -f3)
# Fase 17.3.3 — 4th column is the tag (optional, empty on pre-Fase-17 rows).
target_tag=$(printf '%s' "$target_row" | cut -f4)
echo "==> Rollback target" echo "==> Rollback target"
echo " timestamp: $target_ts" echo " timestamp: $target_ts"
echo " git sha: $target_sha" echo " git sha: $target_sha"
if [ -n "${target_tag:-}" ]; then
echo " git tag: $target_tag"
fi
echo " backup: $target_backup" echo " backup: $target_backup"
# ── 3. Verify SHA + backup ─────────────────────────────────────────────── # ── 3. Verify SHA + backup ───────────────────────────────────────────────