diff --git a/.gitea/workflows/anno-apply-pr.yml b/.gitea/workflows/anno-apply-pr.yml index c72fec8..8bf69ea 100644 --- a/.gitea/workflows/anno-apply-pr.yml +++ b/.gitea/workflows/anno-apply-pr.yml @@ -16,6 +16,10 @@ defaults: run: shell: bash +concurrency: + group: anno-apply-${{ github.event.issue.number || inputs.issue || 'manual' }} + cancel-in-progress: true + jobs: apply-approved: runs-on: ubuntu-latest @@ -29,7 +33,6 @@ jobs: git --version node --version npm --version - npm ping --registry=https://registry.npmjs.org - name: Derive context (event.json / workflow_dispatch) env: @@ -110,6 +113,7 @@ jobs: run: | set -euo pipefail source /tmp/anno.env + if [[ "$LABEL_NAME" != "state/approved" && "$LABEL_NAME" != "workflow_dispatch" ]]; then echo "ℹ️ label=$LABEL_NAME => skip" echo "SKIP=1" >> /tmp/anno.env @@ -117,6 +121,93 @@ jobs: fi echo "✅ proceed (issue=$ISSUE_NUMBER)" + - name: Fetch issue + gate on Type (skip Proposer) + env: + FORGE_TOKEN: ${{ secrets.FORGE_TOKEN }} + run: | + set -euo pipefail + source /tmp/anno.env + [[ "${SKIP:-0}" != "1" ]] || { echo "ℹ️ skipped"; exit 0; } + + test -n "${FORGE_TOKEN:-}" || { echo "❌ Missing secret FORGE_TOKEN"; exit 1; } + + ISSUE_JSON="$(curl -fsS \ + -H "Authorization: token $FORGE_TOKEN" \ + -H "Accept: application/json" \ + "$API_BASE/api/v1/repos/$OWNER/$REPO/issues/$ISSUE_NUMBER")" + + node --input-type=module - <<'NODE' "$ISSUE_JSON" >> /tmp/anno.env + const issue = JSON.parse(process.argv[1] || "{}"); + const title = String(issue.title || ""); + const body = String(issue.body || "").replace(/\r\n/g, "\n"); + + function pickLine(key) { + const re = new RegExp(`^\\s*${key}\\s*:\\s*([^\\n\\r]+)`, "mi"); + const m = body.match(re); + return m ? m[1].trim() : ""; + } + + const typeRaw = pickLine("Type"); + const type = String(typeRaw || "").trim().toLowerCase(); + + const allowed = new Set(["type/media","type/reference","type/comment"]); + const proposer = new Set(["type/correction","type/fact-check"]); + + const out = []; + out.push(`ISSUE_TITLE=${JSON.stringify(title)}`); + out.push(`ISSUE_TYPE=${JSON.stringify(type)}`); + + if (!type) { + out.push(`SKIP=1`); + out.push(`SKIP_REASON=${JSON.stringify("missing_type")}`); + } else if (allowed.has(type)) { + // proceed + } else if (proposer.has(type)) { + out.push(`SKIP=1`); + out.push(`SKIP_REASON=${JSON.stringify("proposer_type:"+type)}`); + } else { + out.push(`SKIP=1`); + out.push(`SKIP_REASON=${JSON.stringify("unsupported_type:"+type)}`); + } + + process.stdout.write(out.join("\n") + "\n"); + NODE + + echo "✅ issue type gating:" + grep -E '^(ISSUE_TYPE|SKIP|SKIP_REASON)=' /tmp/anno.env || true + + - name: Comment issue if skipped (Proposer / unsupported / missing Type) + if: ${{ always() }} + env: + FORGE_TOKEN: ${{ secrets.FORGE_TOKEN }} + run: | + set -euo pipefail + source /tmp/anno.env || true + + [[ "${SKIP:-0}" == "1" ]] || exit 0 + [[ "$LABEL_NAME" == "state/approved" || "$LABEL_NAME" == "workflow_dispatch" ]] || exit 0 + + # message différent si Proposer + REASON="${SKIP_REASON:-}" + TYPE="${ISSUE_TYPE:-}" + TITLE="${ISSUE_TITLE:-}" + + if [[ "$REASON" == proposer_type:* ]]; then + MSG="ℹ️ Ticket #${ISSUE_NUMBER} détecté comme **Proposer** (${TYPE}).\n\n- Ce type est **traité manuellement par les editors** (correction/fact-check + cat/*).\n- Le bot n'applique **jamais** Proposer et n'ajoute **jamais** state/approved automatiquement.\n\n✅ Action : traitement éditorial manuel." + elif [[ "$REASON" == unsupported_type:* ]]; then + MSG="ℹ️ Ticket #${ISSUE_NUMBER} ignoré : Type non supporté par le bot (${TYPE}).\n\nTypes supportés : type/media, type/reference, type/comment.\n✅ Action : traitement manuel si nécessaire." + else + MSG="ℹ️ Ticket #${ISSUE_NUMBER} ignoré : champ 'Type:' manquant ou illisible.\n\n✅ Action : corriger le ticket (ajouter 'Type: type/media|type/reference|type/comment') ou traiter manuellement." + fi + + PAYLOAD="$(node --input-type=module -e 'console.log(JSON.stringify({body: process.argv[1]||""}))' "$MSG")" + + curl -fsS -X POST \ + -H "Authorization: token $FORGE_TOKEN" \ + -H "Content-Type: application/json" \ + "$API_BASE/api/v1/repos/$OWNER/$REPO/issues/$ISSUE_NUMBER/comments" \ + --data-binary "$PAYLOAD" + - name: Checkout default branch run: | set -euo pipefail @@ -135,7 +226,7 @@ jobs: set -euo pipefail source /tmp/anno.env [[ "${SKIP:-0}" != "1" ]] || { echo "ℹ️ skipped"; exit 0; } - npm ci + npm ci --no-audit --no-fund - name: Check apply script exists run: | @@ -154,7 +245,7 @@ jobs: source /tmp/anno.env [[ "${SKIP:-0}" != "1" ]] || { echo "ℹ️ skipped"; exit 0; } - npm run build:clean + npm run build test -f dist/para-index.json || { echo "❌ missing dist/para-index.json after build" @@ -220,7 +311,7 @@ jobs: FORGE_TOKEN: ${{ secrets.FORGE_TOKEN }} run: | set -euo pipefail - source /tmp/anno.env + source /tmp/anno.env || true [[ "${SKIP:-0}" != "1" ]] || { echo "ℹ️ skipped"; exit 0; } RC="${APPLY_RC:-0}" @@ -229,9 +320,13 @@ jobs: exit 0 fi - BODY="$(tail -n 160 /tmp/apply.log | sed 's/\r$//')" - MSG="❌ apply-annotation-ticket a échoué (rc=${RC}).\n\n\`\`\`\n${BODY}\n\`\`\`\n" + if [[ -f /tmp/apply.log ]]; then + BODY="$(tail -n 160 /tmp/apply.log | sed 's/\r$//')" + else + BODY="(no apply log found)" + fi + MSG="❌ apply-annotation-ticket a échoué (rc=${RC}).\n\n\`\`\`\n${BODY}\n\`\`\`\n" PAYLOAD="$(node --input-type=module -e 'console.log(JSON.stringify({body: process.argv[1]||""}))' "$MSG")" curl -fsS -X POST \ @@ -246,7 +341,7 @@ jobs: FORGE_TOKEN: ${{ secrets.FORGE_TOKEN }} run: | set -euo pipefail - source /tmp/anno.env + source /tmp/anno.env || true [[ "${SKIP:-0}" != "1" ]] || exit 0 [[ "${APPLY_RC:-0}" == "0" ]] || exit 0 @@ -267,7 +362,7 @@ jobs: FORGE_TOKEN: ${{ secrets.FORGE_TOKEN }} run: | set -euo pipefail - source /tmp/anno.env + source /tmp/anno.env || true [[ "${SKIP:-0}" != "1" ]] || exit 0 [[ "${APPLY_RC:-0}" == "0" ]] || { echo "ℹ️ apply failed -> skip push"; exit 0; } @@ -290,7 +385,7 @@ jobs: FORGE_TOKEN: ${{ secrets.FORGE_TOKEN }} run: | set -euo pipefail - source /tmp/anno.env + source /tmp/anno.env || true [[ "${SKIP:-0}" != "1" ]] || exit 0 [[ "${APPLY_RC:-0}" == "0" ]] || { echo "ℹ️ apply failed -> skip PR"; exit 0; } @@ -333,6 +428,7 @@ jobs: run: | set -euo pipefail source /tmp/anno.env || true + [[ "${SKIP:-0}" != "1" ]] || { echo "ℹ️ skipped"; exit 0; } RC="${APPLY_RC:-0}"