Compare commits
8 Commits
debug/prop
...
hotfix/pro
| Author | SHA1 | Date | |
|---|---|---|---|
| 5d3473d66c | |||
| 513ae72e85 | |||
| 4c4dd1c515 | |||
| 46b15ed6ab | |||
| a015e72f7c | |||
|
|
d5df7d77a0 | ||
| ec3ceee862 | |||
| b024c5557c |
@@ -130,8 +130,6 @@ jobs:
|
|||||||
echo "event=$EVENT_NAME label=${LABEL_NAME:-<empty>}"
|
echo "event=$EVENT_NAME label=${LABEL_NAME:-<empty>}"
|
||||||
|
|
||||||
if [[ "$EVENT_NAME" == "issues" ]]; then
|
if [[ "$EVENT_NAME" == "issues" ]]; then
|
||||||
# Gitea peut fournir un payload "issues/labeled" sans label exploitable.
|
|
||||||
# On ne skip QUE si le label est explicitement présent ET différent de state/approved.
|
|
||||||
if [[ -n "${LABEL_NAME:-}" && "$LABEL_NAME" != "state/approved" ]]; then
|
if [[ -n "${LABEL_NAME:-}" && "$LABEL_NAME" != "state/approved" ]]; then
|
||||||
echo "issues/labeled with explicit non-approved label=$LABEL_NAME -> skip"
|
echo "issues/labeled with explicit non-approved label=$LABEL_NAME -> skip"
|
||||||
echo 'SKIP=1' >> /tmp/proposer.env
|
echo 'SKIP=1' >> /tmp/proposer.env
|
||||||
@@ -218,6 +216,43 @@ jobs:
|
|||||||
echo "Target batch:"
|
echo "Target batch:"
|
||||||
grep -E '^(TARGET_PRIMARY_ISSUE|TARGET_ISSUES|TARGET_COUNT|TARGET_CHEMIN)=' /tmp/proposer.env
|
grep -E '^(TARGET_PRIMARY_ISSUE|TARGET_ISSUES|TARGET_COUNT|TARGET_CHEMIN)=' /tmp/proposer.env
|
||||||
|
|
||||||
|
- name: Derive deterministic batch identity
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
source /tmp/proposer.env
|
||||||
|
[[ "${SKIP:-0}" != "1" ]] || { echo "Skipped"; exit 0; }
|
||||||
|
|
||||||
|
export TARGET_ISSUES TARGET_CHEMIN
|
||||||
|
|
||||||
|
node --input-type=module - <<'NODE'
|
||||||
|
import fs from "node:fs";
|
||||||
|
import crypto from "node:crypto";
|
||||||
|
|
||||||
|
const issues = String(process.env.TARGET_ISSUES || "")
|
||||||
|
.trim()
|
||||||
|
.split(/\s+/)
|
||||||
|
.filter(Boolean)
|
||||||
|
.sort((a, b) => Number(a) - Number(b));
|
||||||
|
|
||||||
|
const chemin = String(process.env.TARGET_CHEMIN || "").trim();
|
||||||
|
const keySource = `${chemin}::${issues.join(",")}`;
|
||||||
|
const hash = crypto.createHash("sha1").update(keySource).digest("hex").slice(0, 12);
|
||||||
|
const primary = issues[0] || "0";
|
||||||
|
const batchBranch = `bot/proposer-${primary}-${hash}`;
|
||||||
|
|
||||||
|
fs.appendFileSync(
|
||||||
|
"/tmp/proposer.env",
|
||||||
|
[
|
||||||
|
`BATCH_KEY=${JSON.stringify(keySource)}`,
|
||||||
|
`BATCH_HASH=${JSON.stringify(hash)}`,
|
||||||
|
`BATCH_BRANCH=${JSON.stringify(batchBranch)}`
|
||||||
|
].join("\n") + "\n"
|
||||||
|
);
|
||||||
|
NODE
|
||||||
|
|
||||||
|
echo "Batch identity:"
|
||||||
|
grep -E '^(BATCH_KEY|BATCH_HASH|BATCH_BRANCH)=' /tmp/proposer.env
|
||||||
|
|
||||||
- name: Inspect open proposer PRs
|
- name: Inspect open proposer PRs
|
||||||
env:
|
env:
|
||||||
FORGE_TOKEN: ${{ secrets.FORGE_TOKEN }}
|
FORGE_TOKEN: ${{ secrets.FORGE_TOKEN }}
|
||||||
@@ -233,6 +268,8 @@ jobs:
|
|||||||
-o /tmp/open_pulls.json
|
-o /tmp/open_pulls.json
|
||||||
|
|
||||||
export TARGET_ISSUES="${TARGET_ISSUES:-}"
|
export TARGET_ISSUES="${TARGET_ISSUES:-}"
|
||||||
|
export BATCH_BRANCH="${BATCH_BRANCH:-}"
|
||||||
|
export BATCH_KEY="${BATCH_KEY:-}"
|
||||||
|
|
||||||
node --input-type=module - <<'NODE' >> /tmp/proposer.env
|
node --input-type=module - <<'NODE' >> /tmp/proposer.env
|
||||||
import fs from "node:fs";
|
import fs from "node:fs";
|
||||||
@@ -243,15 +280,21 @@ jobs:
|
|||||||
.split(/\s+/)
|
.split(/\s+/)
|
||||||
.filter(Boolean);
|
.filter(Boolean);
|
||||||
|
|
||||||
|
const batchBranch = String(process.env.BATCH_BRANCH || "");
|
||||||
|
const batchKey = String(process.env.BATCH_KEY || "");
|
||||||
|
|
||||||
const proposerOpen = Array.isArray(pulls)
|
const proposerOpen = Array.isArray(pulls)
|
||||||
? pulls.filter((pr) => String(pr?.head?.ref || "").startsWith("bot/proposer-"))
|
? pulls.filter((pr) => String(pr?.head?.ref || "").startsWith("bot/proposer-"))
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
const current = proposerOpen.find((pr) => {
|
const sameBatch = proposerOpen.find((pr) => {
|
||||||
const ref = String(pr?.head?.ref || "");
|
const ref = String(pr?.head?.ref || "");
|
||||||
const title = String(pr?.title || "");
|
const title = String(pr?.title || "");
|
||||||
const body = String(pr?.body || "");
|
const body = String(pr?.body || "");
|
||||||
|
|
||||||
|
if (batchBranch && ref === batchBranch) return true;
|
||||||
|
if (batchKey && body.includes(`Batch-Key: ${batchKey}`)) return true;
|
||||||
|
|
||||||
return issues.some((n) =>
|
return issues.some((n) =>
|
||||||
ref.startsWith(`bot/proposer-${n}-`) ||
|
ref.startsWith(`bot/proposer-${n}-`) ||
|
||||||
title.includes(`#${n}`) ||
|
title.includes(`#${n}`) ||
|
||||||
@@ -262,10 +305,11 @@ jobs:
|
|||||||
|
|
||||||
const out = [];
|
const out = [];
|
||||||
|
|
||||||
if (current) {
|
if (sameBatch) {
|
||||||
out.push("SKIP=1");
|
out.push("SKIP=1");
|
||||||
out.push(`SKIP_REASON=${JSON.stringify("issue_already_has_open_pr")}`);
|
out.push(`SKIP_REASON=${JSON.stringify("issue_already_has_open_pr")}`);
|
||||||
out.push(`OPEN_PR_URL=${JSON.stringify(String(current.html_url || current.url || ""))}`);
|
out.push(`OPEN_PR_URL=${JSON.stringify(String(sameBatch.html_url || sameBatch.url || ""))}`);
|
||||||
|
out.push(`OPEN_PR_BRANCH=${JSON.stringify(String(sameBatch?.head?.ref || ""))}`);
|
||||||
} else if (proposerOpen.length > 0) {
|
} else if (proposerOpen.length > 0) {
|
||||||
const first = proposerOpen[0];
|
const first = proposerOpen[0];
|
||||||
out.push("SKIP=1");
|
out.push("SKIP=1");
|
||||||
@@ -277,6 +321,22 @@ jobs:
|
|||||||
process.stdout.write(out.join("\n") + (out.length ? "\n" : ""));
|
process.stdout.write(out.join("\n") + (out.length ? "\n" : ""));
|
||||||
NODE
|
NODE
|
||||||
|
|
||||||
|
- name: Guard on remote batch branch before heavy work
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
source /tmp/proposer.env
|
||||||
|
[[ "${SKIP:-0}" != "1" ]] || { echo "Skipped"; exit 0; }
|
||||||
|
|
||||||
|
if git ls-remote --exit-code --heads origin "$BATCH_BRANCH" >/dev/null 2>&1; then
|
||||||
|
echo 'SKIP=1' >> /tmp/proposer.env
|
||||||
|
echo 'SKIP_REASON="batch_branch_exists_without_pr"' >> /tmp/proposer.env
|
||||||
|
echo "OPEN_PR_BRANCH=${BATCH_BRANCH}" >> /tmp/proposer.env
|
||||||
|
echo "Remote batch branch already exists -> skip duplicate materialization"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Remote batch branch is free"
|
||||||
|
|
||||||
- name: Comment issue if queued / skipped
|
- name: Comment issue if queued / skipped
|
||||||
if: ${{ always() }}
|
if: ${{ always() }}
|
||||||
env:
|
env:
|
||||||
@@ -306,7 +366,13 @@ jobs:
|
|||||||
MSG="Ticket queued in proposer queue. An open proposer PR already exists: ${OPEN_PR_URL:-"(URL unavailable)"}. The workflow will resume after merge on main."
|
MSG="Ticket queued in proposer queue. An open proposer PR already exists: ${OPEN_PR_URL:-"(URL unavailable)"}. The workflow will resume after merge on main."
|
||||||
;;
|
;;
|
||||||
issue_already_has_open_pr)
|
issue_already_has_open_pr)
|
||||||
MSG="This ticket already has an open proposer PR: ${OPEN_PR_URL:-"(URL unavailable)"}"
|
MSG="This batch already has an open proposer PR: ${OPEN_PR_URL:-"(URL unavailable)"}"
|
||||||
|
;;
|
||||||
|
batch_branch_exists_without_pr)
|
||||||
|
MSG="This batch already has a remote batch branch (${OPEN_PR_BRANCH:-"(unknown branch)"}). Manual inspection is required before any new proposer PR is created."
|
||||||
|
;;
|
||||||
|
batch_branch_already_materialized)
|
||||||
|
MSG="This batch was already materialized by another run on branch ${OPEN_PR_BRANCH:-"(unknown branch)"}. No duplicate PR was created."
|
||||||
;;
|
;;
|
||||||
explicit_issue_missing_chemin)
|
explicit_issue_missing_chemin)
|
||||||
MSG="Proposer Apply: cannot process this ticket automatically because field Chemin is missing or unreadable."
|
MSG="Proposer Apply: cannot process this ticket automatically because field Chemin is missing or unreadable."
|
||||||
@@ -328,6 +394,7 @@ jobs:
|
|||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
export MSG
|
||||||
node --input-type=module - <<'NODE' > /tmp/proposer.skip.comment.json
|
node --input-type=module - <<'NODE' > /tmp/proposer.skip.comment.json
|
||||||
const msg = process.env.MSG || "";
|
const msg = process.env.MSG || "";
|
||||||
process.stdout.write(JSON.stringify({ body: msg }));
|
process.stdout.write(JSON.stringify({ body: msg }));
|
||||||
@@ -384,8 +451,7 @@ jobs:
|
|||||||
git config user.email "${BOT_GIT_EMAIL:-bot@archicratie.local}"
|
git config user.email "${BOT_GIT_EMAIL:-bot@archicratie.local}"
|
||||||
|
|
||||||
START_SHA="$(git rev-parse HEAD)"
|
START_SHA="$(git rev-parse HEAD)"
|
||||||
TS="$(date -u +%Y%m%d-%H%M%S)"
|
BR="$BATCH_BRANCH"
|
||||||
BR="bot/proposer-${TARGET_PRIMARY_ISSUE}-${TS}"
|
|
||||||
echo "BRANCH=$BR" >> /tmp/proposer.env
|
echo "BRANCH=$BR" >> /tmp/proposer.env
|
||||||
git checkout -b "$BR"
|
git checkout -b "$BR"
|
||||||
|
|
||||||
@@ -518,6 +584,27 @@ jobs:
|
|||||||
--data-binary @/tmp/proposer.failure.comment.json || true
|
--data-binary @/tmp/proposer.failure.comment.json || true
|
||||||
done
|
done
|
||||||
|
|
||||||
|
- name: Late guard against duplicate batch materialization
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
source /tmp/proposer.env || true
|
||||||
|
[[ "${SKIP:-0}" != "1" ]] || exit 0
|
||||||
|
[[ "${APPLY_RC:-0}" == "0" ]] || exit 0
|
||||||
|
[[ "${REBASE_RC:-0}" == "0" ]] || exit 0
|
||||||
|
[[ "${NOOP:-0}" == "0" ]] || exit 0
|
||||||
|
|
||||||
|
REMOTE_SHA="$(git ls-remote --heads origin "$BATCH_BRANCH" | awk 'NR==1 {print $1}')"
|
||||||
|
|
||||||
|
if [[ -n "${REMOTE_SHA:-}" && "${REMOTE_SHA}" != "${END_SHA:-}" ]]; then
|
||||||
|
echo 'SKIP=1' >> /tmp/proposer.env
|
||||||
|
echo 'SKIP_REASON="batch_branch_already_materialized"' >> /tmp/proposer.env
|
||||||
|
echo "OPEN_PR_BRANCH=${BATCH_BRANCH}" >> /tmp/proposer.env
|
||||||
|
echo "Remote batch branch already exists at $REMOTE_SHA -> skip duplicate push/PR"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Late guard OK"
|
||||||
|
|
||||||
- name: Push bot branch
|
- name: Push bot branch
|
||||||
if: ${{ always() }}
|
if: ${{ always() }}
|
||||||
env:
|
env:
|
||||||
@@ -555,46 +642,74 @@ jobs:
|
|||||||
[[ "${NOOP:-0}" == "0" ]] || exit 0
|
[[ "${NOOP:-0}" == "0" ]] || exit 0
|
||||||
[[ -n "${BRANCH:-}" ]] || { echo "BRANCH unset -> skip PR"; exit 0; }
|
[[ -n "${BRANCH:-}" ]] || { echo "BRANCH unset -> skip PR"; exit 0; }
|
||||||
|
|
||||||
|
test -n "${FORGE_TOKEN:-}" || { echo "Missing FORGE_TOKEN"; exit 1; }
|
||||||
|
|
||||||
|
OPEN_PRS_JSON="$(curl -fsS \
|
||||||
|
-H "Authorization: token $FORGE_TOKEN" \
|
||||||
|
-H "Accept: application/json" \
|
||||||
|
"$API_BASE/api/v1/repos/$OWNER/$REPO/pulls?state=open&limit=100")"
|
||||||
|
|
||||||
|
export OPEN_PRS_JSON BATCH_BRANCH BATCH_KEY
|
||||||
|
|
||||||
|
EXISTING_PR_URL="$(node --input-type=module -e '
|
||||||
|
const pulls = JSON.parse(process.env.OPEN_PRS_JSON || "[]");
|
||||||
|
const branch = String(process.env.BATCH_BRANCH || "");
|
||||||
|
const key = String(process.env.BATCH_KEY || "");
|
||||||
|
const current = Array.isArray(pulls)
|
||||||
|
? pulls.find((pr) => {
|
||||||
|
const ref = String(pr?.head?.ref || "");
|
||||||
|
const body = String(pr?.body || "");
|
||||||
|
return (branch && ref === branch) || (key && body.includes(`Batch-Key: ${key}`));
|
||||||
|
})
|
||||||
|
: null;
|
||||||
|
process.stdout.write(current ? String(current.html_url || current.url || "") : "");
|
||||||
|
')"
|
||||||
|
|
||||||
|
if [[ -n "${EXISTING_PR_URL:-}" ]]; then
|
||||||
|
echo "PR already exists for this batch: $EXISTING_PR_URL"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ "${TARGET_COUNT:-0}" == "1" ]]; then
|
if [[ "${TARGET_COUNT:-0}" == "1" ]]; then
|
||||||
PR_TITLE="proposer: apply ticket #${TARGET_PRIMARY_ISSUE}"
|
PR_TITLE="proposer: apply ticket #${TARGET_PRIMARY_ISSUE}"
|
||||||
else
|
else
|
||||||
PR_TITLE="proposer: apply ${TARGET_COUNT} tickets on ${TARGET_CHEMIN}"
|
PR_TITLE="proposer: apply ${TARGET_COUNT} tickets on ${TARGET_CHEMIN}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
export TITLE="$PR_TITLE"
|
export PR_TITLE TARGET_CHEMIN TARGET_ISSUES BRANCH END_SHA DEFAULT_BRANCH OWNER BATCH_KEY
|
||||||
export CHEMIN="$TARGET_CHEMIN"
|
|
||||||
export ISSUES="$TARGET_ISSUES"
|
|
||||||
export BRANCH="$BRANCH"
|
|
||||||
export END_SHA="${END_SHA:-unknown}"
|
|
||||||
export DEFAULT_BRANCH="$DEFAULT_BRANCH"
|
|
||||||
export OWNER="$OWNER"
|
|
||||||
|
|
||||||
node --input-type=module - <<'NODE' > /tmp/proposer.pr.json
|
node --input-type=module -e '
|
||||||
const issues = String(process.env.ISSUES || "")
|
import fs from "node:fs";
|
||||||
.trim()
|
|
||||||
.split(/\s+/)
|
|
||||||
.filter(Boolean);
|
|
||||||
|
|
||||||
const body = [
|
const issues = String(process.env.TARGET_ISSUES || "")
|
||||||
`PR auto depuis ticket${issues.length > 1 ? "s" : ""} ${issues.map((n) => `#${n}`).join(", ")} (state/approved).`,
|
.trim()
|
||||||
"",
|
.split(/\s+/)
|
||||||
`- Chemin: ${process.env.CHEMIN || "(inconnu)"}`,
|
.filter(Boolean);
|
||||||
"- Tickets:",
|
|
||||||
...issues.map((n) => ` - #${n}`),
|
|
||||||
`- Branche: ${process.env.BRANCH || ""}`,
|
|
||||||
`- Commit: ${process.env.END_SHA || "unknown"}`,
|
|
||||||
"",
|
|
||||||
"Merge si CI OK."
|
|
||||||
].join("\n");
|
|
||||||
|
|
||||||
process.stdout.write(JSON.stringify({
|
const body = [
|
||||||
title: process.env.TITLE || "proposer: apply tickets",
|
`PR auto depuis ticket${issues.length > 1 ? "s" : ""} ${issues.map((n) => `#${n}`).join(", ")} (state/approved).`,
|
||||||
body,
|
"",
|
||||||
base: process.env.DEFAULT_BRANCH || "main",
|
`- Chemin: ${process.env.TARGET_CHEMIN || "(inconnu)"}`,
|
||||||
head: `${process.env.OWNER}:${process.env.BRANCH}`,
|
"- Tickets:",
|
||||||
allow_maintainer_edit: true
|
...issues.map((n) => ` - #${n}`),
|
||||||
}));
|
`- Branche: ${process.env.BRANCH || ""}`,
|
||||||
NODE
|
`- Commit: ${process.env.END_SHA || "unknown"}`,
|
||||||
|
`- Batch-Key: ${process.env.BATCH_KEY || ""}`,
|
||||||
|
"",
|
||||||
|
"Merge si CI OK."
|
||||||
|
].join("\n");
|
||||||
|
|
||||||
|
fs.writeFileSync(
|
||||||
|
"/tmp/proposer.pr.json",
|
||||||
|
JSON.stringify({
|
||||||
|
title: process.env.PR_TITLE || "proposer: apply tickets",
|
||||||
|
body,
|
||||||
|
base: process.env.DEFAULT_BRANCH || "main",
|
||||||
|
head: `${process.env.OWNER}:${process.env.BRANCH}`,
|
||||||
|
allow_maintainer_edit: true
|
||||||
|
})
|
||||||
|
);
|
||||||
|
'
|
||||||
|
|
||||||
PR_JSON="$(curl -fsS -X POST \
|
PR_JSON="$(curl -fsS -X POST \
|
||||||
-H "Authorization: token $FORGE_TOKEN" \
|
-H "Authorization: token $FORGE_TOKEN" \
|
||||||
@@ -602,10 +717,7 @@ jobs:
|
|||||||
"$API_BASE/api/v1/repos/$OWNER/$REPO/pulls" \
|
"$API_BASE/api/v1/repos/$OWNER/$REPO/pulls" \
|
||||||
--data-binary @/tmp/proposer.pr.json)"
|
--data-binary @/tmp/proposer.pr.json)"
|
||||||
|
|
||||||
PR_URL="$(node --input-type=module -e '
|
PR_URL="$(node --input-type=module -e 'const pr = JSON.parse(process.argv[1] || "{}"); console.log(pr.html_url || pr.url || "");' "$PR_JSON")"
|
||||||
const pr = JSON.parse(process.argv[1] || "{}");
|
|
||||||
console.log(pr.html_url || pr.url || "");
|
|
||||||
' "$PR_JSON")"
|
|
||||||
|
|
||||||
test -n "$PR_URL" || {
|
test -n "$PR_URL" || {
|
||||||
echo "PR URL missing. Raw: $PR_JSON"
|
echo "PR URL missing. Raw: $PR_JSON"
|
||||||
@@ -614,15 +726,21 @@ jobs:
|
|||||||
|
|
||||||
for ISSUE in $TARGET_ISSUES; do
|
for ISSUE in $TARGET_ISSUES; do
|
||||||
export ISSUE PR_URL
|
export ISSUE PR_URL
|
||||||
node --input-type=module - <<'NODE' > /tmp/proposer.issue.close.comment.json
|
|
||||||
const issue = process.env.ISSUE || "";
|
|
||||||
const url = process.env.PR_URL || "";
|
|
||||||
const msg =
|
|
||||||
`PR proposer created for ticket #${issue}: ${url}\n\n` +
|
|
||||||
`The ticket is closed automatically. Discussion can continue in the PR.`;
|
|
||||||
|
|
||||||
process.stdout.write(JSON.stringify({ body: msg }));
|
node --input-type=module -e '
|
||||||
NODE
|
import fs from "node:fs";
|
||||||
|
|
||||||
|
const issue = process.env.ISSUE || "";
|
||||||
|
const url = process.env.PR_URL || "";
|
||||||
|
const msg =
|
||||||
|
`PR proposer creee pour le ticket #${issue} : ${url}\n\n` +
|
||||||
|
`Le ticket est cloture automatiquement ; la discussion peut se poursuivre dans la PR.`;
|
||||||
|
|
||||||
|
fs.writeFileSync(
|
||||||
|
"/tmp/proposer.issue.close.comment.json",
|
||||||
|
JSON.stringify({ body: msg })
|
||||||
|
);
|
||||||
|
'
|
||||||
|
|
||||||
curl -fsS -X POST \
|
curl -fsS -X POST \
|
||||||
-H "Authorization: token $FORGE_TOKEN" \
|
-H "Authorization: token $FORGE_TOKEN" \
|
||||||
@@ -635,6 +753,17 @@ jobs:
|
|||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
"$API_BASE/api/v1/repos/$OWNER/$REPO/issues/$ISSUE" \
|
"$API_BASE/api/v1/repos/$OWNER/$REPO/issues/$ISSUE" \
|
||||||
--data-binary '{"state":"closed"}'
|
--data-binary '{"state":"closed"}'
|
||||||
|
|
||||||
|
ISSUE_STATE="$(curl -fsS \
|
||||||
|
-H "Authorization: token $FORGE_TOKEN" \
|
||||||
|
-H "Accept: application/json" \
|
||||||
|
"$API_BASE/api/v1/repos/$OWNER/$REPO/issues/$ISSUE" | \
|
||||||
|
node --input-type=module -e 'let s=""; process.stdin.on("data", d => s += d); process.stdin.on("end", () => { const j = JSON.parse(s || "{}"); process.stdout.write(String(j.state || "")); });')"
|
||||||
|
|
||||||
|
[[ "$ISSUE_STATE" == "closed" ]] || {
|
||||||
|
echo "Issue #$ISSUE is still not closed after PATCH"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
done
|
done
|
||||||
|
|
||||||
echo "PR: $PR_URL"
|
echo "PR: $PR_URL"
|
||||||
|
|||||||
@@ -1 +1,5 @@
|
|||||||
{}
|
{
|
||||||
|
"/archicrat-ia/prologue/": {
|
||||||
|
"p-0-d7974f88": "p-0-e729df02"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ source:
|
|||||||
kind: docx
|
kind: docx
|
||||||
path: "sources/docx/archicrat-ia/Prologue—Archicratie-fondation_et_finalite_sociopolitique_et_historique-version_officielle.docx"
|
path: "sources/docx/archicrat-ia/Prologue—Archicratie-fondation_et_finalite_sociopolitique_et_historique-version_officielle.docx"
|
||||||
---
|
---
|
||||||
Nous vivons dans une époque saturée de diagnostics sur les formes de domination, les mutations du pouvoir, les détournements de la souveraineté. Depuis une vingtaine d’années, les appellations s’accumulent : *démocratie illibérale*, *ploutocratie*, *happycratie*, *gouvernement algorithmique*, *démocrature*… À travers ces tentatives de nommer le désordre du présent, un fait se répète, de manière sourde : la scène politique semble désorientée. Les catégories héritées — *État*, *pouvoir*, *représentation*, *volonté générale*, *contrat social* — apparaissent de moins en moins capables de décrire ce qui nous gouverne effectivement.
|
Nous vivons une époque saturée de diagnostics sur les formes de domination, les mutations du pouvoir, les détournements de la souveraineté. Depuis une vingtaine d’années, les appellations s’accumulent : démocratie illibérale, ploutocratie, happycratie, gouvernement algorithmique, démocrature… À travers ces tentatives de nommer le désordre du présent, un fait se répète, de manière sourde : la scène politique semble désorientée. Les catégories héritées — État, pouvoir, représentation, volonté générale, contrat social — apparaissent de moins en moins capables de décrire ce qui nous gouverne effectivement.
|
||||||
|
|
||||||
C’est cette perte de prise sur le réel que ce livre souhaite prendre au sérieux. Non pour lui ajouter un terme de plus au lexique fatigué des contre-pouvoirs ou des impuissances, mais pour repartir d’un point plus fondamental, presque en-deçà de la question politique classique. Ce point, c’est celui de la *tenue d’un monde commun* — c’est-à-dire la possibilité, pour des êtres dissemblables, vulnérables, inégaux, traversés de contradictions et situés dans des temporalités hétérogènes, de coexister sans s’annihiler.
|
C’est cette perte de prise sur le réel que ce livre souhaite prendre au sérieux. Non pour lui ajouter un terme de plus au lexique fatigué des contre-pouvoirs ou des impuissances, mais pour repartir d’un point plus fondamental, presque en-deçà de la question politique classique. Ce point, c’est celui de la *tenue d’un monde commun* — c’est-à-dire la possibilité, pour des êtres dissemblables, vulnérables, inégaux, traversés de contradictions et situés dans des temporalités hétérogènes, de coexister sans s’annihiler.
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user