From 4dec9e182b522b2fa0102e6b31bb5365656aab12 Mon Sep 17 00:00:00 2001 From: Archicratia Date: Thu, 26 Feb 2026 17:58:58 +0100 Subject: [PATCH] ci: fix deploy checkout (no eval) + workflow_dispatch fallback --- .gitea/workflows/deploy-staging-live.yml | 139 ++++++++++------------- 1 file changed, 60 insertions(+), 79 deletions(-) diff --git a/.gitea/workflows/deploy-staging-live.yml b/.gitea/workflows/deploy-staging-live.yml index 8fb62d3..8ac4a4d 100644 --- a/.gitea/workflows/deploy-staging-live.yml +++ b/.gitea/workflows/deploy-staging-live.yml @@ -37,75 +37,71 @@ jobs: node --version npm --version - - name: Checkout (from event.json, no external actions) + - name: Checkout (push or workflow_dispatch, no external actions) + env: + EVENT_JSON: /var/run/act/workflow/event.json run: | set -euo pipefail - EVENT_JSON="/var/run/act/workflow/event.json" test -f "$EVENT_JSON" || { echo "❌ Missing $EVENT_JSON"; exit 1; } - # Node prints REPO_URL, DEFAULT_BRANCH, REF, SHA_CAND (may be empty in workflow_dispatch) - OUT="$(node --input-type=module -e ' - import fs from "node:fs"; - const ev = JSON.parse(fs.readFileSync(process.env.EVENT_JSON,"utf8")); + # Write /tmp/deploy.env with shell-safe quoting (NO eval) + node --input-type=module <<'NODE' + import fs from "node:fs"; - const repoObj = ev?.repository || {}; - const repo = - repoObj?.clone_url || - (repoObj?.html_url ? (repoObj.html_url.replace(/\/$/,"") + ".git") : ""); + const ev = JSON.parse(fs.readFileSync(process.env.EVENT_JSON, "utf8")); + const repoObj = ev?.repository || {}; - if (!repo) throw new Error("No repository url in event.json"); + 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 defaultBranch = repoObj?.default_branch || "main"; - const ref = - ev?.ref || `refs/heads/${defaultBranch}`; + // On push, act/Gitea usually provides GITHUB_SHA; on workflow_dispatch it may be empty. + const sha = + (process.env.GITHUB_SHA && String(process.env.GITHUB_SHA).trim()) || + ev?.after || + ev?.sha || + ev?.head_commit?.id || + ev?.pull_request?.head?.sha || + ""; - const sha = - ev?.after || - ev?.pull_request?.head?.sha || - ev?.head_commit?.id || - ev?.sha || - ""; + const shq = (s) => "'" + String(s).replace(/'/g, "'\\''") + "'"; - process.stdout.write( - `REPO_URL=${JSON.stringify(repo)}\n` + - `DEFAULT_BRANCH=${JSON.stringify(defaultBranch)}\n` + - `REF=${JSON.stringify(ref)}\n` + - `SHA_CAND=${JSON.stringify(sha)}\n` - ); - ' EVENT_JSON="$EVENT_JSON")" || { echo "❌ Cannot parse event.json"; exit 1; }" + const out = [ + `REPO_URL=${shq(cloneUrl)}`, + `DEFAULT_BRANCH=${shq(defaultBranch)}`, + `SHA=${shq(sha)}` + ].join("\n") + "\n"; - eval "$OUT" + fs.writeFileSync("/tmp/deploy.env", out); + process.stdout.write(out); + NODE - echo "Repo URL: $REPO_URL" - echo "Default branch: $DEFAULT_BRANCH" - echo "Ref: $REF" - echo "SHA candidate: ${SHA_CAND:-}" + source /tmp/deploy.env + + echo "Repo URL: $REPO_URL" + echo "Default branch: $DEFAULT_BRANCH" + echo "SHA: ${SHA:-}" rm -rf .git git init -q git remote add origin "$REPO_URL" - if [[ -n "${SHA_CAND:-}" ]]; then - echo "Checkout by SHA: $SHA_CAND" - git fetch --depth 1 origin "$SHA_CAND" + if [[ -n "${SHA:-}" ]]; then + git fetch --depth 1 origin "$SHA" git -c advice.detachedHead=false checkout -q FETCH_HEAD else - # workflow_dispatch often has no SHA; fetch by ref/branch - REF_TO_FETCH="$REF" - if [[ "$REF_TO_FETCH" == refs/heads/* ]]; then - REF_TO_FETCH="${REF_TO_FETCH#refs/heads/}" - fi - echo "Checkout by ref: $REF_TO_FETCH" - git fetch --depth 1 origin "$REF_TO_FETCH" - git -c advice.detachedHead=false checkout -q FETCH_HEAD + # workflow_dispatch fallback: checkout latest of default branch + git fetch --depth 1 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" fi - SHA="$(git rev-parse HEAD)" git log -1 --oneline - echo "SHA=$SHA" >> /tmp/deploy.env - echo "REPO_URL=$REPO_URL" >> /tmp/deploy.env - echo "DEFAULT_BRANCH=$DEFAULT_BRANCH" >> /tmp/deploy.env - name: Gate — auto deploy only on annotations/media changes env: @@ -121,19 +117,9 @@ jobs: exit 0 fi - # Robust changed-files list (merge commits included) - # Prefer diff vs first parent; fallback to git show. - if git rev-parse "${SHA}^" >/dev/null 2>&1; then - CHANGED="$(git diff --name-only "${SHA}^" "$SHA" || true)" - else - CHANGED="" - fi - if [[ -z "$CHANGED" ]]; then - CHANGED="$(git show --name-only --pretty="" -m "$SHA" | sed '/^$/d' || true)" - fi - + CHANGED="$(git show --name-only --pretty="" "$SHA" | sed '/^$/d' || true)" echo "== changed files ==" - echo "$CHANGED" | sed -n '1,200p' + echo "$CHANGED" | sed -n '1,240p' if echo "$CHANGED" | grep -qE '^(src/annotations/|public/media/)'; then echo "GO=1" >> /tmp/deploy.env @@ -149,9 +135,6 @@ jobs: source /tmp/deploy.env [[ "${GO:-0}" == "1" ]] || { echo "ℹ️ skipped"; exit 0; } - # Must have docker socket mounted by runner - test -S /var/run/docker.sock || { echo "❌ /var/run/docker.sock missing in job container"; exit 10; } - apt-get -o Acquire::Retries=5 -o Acquire::ForceIPv4=true update apt-get install -y --no-install-recommends ca-certificates curl docker.io rm -rf /var/lib/apt/lists/* @@ -202,13 +185,12 @@ jobs: [[ "${GO:-0}" == "1" ]] || { echo "ℹ️ skipped"; exit 0; } TS="$(date -u +%Y%m%d-%H%M%S)" - echo "TS=$TS" >> /tmp/deploy.env - + echo "TS='$TS'" >> /tmp/deploy.env docker image tag archicratie-web:blue "archicratie-web:blue.BAK.${TS}" || true docker image tag archicratie-web:green "archicratie-web:green.BAK.${TS}" || true - docker compose build --no-cache web_blue - docker compose up -d --force-recreate web_blue + docker compose -f docker-compose.yml build --no-cache web_blue + docker compose -f docker-compose.yml up -d --force-recreate web_blue curl -fsS "http://127.0.0.1:8081/para-index.json" >/dev/null curl -fsS "http://127.0.0.1:8081/annotations-index.json" >/dev/null @@ -216,7 +198,9 @@ jobs: CANON="$(curl -fsS "http://127.0.0.1:8081/archicrat-ia/chapitre-1/" | grep -oE 'rel="canonical" href="[^"]+"' | head -n1 || true)" echo "canonical(blue)=$CANON" - echo "$CANON" | grep -q 'https://staging\.archicratie\.trans-hands\.synology\.me/' || { echo "❌ staging canonical mismatch"; exit 3; } + echo "$CANON" | grep -q 'https://staging\.archicratie\.trans-hands\.synology\.me/' || { + echo "❌ staging canonical mismatch"; exit 3; + } echo "✅ staging OK" @@ -235,25 +219,22 @@ jobs: rollback() { echo "⚠️ rollback green -> previous image tag (best effort)" docker image tag "archicratie-web:green.BAK.${TS}" archicratie-web:green || true - docker compose up -d --force-recreate web_green || true + docker compose -f docker-compose.yml up -d --force-recreate web_green || true } set +e - docker compose build --no-cache web_green - BRC=$? - [[ "$BRC" -eq 0 ]] || { echo "❌ build green failed"; rollback; exit 4; } + docker compose -f docker-compose.yml build --no-cache web_green + docker compose -f docker-compose.yml up -d --force-recreate web_green - docker compose up -d --force-recreate web_green - URC=$? - [[ "$URC" -eq 0 ]] || { echo "❌ up green failed"; rollback; exit 4; } - - curl -fsS "http://127.0.0.1:8082/para-index.json" >/dev/null || { rollback; exit 4; } - curl -fsS "http://127.0.0.1:8082/annotations-index.json" >/dev/null || { rollback; exit 4; } - curl -fsS "http://127.0.0.1:8082/pagefind/pagefind.js" >/dev/null || { rollback; exit 4; } + curl -fsS "http://127.0.0.1:8082/para-index.json" >/dev/null + curl -fsS "http://127.0.0.1:8082/annotations-index.json" >/dev/null + curl -fsS "http://127.0.0.1:8082/pagefind/pagefind.js" >/dev/null CANON="$(curl -fsS "http://127.0.0.1:8082/archicrat-ia/chapitre-1/" | grep -oE 'rel="canonical" href="[^"]+"' | head -n1 || true)" echo "canonical(green)=$CANON" - echo "$CANON" | grep -q 'https://archicratie\.trans-hands\.synology\.me/' || { echo "❌ live canonical mismatch"; rollback; exit 4; } + echo "$CANON" | grep -q 'https://archicratie\.trans-hands\.synology\.me/' || { + echo "❌ live canonical mismatch"; rollback; exit 4; + } echo "✅ live OK" set -e \ No newline at end of file -- 2.49.1