Compare commits

...

2 Commits

Author SHA1 Message Date
0b4a31a432 ci(deploy): make gate merge-proof (diff before..after)
All checks were successful
SMOKE / smoke (push) Successful in 7s
CI / build-and-anchors (push) Successful in 43s
CI / build-and-anchors (pull_request) Successful in 37s
2026-03-03 19:58:27 +01:00
c617dc3979 Merge pull request 'test: B hotpatch-auto gate (touch src/annotations)' (#183) from testB-hotpatch-auto-20260303-183745 into main
All checks were successful
SMOKE / smoke (push) Successful in 7s
CI / build-and-anchors (push) Successful in 38s
Deploy staging+live (annotations) / deploy (push) Successful in 7m50s
Reviewed-on: #183
2026-03-03 18:40:39 +01:00

View File

@@ -47,48 +47,53 @@ jobs:
node --input-type=module <<'NODE'
import fs from "node:fs";
const ev = JSON.parse(fs.readFileSync(process.env.EVENT_JSON, "utf8"));
const repoObj = ev?.repository || {};
const cloneUrl =
repoObj?.clone_url ||
(repoObj?.html_url ? (repoObj.html_url.replace(/\/$/,"") + ".git") : "");
if (!cloneUrl) throw new Error("No repository clone_url/html_url in event.json");
const defaultBranch = repoObj?.default_branch || "main";
const sha =
// Push-range (most reliable for change detection)
const before = String(ev?.before || "").trim();
const after =
(process.env.GITHUB_SHA && String(process.env.GITHUB_SHA).trim()) ||
ev?.after ||
ev?.sha ||
ev?.head_commit?.id ||
ev?.pull_request?.head?.sha ||
"";
String(ev?.after || ev?.sha || ev?.head_commit?.id || ev?.pull_request?.head?.sha || "").trim();
const shq = (s) => "'" + String(s).replace(/'/g, "'\\''") + "'";
fs.writeFileSync("/tmp/deploy.env", [
`REPO_URL=${shq(cloneUrl)}`,
`DEFAULT_BRANCH=${shq(defaultBranch)}`,
`SHA=${shq(sha)}`
`BEFORE=${shq(before)}`,
`AFTER=${shq(after)}`
].join("\n") + "\n");
NODE
source /tmp/deploy.env
echo "Repo URL: $REPO_URL"
echo "Default branch: $DEFAULT_BRANCH"
echo "SHA: ${SHA:-<empty>}"
echo "BEFORE: ${BEFORE:-<empty>}"
echo "AFTER: ${AFTER:-<empty>}"
rm -rf .git
git init -q
git remote add origin "$REPO_URL"
if [[ -n "${SHA:-}" ]]; then
git fetch --depth 1 origin "$SHA"
# Checkout AFTER (or default branch if missing)
if [[ -n "${AFTER:-}" ]]; then
git fetch --depth 50 origin "$AFTER"
git -c advice.detachedHead=false checkout -q FETCH_HEAD
else
git fetch --depth 1 origin "$DEFAULT_BRANCH"
git fetch --depth 50 origin "$DEFAULT_BRANCH"
git -c advice.detachedHead=false checkout -q "origin/$DEFAULT_BRANCH"
SHA="$(git rev-parse HEAD)"
echo "SHA='$SHA'" >> /tmp/deploy.env
echo "Resolved SHA: $SHA"
AFTER="$(git rev-parse HEAD)"
echo "AFTER='$AFTER'" >> /tmp/deploy.env
echo "Resolved AFTER: $AFTER"
fi
git log -1 --oneline
@@ -101,57 +106,69 @@ jobs:
source /tmp/deploy.env
FORCE="${INPUT_FORCE:-0}"
BEFORE="${BEFORE:-}"
AFTER="${AFTER:-}"
# helper: true si sha = 0000... (push delete / weird payload)
is_zeros() { [[ -n "${1:-}" && "$1" =~ ^0+$ ]]; }
CHANGED=""
if [[ "$FORCE" == "1" ]]; then
echo "GO=1" >> /tmp/deploy.env
echo "MODE='full'" >> /tmp/deploy.env
echo "Gate flags: HAS_FULL=1 HAS_HOTPATCH=0"
echo "✅ force=1 -> MODE=full (rebuild+restart)"
exit 0
fi
# ✅ Push range (robuste, merge-proof)
if [[ -n "$BEFORE" && -n "$AFTER" && ! is_zeros "$BEFORE" ]]; then
# On s'assure que BEFORE est présent localement (checkout a fetch AFTER)
git cat-file -e "$BEFORE^{commit}" 2>/dev/null || git fetch --depth 50 origin "$BEFORE" || true
CHANGED="$(git diff --name-only "$BEFORE" "$AFTER" | sed '/^$/d' || true)"
else
# Fallback (workflow_dispatch, etc.) : diff HEAD^..HEAD si possible
if git cat-file -e "${AFTER}^" 2>/dev/null; then
CHANGED="$(git diff --name-only "${AFTER}^" "$AFTER" | sed '/^$/d' || true)"
else
# Merge commit sans parents dispo / autre cas : force la version merge-aware
CHANGED="$(git show -m --first-parent --name-only --pretty="" "$AFTER" | sed '/^$/d' || true)"
fi
fi
# liste fichiers touchés (utile pour copier les médias)
CHANGED="$(git show --name-only --pretty="" "$SHA" | sed '/^$/d' || true)"
printf "%s\n" "$CHANGED" > /tmp/changed.txt
echo "== changed files =="
echo "$CHANGED" | sed -n '1,260p'
# 0) Forçage manuel
if [[ "$FORCE" == "1" ]]; then
echo "GO=1" >> /tmp/deploy.env
echo "MODE='full'" >> /tmp/deploy.env
echo "✅ force=1 -> MODE=full (rebuild+restart)"
exit 0
fi
# 1) Détection des classes de changements
HAS_FULL=0
# --- Flags ---
HAS_HOTPATCH=0
echo "$CHANGED" | grep -qE '^(src/annotations/|public/media/)' && HAS_HOTPATCH=1
# FULL si build-impacting (zéro surprise)
if echo "$CHANGED" | grep -qE '^(src/content/|src/anchors/|src/pages/|scripts/)'; then
HAS_FULL=0
echo "$CHANGED" | grep -qE '^(src/(content|anchors|pages|components|layouts|styles|lib|plugins)/|scripts/|astro\.config\.mjs|package(-lock)?\.json|Dockerfile|nginx\.conf|docker-compose\.yml)' && HAS_FULL=1
# public/ hors media => FULL (sinon ce n'est jamais déployé par hotpatch)
if echo "$CHANGED" | grep -qE '^public/' && ! echo "$CHANGED" | grep -qE '^public/media/'; then
HAS_FULL=1
fi
# HOTPATCH si annotations/media
if echo "$CHANGED" | grep -qE '^(src/annotations/|public/media/)'; then
HAS_HOTPATCH=1
fi
echo "Gate flags: HAS_FULL=${HAS_FULL} HAS_HOTPATCH=${HAS_HOTPATCH}"
echo "Gate flags: HAS_FULL=$HAS_FULL HAS_HOTPATCH=$HAS_HOTPATCH"
# 2) Décision (priorité au FULL)
if [[ "$HAS_FULL" == "1" ]]; then
echo "GO=1" >> /tmp/deploy.env
echo "MODE='full'" >> /tmp/deploy.env
echo "✅ build-impacting change -> MODE=full (rebuild+restart)"
exit 0
fi
if [[ "$HAS_HOTPATCH" == "1" ]]; then
elif [[ "$HAS_HOTPATCH" == "1" ]]; then
echo "GO=1" >> /tmp/deploy.env
echo "MODE='hotpatch'" >> /tmp/deploy.env
echo "✅ annotations/media change -> MODE=hotpatch"
exit 0
else
echo "GO=0" >> /tmp/deploy.env
echo "MODE='skip'" >> /tmp/deploy.env
echo " no deploy-relevant change -> skip deploy"
fi
echo "GO=0" >> /tmp/deploy.env
echo "MODE='skip'" >> /tmp/deploy.env
echo " no deploy-relevant change -> skip deploy"
- name: Toolchain sanity + resolve COMPOSE_PROJECT_NAME
run: |
set -euo pipefail