Compare commits

..

1 Commits

Author SHA1 Message Date
5e95dc9898 ci: fix deploy (install docker compose plugin)
All checks were successful
CI / build-and-anchors (push) Successful in 1m42s
SMOKE / smoke (push) Successful in 23s
2026-02-26 11:15:53 +01:00

View File

@@ -1,4 +1,4 @@
name: Deploy (staging + live) — annotations
name: Deploy staging+live (annotations)
on:
push:
@@ -6,21 +6,18 @@ on:
workflow_dispatch:
env:
# valeurs "publiques" injectées au build (import.meta.env.PUBLIC_*)
PUBLIC_GITEA_BASE: https://gitea.archicratie.trans-hands.synology.me
PUBLIC_GITEA_OWNER: Archicratia
PUBLIC_GITEA_REPO: archicratie-edition
# canonical/sitemap (IMPORTANT)
STAGING_PUBLIC_SITE: https://staging.archicratie.trans-hands.synology.me
LIVE_PUBLIC_SITE: https://archicratie.trans-hands.synology.me
REQUIRE_PUBLIC_SITE: "1"
NODE_OPTIONS: --dns-result-order=ipv4first
DOCKER_API_VERSION: "1.43"
COMPOSE_VERSION: "2.29.7"
defaults:
run:
shell: bash
concurrency:
group: deploy-staging-live-main
cancel-in-progress: false
jobs:
deploy:
runs-on: ubuntu-latest
@@ -28,20 +25,13 @@ jobs:
image: mcr.microsoft.com/devcontainers/javascript-node:22-bookworm
steps:
- name: Tools sanity + install docker cli
- name: Tools sanity
run: |
set -euo pipefail
git --version
node --version
npm --version
# docker cli + compose plugin + curl (dans le conteneur du job)
apt-get update -y
apt-get install -y --no-install-recommends docker.io docker-compose-plugin curl ca-certificates
docker --version
docker compose version
curl --version | head -n 1
- name: Checkout (from event.json, no external actions)
run: |
set -euo pipefail
@@ -53,171 +43,149 @@ jobs:
const repo =
ev?.repository?.clone_url ||
(ev?.repository?.html_url ? (ev.repository.html_url.replace(/\/$/,"") + ".git") : "");
const after =
const sha =
ev?.after ||
ev?.pull_request?.head?.sha ||
ev?.head_commit?.id ||
ev?.sha || "";
const before =
ev?.before || "";
ev?.sha ||
"";
if (!repo) throw new Error("No repository url in event.json");
if (!after) throw new Error("No after sha in event.json");
process.stdout.write(`REPO_URL=${JSON.stringify(repo)}\nAFTER=${JSON.stringify(after)}\nBEFORE=${JSON.stringify(before)}\n`);
if (!sha) throw new Error("No sha in event.json");
process.stdout.write(`REPO_URL=${JSON.stringify(repo)}\nSHA=${JSON.stringify(sha)}\n`);
')"
echo "Repo URL: $REPO_URL"
echo "BEFORE: $BEFORE"
echo "AFTER: $AFTER"
echo "SHA: $SHA"
rm -rf .git
git init -q
git remote add origin "$REPO_URL"
# fetch AFTER (+ BEFORE si dispo)
git fetch --depth 50 origin "$AFTER"
git fetch --depth 1 origin "$SHA"
git -c advice.detachedHead=false checkout -q FETCH_HEAD
# fetch BEFORE si c'est un sha plausible (pas 0000..)
if [[ -n "$BEFORE" && ! "$BEFORE" =~ ^0+$ ]]; then
git fetch --depth 50 origin "$BEFORE" || true
fi
git log -1 --oneline
echo "SHA=$SHA" >> /tmp/deploy.env
echo "REPO_URL=$REPO_URL" >> /tmp/deploy.env
- name: Gate — auto deploy only on annotations/media changes
run: |
set -euo pipefail
source /tmp/deploy.env
# récupère BEFORE/AFTER depuis event.json (déjà dispo dans env du shell précédent? non)
export EVENT_JSON="/var/run/act/workflow/event.json"
BEFORE="$(node --input-type=module -e 'import fs from "node:fs"; const ev=JSON.parse(fs.readFileSync(process.env.EVENT_JSON,"utf8")); process.stdout.write(String(ev?.before||""))')"
AFTER="$(git rev-parse HEAD)"
echo "AFTER=$AFTER"
echo "BEFORE=$BEFORE"
# si BEFORE absent/zeros → on autorise (premier push / edge case)
if [[ -z "$BEFORE" || "$BEFORE" =~ ^0+$ ]]; then
echo " No BEFORE sha => allow deploy"
exit 0
fi
# liste des fichiers changés
CHANGED="$(git diff --name-only "$BEFORE" "$AFTER" || true)"
# fichiers touchés par CE commit (merge commit inclus)
CHANGED="$(git diff-tree --no-commit-id --name-only -r "$SHA" || true)"
echo "== changed files =="
echo "$CHANGED" | sed -n '1,240p'
echo "$CHANGED" | sed -n '1,200p'
# autorisé uniquement sur ces chemins
BAD=0
while IFS= read -r f; do
[[ -z "$f" ]] && continue
if [[ "$f" =~ ^src/annotations/ ]] || [[ "$f" =~ ^public/media/ ]]; then
continue
fi
# tout le reste => on skip lauto-deploy (pas un échec)
echo "⚠️ non-annotation change detected: $f"
BAD=1
done <<< "$CHANGED"
if [[ "$BAD" -eq 1 ]]; then
echo " Skip auto deploy (changes not limited to annotations/media)."
echo "SKIP_DEPLOY=1" >> "$GITHUB_ENV" 2>/dev/null || true
echo "SKIP_DEPLOY=1" >> /tmp/deploy.env
# Gate strict : uniquement annotations + media + le workflow lui-même (si tu veux autoriser)
if echo "$CHANGED" | grep -qE '^(src/annotations/|public/media/)'; then
echo "GO=1" >> /tmp/deploy.env
echo "✅ deploy allowed (annotations/media change detected)"
exit 0
fi
echo "✅ Allowed: annotations/media only"
echo "GO=0" >> /tmp/deploy.env
echo " no annotations/media change -> skip deploy"
exit 0
- name: Build + deploy staging (blue) then smoke
- name: Install docker client + docker compose plugin (v2)
run: |
set -euo pipefail
if [[ -f /tmp/deploy.env ]] && grep -q '^SKIP_DEPLOY=1' /tmp/deploy.env; then
echo " skipped"
exit 0
fi
source /tmp/deploy.env
[[ "${GO:-0}" == "1" ]] || { echo " skipped"; exit 0; }
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/*
mkdir -p /usr/local/lib/docker/cli-plugins
curl -fsSL \
"https://github.com/docker/compose/releases/download/v${COMPOSE_VERSION}/docker-compose-linux-x86_64" \
-o /usr/local/lib/docker/cli-plugins/docker-compose
chmod +x /usr/local/lib/docker/cli-plugins/docker-compose
docker version
docker compose version
- name: Assert required vars (PUBLIC_GITEA_*)
env:
PUBLIC_GITEA_BASE: ${{ vars.PUBLIC_GITEA_BASE }}
PUBLIC_GITEA_OWNER: ${{ vars.PUBLIC_GITEA_OWNER }}
PUBLIC_GITEA_REPO: ${{ vars.PUBLIC_GITEA_REPO }}
run: |
set -euo pipefail
source /tmp/deploy.env
[[ "${GO:-0}" == "1" ]] || { echo " skipped"; exit 0; }
test -n "${PUBLIC_GITEA_BASE:-}" || { echo "❌ missing repo var PUBLIC_GITEA_BASE"; exit 2; }
test -n "${PUBLIC_GITEA_OWNER:-}" || { echo "❌ missing repo var PUBLIC_GITEA_OWNER"; exit 2; }
test -n "${PUBLIC_GITEA_REPO:-}" || { echo "❌ missing repo var PUBLIC_GITEA_REPO"; exit 2; }
echo "✅ vars OK"
- name: Build + deploy staging (blue) then smoke
env:
PUBLIC_GITEA_BASE: ${{ vars.PUBLIC_GITEA_BASE }}
PUBLIC_GITEA_OWNER: ${{ vars.PUBLIC_GITEA_OWNER }}
PUBLIC_GITEA_REPO: ${{ vars.PUBLIC_GITEA_REPO }}
run: |
set -euo pipefail
source /tmp/deploy.env
[[ "${GO:-0}" == "1" ]] || { echo " skipped"; exit 0; }
# backup tags (best effort)
TS="$(date -u +%Y%m%d-%H%M%S)"
echo "TS=$TS"
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
# backup image tag (best effort)
docker image inspect archicratie-web:blue >/dev/null 2>&1 && \
docker image tag archicratie-web:blue "archicratie-web:blue.BAK.${TS}" || true
# build + restart staging (blue=8081)
docker compose build --no-cache web_blue
docker compose up -d --force-recreate web_blue
# IMPORTANT: forcer les args de build (staging)
export PUBLIC_SITE="$STAGING_PUBLIC_SITE"
# smoke staging (local port)
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
curl -fsS "http://127.0.0.1:8081/pagefind/pagefind.js" >/dev/null
# on évite les conflits de "container name already in use"
docker rm -f archicratie-web-blue >/dev/null 2>&1 || true
docker compose -f docker-compose.yml build web_blue
docker compose -f docker-compose.yml up -d --force-recreate web_blue
# smoke staging (8081)
echo "== smoke staging (8081) =="
curl -fsS -o /dev/null "http://127.0.0.1:8081/para-index.json"
curl -fsS -o /dev/null "http://127.0.0.1:8081/annotations-index.json"
CANON="$(curl -fsS "http://127.0.0.1:8081/archicrat-ia/chapitre-1/" | grep -oE 'rel="canonical" href="[^"]+"' | head -n1 || true)"
echo "canonical: $CANON"
echo "$CANON" | grep -q "$STAGING_PUBLIC_SITE" || { echo "❌ staging canonical mismatch"; exit 1; }
echo "canonical(blue)=$CANON"
echo "$CANON" | grep -q 'https://staging\.archicratie\.trans-hands\.synology\.me/' || {
echo "❌ staging canonical mismatch"; exit 3;
}
echo "✅ staging OK"
- name: Build + deploy live (green) then smoke + rollback if needed
env:
PUBLIC_GITEA_BASE: ${{ vars.PUBLIC_GITEA_BASE }}
PUBLIC_GITEA_OWNER: ${{ vars.PUBLIC_GITEA_OWNER }}
PUBLIC_GITEA_REPO: ${{ vars.PUBLIC_GITEA_REPO }}
run: |
set -euo pipefail
if [[ -f /tmp/deploy.env ]] && grep -q '^SKIP_DEPLOY=1' /tmp/deploy.env; then
echo " skipped"
exit 0
fi
source /tmp/deploy.env
[[ "${GO:-0}" == "1" ]] || { echo " skipped"; exit 0; }
TS="$(date -u +%Y%m%d-%H%M%S)"
echo "TS=$TS"
TS="${TS:-$(date -u +%Y%m%d-%H%M%S)}"
# backup image tag (best effort)
docker image inspect archicratie-web:green >/dev/null 2>&1 && \
docker image tag archicratie-web:green "archicratie-web:green.BAK.${TS}" || true
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
}
# IMPORTANT: args de build (live)
export PUBLIC_SITE="$LIVE_PUBLIC_SITE"
# on évite les conflits
docker rm -f archicratie-web-green >/dev/null 2>&1 || true
# build + up
set +e
docker compose -f docker-compose.yml build web_green
BUILD_RC=$?
set -e
[[ "$BUILD_RC" -eq 0 ]] || { echo "❌ build live failed"; exit 1; }
docker compose build --no-cache web_green
docker compose up -d --force-recreate web_green
docker compose -f docker-compose.yml up -d --force-recreate web_green
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
# smoke live (8082)
echo "== smoke live (8082) =="
set +e
curl -fsS -o /dev/null "http://127.0.0.1:8082/para-index.json"
A1=$?
curl -fsS -o /dev/null "http://127.0.0.1:8082/annotations-index.json"
A2=$?
CANON="$(curl -fsS "http://127.0.0.1:8082/archicrat-ia/chapitre-1/" | grep -oE 'rel="canonical" href="[^"]+"' | head -n1 || true)"
echo "canonical: $CANON"
echo "$CANON" | grep -q "$LIVE_PUBLIC_SITE"
A3=$?
set -e
echo "canonical(green)=$CANON"
echo "$CANON" | grep -q 'https://archicratie\.trans-hands\.synology\.me/' || {
echo "❌ live canonical mismatch"; rollback; exit 4;
}
if [[ "$A1" -ne 0 || "$A2" -ne 0 || "$A3" -ne 0 ]]; then
echo "❌ live smoke failed => rollback"
# rollback image tag if backup exists
if docker image inspect "archicratie-web:green.BAK.${TS}" >/dev/null 2>&1; then
docker image tag "archicratie-web:green.BAK.${TS}" archicratie-web:green
docker rm -f archicratie-web-green >/dev/null 2>&1 || true
docker compose -f docker-compose.yml up -d --force-recreate --no-build web_green
echo "✅ rollback applied"
else
echo "⚠️ no backup image tag found => cannot rollback automatically"
fi
exit 1
fi
echo "✅ live OK"
echo "✅ live OK"
set -e