From 9e1b704aa69b28d4315103fe114b8c5ef8692385 Mon Sep 17 00:00:00 2001 From: Archicratia Date: Tue, 3 Mar 2026 20:46:03 +0100 Subject: [PATCH] ci(deploy): fix gate bash + robust merge-proof diff + full/hotpatch auto --- .gitea/workflows/deploy-staging-live.yml | 104 ++++++++++++++--------- 1 file changed, 64 insertions(+), 40 deletions(-) diff --git a/.gitea/workflows/deploy-staging-live.yml b/.gitea/workflows/deploy-staging-live.yml index 42fbe68..f4051c2 100644 --- a/.gitea/workflows/deploy-staging-live.yml +++ b/.gitea/workflows/deploy-staging-live.yml @@ -101,74 +101,98 @@ jobs: - name: Gate — decide SKIP vs HOTPATCH vs FULL rebuild env: INPUT_FORCE: ${{ inputs.force }} + EVENT_JSON: /var/run/act/workflow/event.json run: | set -euo pipefail 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+$ ]]; } + # Lire before/after du push depuis event.json (merge-proof) + node --input-type=module <<'NODE' + import fs from "node:fs"; + const ev = JSON.parse(fs.readFileSync(process.env.EVENT_JSON, "utf8")); + const before = ev?.before || ""; + const after = ev?.after || ev?.sha || ""; + const shq = (s) => "'" + String(s).replace(/'/g, "'\\''") + "'"; + fs.writeFileSync("/tmp/gate.env", [ + `EV_BEFORE=${shq(before)}`, + `EV_AFTER=${shq(after)}` + ].join("\n") + "\n"); + NODE - CHANGED="" + source /tmp/gate.env - 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 + BEFORE="${EV_BEFORE:-}" + AFTER="${EV_AFTER:-}" + if [[ -z "${AFTER:-}" ]]; then + AFTER="${SHA:-}" 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)" + echo "Gate ctx: BEFORE=${BEFORE:-} AFTER=${AFTER:-} FORCE=${FORCE}" + + # Produire une liste CHANGED fiable : + # - si BEFORE/AFTER valides -> git diff before..after + # - sinon fallback -> diff parent1..after ou show after + CHANGED="" + Z40="0000000000000000000000000000000000000000" + + if [[ -n "${BEFORE:-}" && "${BEFORE}" != "${Z40}" ]] \ + && git cat-file -e "${BEFORE}^{commit}" 2>/dev/null \ + && git cat-file -e "${AFTER}^{commit}" 2>/dev/null; then + CHANGED="$(git diff --name-only "${BEFORE}" "${AFTER}" || 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)" + P1="$(git rev-parse "${AFTER}^" 2>/dev/null || true)" + if [[ -n "${P1:-}" ]] && git cat-file -e "${P1}^{commit}" 2>/dev/null; then + CHANGED="$(git diff --name-only "${P1}" "${AFTER}" || 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)" + CHANGED="$(git show --name-only --pretty="" "${AFTER}" | sed '/^$/d' || true)" fi fi - printf "%s\n" "$CHANGED" > /tmp/changed.txt + printf "%s\n" "${CHANGED}" > /tmp/changed.txt - echo "== changed files ==" - echo "$CHANGED" | sed -n '1,260p' - - # --- Flags --- - HAS_HOTPATCH=0 - echo "$CHANGED" | grep -qE '^(src/annotations/|public/media/)' && HAS_HOTPATCH=1 + echo "== changed files (first 200) ==" + sed -n '1,200p' /tmp/changed.txt || true + # Flags 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_HOTPATCH=0 + + # FULL si build-impacting (ce que tu veux : content/anchors/pages/scripts) + if grep -qE '^(src/content/|src/anchors/|src/pages/|scripts/)' /tmp/changed.txt; then HAS_FULL=1 fi + # HOTPATCH si annotations/media touchés + if grep -qE '^(src/annotations/|public/media/)' /tmp/changed.txt; then + HAS_HOTPATCH=1 + fi + echo "Gate flags: HAS_FULL=${HAS_FULL} HAS_HOTPATCH=${HAS_HOTPATCH}" - if [[ "$HAS_FULL" == "1" ]]; then - echo "GO=1" >> /tmp/deploy.env - echo "MODE='full'" >> /tmp/deploy.env + # Décision + if [[ "${FORCE}" == "1" ]]; then + GO=1 + MODE="full" + echo "✅ force=1 -> MODE=full (rebuild+restart)" + elif [[ "${HAS_FULL}" == "1" ]]; then + GO=1 + MODE="full" echo "✅ build-impacting change -> MODE=full (rebuild+restart)" - elif [[ "$HAS_HOTPATCH" == "1" ]]; then - echo "GO=1" >> /tmp/deploy.env - echo "MODE='hotpatch'" >> /tmp/deploy.env + elif [[ "${HAS_HOTPATCH}" == "1" ]]; then + GO=1 + MODE="hotpatch" echo "✅ annotations/media change -> MODE=hotpatch" else - echo "GO=0" >> /tmp/deploy.env - echo "MODE='skip'" >> /tmp/deploy.env - echo "ℹ️ no deploy-relevant change -> skip deploy" + GO=0 + MODE="skip" + echo "ℹ️ no relevant change -> skip deploy" fi + echo "GO=${GO}" >> /tmp/deploy.env + echo "MODE='${MODE}'" >> /tmp/deploy.env + - name: Toolchain sanity + resolve COMPOSE_PROJECT_NAME run: | set -euo pipefail -- 2.49.1