repowarden 4.3.0-next.4 → 4.3.0-next.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/proteger CHANGED
@@ -14,7 +14,7 @@
14
14
  # [--sans-tags] [--dry-run]
15
15
  # --sans-tags : dépôt qui déplace lui-même un tag (tag majeur flottant v2…).
16
16
  # Réglages équivalents dans .repowarden.conf (requiredReviews, codeOwnerReview,
17
- # unattributedApproval, environment, environmentReviewers). Demande un gh connecté, admin du dépôt.
17
+ # unattributedApproval, deliveryApproval, environment, environmentReviewers). Demande un gh connecté, admin du dépôt.
18
18
  set -euo pipefail
19
19
 
20
20
  ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)"
@@ -94,7 +94,8 @@ refs_json() { # branches → "refs/heads/a", "refs/heads/b"
94
94
 
95
95
  # Ruleset de branches : $1 nom, $2 branches (liste), $3 méthodes de merge (JSON),
96
96
  # $4 PR à jour exigée (défaut true), $5 approbations (défaut REVIEWS),
97
- # $6 dernier push approuvé par un autre (défaut selon REVIEWS)
97
+ # $6 dernier push approuvé par un autre (défaut selon REVIEWS),
98
+ # $7 règle supplémentaire (JSON), ex. déploiement réussi exigé
98
99
  branch_ruleset() {
99
100
  # shellcheck disable=SC2086 # liste de branches volontairement découpée
100
101
  cat <<JSON
@@ -118,7 +119,8 @@ branch_ruleset() {
118
119
  {"type": "required_status_checks", "parameters": {
119
120
  "strict_required_status_checks_policy": ${4:-true},
120
121
  "do_not_enforce_on_create": false,
121
- "required_status_checks": [$contexts]}}
122
+ "required_status_checks": [$contexts]}}${7:+,
123
+ $7}
122
124
  ]
123
125
  }
124
126
  JSON
@@ -161,12 +163,25 @@ if [ -n "$integration" ]; then
161
163
  # le mainteneur peut donc l'approuver), sans exiger un autre approbateur
162
164
  # que l'auteur du dernier push sauf si des relectures sont configurées
163
165
  livraison=$((REVIEWS > 0 ? REVIEWS : 1))
166
+ # deliveryApproval = deployment (défaut) : la validation humaine est
167
+ # l'approbation de l'environnement de livraison (« Approve and deploy »),
168
+ # exigée par une règle de main (déploiement réussi) ; la revue de PR n'est
169
+ # plus nécessaire (relectures configurées : gardées). review : revue de PR.
170
+ cfg_r deliveryApproval deployment
171
+ DELIVERY="$REPLY"
172
+ [[ "$DELIVERY" =~ ^(deployment|review)$ ]] || { err_t ci.proteger.delivery_invalid; exit 1; }
173
+ deploiement=""
174
+ if [ "$DELIVERY" = deployment ]; then
175
+ ENVIRONMENT="${ENVIRONMENT:-production}"
176
+ livraison="$REVIEWS"
177
+ deploiement="{\"type\": \"required_deployments\", \"parameters\": {\"required_deployment_environments\": [\"$ENVIRONMENT\"]}}"
178
+ fi
164
179
  # PR à jour non exigée non plus : le merge commit d'une livraison n'existe
165
180
  # que sur main ; strict, la livraison suivante serait bloquée tant que
166
181
  # develop ne l'a pas reçu (retour main → develop vide après chaque
167
182
  # release). Une livraison n'apporte que develop, vérifiée par la CI ; le
168
183
  # retour reste fait quand main a un contenu propre (correctif direct).
169
- [ -z "$others" ] || rulesets+=("$(branch_ruleset repowarden "$others" '"merge"' false "$livraison" "$last_push")")
184
+ [ -z "$others" ] || rulesets+=("$(branch_ruleset repowarden "$others" '"merge"' false "$livraison" "$last_push" "$deploiement")")
170
185
  # Branche d'intégration : PR à jour non exigée. Le retour de main (après
171
186
  # une release) serait sinon presque toujours en retard, et « mettre à jour »
172
187
  # fusionnerait develop dans main en contournant la livraison ; la CI
@@ -215,7 +230,11 @@ echo_t ci.proteger.1 "$REPO"
215
230
  echo_t ci.proteger.2 "$existing"
216
231
  echo_t ci.proteger.3 "$CHECKS"
217
232
  echo_t ci.proteger.4 "$methods" "$delete_on_merge"
218
- [ -z "$integration" ] || echo_t ci.proteger.delivery "$livraison" "$auto_merge"
233
+ if [ -n "$integration" ] && [ "$DELIVERY" = deployment ]; then
234
+ echo_t ci.proteger.delivery_deploy "$ENVIRONMENT" "$auto_merge"
235
+ elif [ -n "$integration" ]; then
236
+ echo_t ci.proteger.delivery "$livraison" "$auto_merge"
237
+ fi
219
238
  echo_t ci.proteger.5 "$tags_msg"
220
239
  [ -z "$integration" ] || echo_t ci.proteger.6 "$integration"
221
240
  echo_t ci.proteger.7 "$REVIEWS" "$CODEOWNERS" "$UNATTRIBUTED"
package/hooks/commit-msg CHANGED
@@ -8,6 +8,29 @@ exit_if_skipped
8
8
  run_local_hook "$@"
9
9
 
10
10
  MSG_FILE="$1"
11
+
12
+ # Agents IA : signatures retirées du message, identité d'agent refusée
13
+ if agents_refused; then
14
+ agent_lines_r "$(cat "$MSG_FILE")"
15
+ if [ -n "$REPLY" ]; then
16
+ while IFS= read -r ligne; do attention_t hook.agent_stripped "$ligne"; done <<<"$REPLY"
17
+ # grep -a : texte même avec un emoji (Windows le croirait binaire) ;
18
+ # contenu réécrit dans le même fichier (git le garde ouvert sous Windows)
19
+ filtre="$(LC_ALL=C grep -aviE "$AGENT_LINE_ERE" "$MSG_FILE" || true)"
20
+ printf '%s\n' "$filtre" >"$MSG_FILE"
21
+ fi
22
+ for ident in GIT_AUTHOR_IDENT GIT_COMMITTER_IDENT; do
23
+ id="$(git var "$ident" 2>/dev/null || true)"
24
+ nom="${id%% <*}"
25
+ mail="${id#*<}"
26
+ mail="${mail%%>*}"
27
+ if agent_identity "$nom" "$mail"; then
28
+ err_t hook.agent_identity "$nom <$mail>"
29
+ echo_t hook.agent_identity_fix
30
+ exit 1
31
+ fi
32
+ done
33
+ fi
11
34
  # Première ligne non commentée
12
35
  SUBJECT="$(grep -v '^#' "$MSG_FILE" | sed -n 1p)"
13
36
 
@@ -169,6 +169,28 @@ persistent_pr_r() {
169
169
  esac
170
170
  }
171
171
 
172
+ # Agents IA (code écrit pour quelqu'un) : ni auteur ni committer d'un commit,
173
+ # ni signature dans un message (Co-Authored-By, « Generated with »). Les bots
174
+ # d'automatisation (github-actions, App repowarden, Dependabot) ne sont pas
175
+ # concernés. Réglage allowAgentSignatures = true pour les autoriser.
176
+ AGENT_EMAIL_RE='^(noreply@anthropic\.com|[0-9]+\+[Cc]opilot@users\.noreply\.github\.com|cursoragent@cursor\.com|devin-ai-integration\[bot\]@users\.noreply\.github\.com)$'
177
+ AGENT_NAME_RE='^(Claude|Claude Code|Copilot|Cursor Agent|Devin AI)$'
178
+ # Lignes de signature (grep -iE, insensible à la casse)
179
+ # (locale C : un emoji en tête de ligne, comme « 🤖 Generated with », reste
180
+ # des octets quelconques, y compris sous Windows ; motifs en ASCII)
181
+ AGENT_LINE_ERE='(^[[:space:]]*co-authored-by:.*(anthropic\.com|copilot@users\.noreply\.github\.com|cursor\.com|devin-ai-integration))|(generated (with|by) \[?(claude|github copilot|copilot|cursor|devin))'
182
+
183
+ # Vrai si les signatures d'agents sont refusées (défaut)
184
+ agents_refused() { cfg_r allowAgentSignatures false; [ "$REPLY" != true ]; }
185
+
186
+ # Vrai si l'identité $1 (nom) / $2 (e-mail) est celle d'un agent IA
187
+ agent_identity() { [[ "$2" =~ $AGENT_EMAIL_RE || "$1" =~ $AGENT_NAME_RE ]]; }
188
+
189
+ # REPLY = premières lignes de signature d'agent du texte $1 (vide si aucune)
190
+ agent_lines_r() {
191
+ REPLY="$(LC_ALL=C grep -aiE "$AGENT_LINE_ERE" <<<"$1" || true)"
192
+ }
193
+
172
194
  # Vrai si $1 est un en-tête conforme (format et 72 caractères)
173
195
  header_valid() {
174
196
  [[ "$1" =~ $CC_PATTERN ]] || return 1
@@ -231,6 +231,13 @@ _msg_en() {
231
231
  ci.check.25) _M="Disabled by .repowarden.conf (skip %s)." ;;
232
232
  ci.check.26) _M="Failed:%s" ;;
233
233
  ci.check.27) _M="All repowarden checks passed." ;;
234
+ hook.agent_stripped) _M="AI agent signature removed from the message: %s" ;;
235
+ hook.agent_identity) _M="Commit refused: the author or committer is an AI agent (%s). Commits carry a person's identity." ;;
236
+ hook.agent_identity_fix) _M=" Fix: git config user.name \"Your Name\" && git config user.email you@mail, then commit again. To allow agents: git config repowarden.allowAgentSignatures true" ;;
237
+ ci.check.agent_identity) _M="Commit %s: AI agent author or committer (%s); commits carry a person's identity." ;;
238
+ ci.check.agent_line) _M="Commit %s: AI agent signature in the message (%s)." ;;
239
+ ci.check.agent_pr_body) _M="PR description: AI agent signature (%s); it would become the commit message when squashing, remove it." ;;
240
+ ci.check.agent_fix) _M="Rewrite the affected commits with a person's identity, without agent signature (setting repowarden.allowAgentSignatures = true to allow them)." ;;
234
241
  ci.check.inverted) _M="PR from \"%s\" to \"%s\": a persistent branch only goes out for a delivery (develop → main) or a back-merge (main → develop). Pointless PR: nothing to check, it is closed automatically." ;;
235
242
  ci.check.inverted_fix) _M="To update a work branch: on that branch, git merge origin/%s, then git push." ;;
236
243
  ci.check.back_merge) _M="Back-merge of %s into %s (merge commit): the PR title is not checked." ;;
@@ -263,6 +270,8 @@ _msg_en() {
263
270
  ci.proteger.13) _M="Settings applied." ;;
264
271
  ci.proteger.reviews_invalid) _M="--relecteurs: a number from 0 to 9 is expected." ;;
265
272
  ci.proteger.codeowners_invalid) _M="codeOwnerReview: true or false." ;;
273
+ ci.proteger.delivery_deploy) _M=" delivery into main: approval of the \"%s\" deployment (Approve and deploy), then merged by the GitHub App; auto-merge of work PRs into develop: %s" ;;
274
+ ci.proteger.delivery_invalid) _M="deliveryApproval: deployment or review." ;;
266
275
  ci.proteger.automerge_invalid) _M="autoMergeWork: true or false." ;;
267
276
  ci.proteger.unattributed_invalid) _M="unattributedApproval: true or false." ;;
268
277
  ci.proteger.no_branch) _M="None of the branches \"%s\" exists on %s." ;;
@@ -231,6 +231,13 @@ _msg_fr() {
231
231
  ci.check.25) _M="Désactivé par .repowarden.conf (skip %s)." ;;
232
232
  ci.check.26) _M="Échec :%s" ;;
233
233
  ci.check.27) _M="Toutes les vérifications repowarden sont passées." ;;
234
+ hook.agent_stripped) _M="Signature d'agent IA retirée du message : %s" ;;
235
+ hook.agent_identity) _M="Commit refusé : l'auteur ou le committer est un agent IA (%s). Les commits portent l'identité d'une personne." ;;
236
+ hook.agent_identity_fix) _M=" Corriger : git config user.name \"Ton Nom\" && git config user.email ton@mail, puis recommencer le commit. Pour autoriser les agents : git config repowarden.allowAgentSignatures true" ;;
237
+ ci.check.agent_identity) _M="Commit %s : auteur ou committer agent IA (%s) ; les commits portent l'identité d'une personne." ;;
238
+ ci.check.agent_line) _M="Commit %s : signature d'agent IA dans le message (%s)." ;;
239
+ ci.check.agent_pr_body) _M="Description de la PR : signature d'agent IA (%s) ; elle deviendrait le message du commit en squash, à retirer." ;;
240
+ ci.check.agent_fix) _M="Réécrire les commits concernés avec l'identité d'une personne, sans signature d'agent (réglage repowarden.allowAgentSignatures = true pour les autoriser)." ;;
234
241
  ci.check.inverted) _M="PR de « %s » vers « %s » : une branche persistante ne part que pour une livraison (develop → main) ou un retour (main → develop). PR sans objet : rien à vérifier, elle est fermée automatiquement." ;;
235
242
  ci.check.inverted_fix) _M="Pour mettre une branche de travail à jour : sur cette branche, git merge origin/%s, puis git push." ;;
236
243
  ci.check.back_merge) _M="Retour de %s dans %s (merge commit) : le titre de la PR n'est pas vérifié." ;;
@@ -263,6 +270,8 @@ _msg_fr() {
263
270
  ci.proteger.13) _M="Réglages appliqués." ;;
264
271
  ci.proteger.reviews_invalid) _M="--relecteurs : nombre de 0 à 9 attendu." ;;
265
272
  ci.proteger.codeowners_invalid) _M="codeOwnerReview : true ou false." ;;
273
+ ci.proteger.delivery_deploy) _M=" livraison vers main : approbation du déploiement « %s » (Approve and deploy), puis merge par la GitHub App ; merge automatique des PR de travail vers develop : %s" ;;
274
+ ci.proteger.delivery_invalid) _M="deliveryApproval : deployment ou review." ;;
266
275
  ci.proteger.automerge_invalid) _M="autoMergeWork : true ou false." ;;
267
276
  ci.proteger.unattributed_invalid) _M="unattributedApproval : true ou false." ;;
268
277
  ci.proteger.no_branch) _M="Aucune des branches « %s » n'existe sur %s." ;;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "repowarden",
3
- "version": "4.3.0-next.4",
3
+ "version": "4.3.0-next.6",
4
4
  "description": "Hooks git et CI multi-langages : messages de commit, nommage des branches, secrets, formatage, tests ciblés, code mort, releases.",
5
5
  "keywords": [
6
6
  "git-hooks",