repowarden 4.13.2-next.1 → 4.14.0-next.5

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/garde CHANGED
@@ -152,6 +152,142 @@ fichier_ignore() {
152
152
  git -C "./$cdup" check-ignore -q -- "$prefixe$reste" 2>/dev/null
153
153
  }
154
154
 
155
+ # Vrai si le nom $1 (relatif à la racine du dépôt) est la configuration d'un
156
+ # outil de vérification : la régler est une décision du mainteneur (#518)
157
+ config_outil() {
158
+ case "$1" in
159
+ .github/actionlint.yaml | .github/actionlint.yml | .shellcheckrc | \
160
+ .markdownlint* | .yamllint* | .gitleaks.toml | .gitleaksignore | \
161
+ .pre-commit-config.yaml | .repowarden.conf) return 0 ;;
162
+ esac
163
+ return 1
164
+ }
165
+
166
+ # Vrai si le chemin $1 peut être une telle configuration : son nom seul le dit,
167
+ # avant de consulter le dépôt — un fichier ordinaire ne coûte aucune commande
168
+ config_outil_possible() {
169
+ local n="${1##*/}"
170
+ config_outil "$n" && return 0
171
+ config_outil ".github/$n"
172
+ }
173
+
174
+ # REPLY = chemin $1 relatif à la racine du dépôt : dans un sous-shell on se
175
+ # place dans son dossier existant, et on ajoute le reste au préfixe git ; hors
176
+ # dépôt, chaîne vide (les formes Windows C:/… et D:\… restent lues)
177
+ chemin_racine_r() {
178
+ local p="${1//\\//}" e prefixe reste
179
+ dossier_r "$p"
180
+ e="$REPLY"
181
+ REPLY="$(
182
+ cd "$e" 2>/dev/null || exit 0
183
+ prefixe="$(git rev-parse --show-prefix 2>/dev/null)" || exit 0
184
+ reste="${p#"$e"}"
185
+ reste="${reste#/}"
186
+ printf '%s%s' "$prefixe" "$reste"
187
+ )"
188
+ }
189
+
190
+ # Refus si le fichier $1 (depuis le dossier $2) est la configuration d'un outil
191
+ # de vérification : réglage du mainteneur, en tout lieu d'un dépôt repowarden
192
+ verifier_config_cible() {
193
+ local p="${1%$'\r'}" d
194
+ case "$p" in /* | [A-Za-z]:[/\\]*) ;; *) p="$2/$p" ;; esac
195
+ config_outil_possible "$p" || return 0
196
+ dossier_r "$2"
197
+ d="$REPLY"
198
+ (
199
+ cd "$d" 2>/dev/null || exit 0
200
+ depot_repowarden || exit 0
201
+ cfg_r agentsConfig false
202
+ [ "$REPLY" != true ] || exit 0
203
+ chemin_racine_r "$p"
204
+ config_outil "$REPLY" || exit 0
205
+ refuser garde.config_verification "$REPLY"
206
+ ) || exit $?
207
+ }
208
+
209
+ # Cibles de redirection des mots $@ (commande de $dir) : écrire la configuration
210
+ # d'un outil de vérification s'y refuse, comme par un outil d'édition
211
+ verifier_redirections() {
212
+ local mot cible attendu=""
213
+ for mot in "$@"; do
214
+ if [ -n "$attendu" ]; then
215
+ attendu=""
216
+ cible="$mot"
217
+ else
218
+ # Après le chevron (> et >> y compris) : vide = cible mot suivant
219
+ cible="${mot#*>}"
220
+ [ "$cible" != "$mot" ] || continue
221
+ while [ "${cible#>}" != "$cible" ]; do cible="${cible#>}"; done
222
+ if [ -z "$cible" ]; then
223
+ attendu=1
224
+ continue
225
+ fi
226
+ fi
227
+ verifier_config_cible "$cible" "$dir"
228
+ done
229
+ }
230
+
231
+ # Cibles écrites par une commande shell (cp, mv, install, ln, tee, touch,
232
+ # truncate, rm, sed -i, perl -i) : la configuration d'un outil s'y refuse aussi
233
+ verifier_ecriture_fichiers() {
234
+ local m mot cible="" inplace="" vu="" n d i
235
+ m="${1//[\"\']/}"
236
+ m="${m##*/}"
237
+ m="${m##*\\}"
238
+ case "$m" in
239
+ *.exe | *.EXE | *.cmd | *.CMD | *.bat | *.BAT) m="${m%.*}" ;;
240
+ esac
241
+ case "$m" in
242
+ tee | touch | truncate | rm)
243
+ # Tous les arguments non option sont des fichiers écrits
244
+ for mot in "${@:2}"; do
245
+ case "$mot" in -*) continue ;; esac
246
+ verifier_config_cible "$mot" "$dir"
247
+ done
248
+ ;;
249
+ cp | mv | install | ln)
250
+ # Le dernier argument non option est la cible ; si c'est un
251
+ # dossier existant (vu depuis $dir) ou finit par /, chaque source
252
+ # y est écrite : vérifier alors <cible>/<nom de la source>
253
+ n=0
254
+ for mot in "${@:2}"; do
255
+ case "$mot" in -*) continue ;; esac
256
+ n=$((n + 1))
257
+ cible="$mot"
258
+ done
259
+ [ "$n" -ne 0 ] || return 0
260
+ verifier_config_cible "$cible" "$dir"
261
+ d="$cible"
262
+ while [ "${d%/}" != "$d" ]; do d="${d%/}"; done
263
+ [ -n "$d" ] || d=/
264
+ case "$d" in /* | [A-Za-z]:[/\\]*) ;; *) d="$dir/$d" ;; esac
265
+ case "$cible" in */) ;; *) [ -d "$d" ] || return 0 ;; esac
266
+ i=0
267
+ for mot in "${@:2}"; do
268
+ case "$mot" in -*) continue ;; esac
269
+ i=$((i + 1))
270
+ [ "$i" -lt "$n" ] || break
271
+ verifier_config_cible "$d/${mot##*/}" "$dir"
272
+ done
273
+ ;;
274
+ sed | perl)
275
+ # Avec -i (--in-place), les arguments après le script sont modifiés
276
+ for mot in "${@:2}"; do
277
+ case "$mot" in
278
+ --in-place | --in-place=* | -i* | -[!-]*i*) inplace=1 ;;
279
+ esac
280
+ done
281
+ [ -n "$inplace" ] || return 0
282
+ for mot in "${@:2}"; do
283
+ case "$mot" in -*) continue ;; esac
284
+ if [ -z "$vu" ]; then vu=1; continue; fi
285
+ verifier_config_cible "$mot" "$dir"
286
+ done
287
+ ;;
288
+ esac
289
+ }
290
+
155
291
  # Modification du fichier $1 : seulement sur une branche de travail (de ticket,
156
292
  # si le dépôt suit ses tickets) ; un fichier ignoré par git l'est partout
157
293
  verifier_fichier() {
@@ -160,6 +296,8 @@ verifier_fichier() {
160
296
  # être en forme Windows (D:/…), absolu lui aussi
161
297
  local d="$REPLY" f="${1%$'\r'}"
162
298
  case "$f" in /* | [A-Za-z]:[/\\]*) ;; *) f="$cwd/$f" ;; esac
299
+ # Configuration d'outil de vérification : refusée avant toute autre règle
300
+ verifier_config_cible "$f" "$d"
163
301
  (
164
302
  cd "$d" 2>/dev/null || exit 0
165
303
  depot_repowarden || exit 0
@@ -587,6 +725,10 @@ verifier_shell() {
587
725
  esac
588
726
  done
589
727
  [ $# -gt 0 ] || continue
728
+ # Redirection vers la configuration d'un outil de vérification : refusée
729
+ # comme la modification du fichier, avant même la commande
730
+ verifier_redirections "$@"
731
+ verifier_ecriture_fichiers "$@"
590
732
  # Commande : dernier élément du chemin (/ ou \), sans .exe/.cmd/.bat
591
733
  m="${1//[\"\']/}"
592
734
  m="${m##*/}"
@@ -63,6 +63,7 @@ verifier() {
63
63
  # déclencherait aucun workflow (préversion, livraison, tickets).
64
64
  merge_auto() {
65
65
  local pr="$1" base="$2" brouillon="$3"
66
+ local expected online online_ok missing _rest _one
66
67
  [ "${AUTO_MERGE:-true}" = true ] && [ "$base" = "${INTEGRATION:-develop}" ] || return 0
67
68
  if [ "$brouillon" = true ]; then
68
69
  gh pr merge "$pr" --disable-auto >/dev/null 2>&1 || true
@@ -72,6 +73,49 @@ merge_auto() {
72
73
  notice_t tk.automerge_no_app "$pr"
73
74
  return 0
74
75
  fi
76
+ # Vérifications attendues (requiredChecks) vs règles en ligne : si une
77
+ # vérification attendue manque en ligne, l'auto-merge n'est pas armé, on
78
+ # signale et on commente la PR une seule fois (repère regles-perimees).
79
+ # Absence de requiredChecks : rien à comparer, comportement historique.
80
+ # Échec de lecture des règles (droits, réseau) : on arme comme avant.
81
+ cfg_r requiredChecks ""
82
+ expected="$REPLY"
83
+ if [ -n "$expected" ]; then
84
+ online_ok=0
85
+ online="$(gh api "repos/$GH_REPO/rules/branches/$base" \
86
+ --jq '.[] | select(.type=="required_status_checks") | .parameters.required_status_checks[].context' \
87
+ 2>/dev/null)" && online_ok=1 || online_ok=0
88
+ if [ "$online_ok" = 1 ]; then
89
+ missing=""
90
+ # Parcours des vérifications attendues (virgule), espaces possibles
91
+ _rest="$expected"
92
+ while [ -n "$_rest" ]; do
93
+ case "$_rest" in
94
+ *,*) _one="${_rest%%,*}"; _rest="${_rest#*,}" ;;
95
+ *) _one="$_rest"; _rest="" ;;
96
+ esac
97
+ # suppression des espaces de bord
98
+ _one="${_one#"${_one%%[![:space:]]*}"}"
99
+ _one="${_one%"${_one##*[![:space:]]}"}"
100
+ [ -z "$_one" ] && continue
101
+ if ! grep -Fxq "$_one" <<<"$online"; then
102
+ if [ -z "$missing" ]; then missing="$_one"; else missing="$missing, $_one"; fi
103
+ fi
104
+ done
105
+ if [ -n "$missing" ]; then
106
+ notice_t tk.automerge_rules_stale "$pr" "$missing" "$expected"
107
+ if ! gh pr view "$pr" --json comments -q '.comments[].body' 2>/dev/null \
108
+ | grep -qF '<!-- repowarden:regles-perimees -->'; then
109
+ _tr tk.automerge_rules_stale "$pr" "$missing" "$expected"
110
+ gh pr comment "$pr" --body "$_T"$'\n\n''<!-- repowarden:regles-perimees -->' \
111
+ >/dev/null 2>&1 || true
112
+ fi
113
+ return 0
114
+ fi
115
+ else
116
+ notice_t tk.automerge_rules_unread "$pr"
117
+ fi
118
+ fi
75
119
  if gh pr merge "$pr" --auto --squash >/dev/null 2>&1; then
76
120
  notice_t tk.automerge_armed "$pr"
77
121
  else
@@ -410,7 +410,7 @@ _msg_en() {
410
410
  ci.livraison.maintenance) _M="Maintenance only: no version to publish" ;;
411
411
  ci.livraison.non_exigee) _M="%s: prerelease not required" ;;
412
412
  ci.livraison.ok) _M="%s: prerelease tested, deliverable" ;;
413
- ci.livraison.preversion) _M="%s: no prerelease %s; wait for the evening one, or Actions → release → Run workflow on %s" ;;
413
+ ci.livraison.preversion) _M="%s: waiting for a prerelease: no %s contains this commit; wait for the evening one, or Actions → release → Run workflow on %s" ;;
414
414
  ci.install_tool.os) _M="Unsupported system: %s" ;;
415
415
  ci.install_tool.arch) _M="Unsupported architecture: %s" ;;
416
416
  ci.install_tool.unknown) _M="Unknown tool: %s" ;;
@@ -435,6 +435,9 @@ _msg_en() {
435
435
  dc.knip_file) _M="file never imported" ;;
436
436
  dc.knip_export) _M="export never imported:" ;;
437
437
  dc.knip_dep) _M="dependency never imported:" ;;
438
+ # Tests removed by a PR (tests check)
439
+ ci.check.tests_down) _M="%s: %s → %s tests (before → after); a PR does not remove tests." ;;
440
+ ci.check.tests_fix) _M=" Fix: restore the removed tests, or write it in the PR description (line \"Tests removed: <reason>\", \"Tests retirés : …\" also accepted)." ;;
438
441
  # Functions added without a comment
439
442
  cm.section) _M="Functions without a comment (new code only)" ;;
440
443
  cm.no_base) _M="No base: comment check skipped." ;;
@@ -552,6 +555,8 @@ _msg_en() {
552
555
  tk.automerge_armed) _M="PR #%s: auto-merge armed (squash), as soon as the checks are green." ;;
553
556
  tk.automerge_no_app) _M="PR #%s: auto-merge not armed, no GitHub App (repowarden app init): with the Actions token, the merge would trigger no workflow." ;;
554
557
  tk.automerge_failed) _M="PR #%s: auto-merge not armed (auto-merge not allowed on the repository: repowarden proteger)." ;;
558
+ tk.automerge_rules_stale) _M="PR #%s: auto-merge not armed (online rules stale: %s). Run: repowarden proteger --checks \"%s\"." ;;
559
+ tk.automerge_rules_unread) _M="PR #%s: online rules unreadable (permissions or network): auto-merge armed without comparing requiredChecks." ;;
555
560
  app.no_node) _M="node is required (local page creating the App): https://nodejs.org" ;;
556
561
  app.step_create) _M="Creating the GitHub App" ;;
557
562
  app.exists) _M="App already set up for this repository (client %s): nothing to recreate." ;;
@@ -749,6 +754,7 @@ _msg_en() {
749
754
  garde.detache) _M="change refused: detached HEAD in %s.\n → Switch to a ticket's branch: repowarden ticket <n>" ;;
750
755
  garde.protegee) _M="change refused: \"%s\" is a protected branch.\n → Work on a ticket's branch: repowarden ticket <n>" ;;
751
756
  garde.protegee_libre) _M="change refused: \"%s\" is a protected branch.\n → Create a work branch: git switch -c <type>/<topic>" ;;
757
+ garde.config_verification) _M="tool verification setting: the maintainer's decision; fix the code rather than the rule (\"%s\").\n → Fix the code, never the rule; only the maintainer sets the tool (setting repowarden.agentsConfig = true)." ;;
752
758
  garde.carte) _M=" → Before changing %s, once on the right branch: repowarden carte %s (functions, tests, tickets)" ;;
753
759
  garde.hors_ticket) _M="change refused: branch \"%s\" is not a ticket's branch (<type>/<n>-<topic>).\n → Take the ticket: repowarden ticket <n>" ;;
754
760
  garde.branche) _M="command refused: \"%s\" creates a branch; here, branches come from tickets.\n → repowarden ticket <n>" ;;
@@ -411,7 +411,7 @@ _msg_fr() {
411
411
  ci.livraison.maintenance) _M="Maintenance seule : aucune version à publier" ;;
412
412
  ci.livraison.non_exigee) _M="%s : préversion non exigée" ;;
413
413
  ci.livraison.ok) _M="%s : préversion testée, livrable" ;;
414
- ci.livraison.preversion) _M="%s : aucune préversion %s ; attendre celle du soir, ou Actions → release → Run workflow sur %s" ;;
414
+ ci.livraison.preversion) _M="%s : en attente de préversion : aucune %s ne contient ce commit ; attendre celle du soir, ou Actions → release → Run workflow sur %s" ;;
415
415
  ci.install_tool.os) _M="Système non supporté : %s" ;;
416
416
  ci.install_tool.arch) _M="Architecture non supportée : %s" ;;
417
417
  ci.install_tool.unknown) _M="Outil inconnu : %s" ;;
@@ -436,6 +436,9 @@ _msg_fr() {
436
436
  dc.knip_file) _M="fichier jamais importé" ;;
437
437
  dc.knip_export) _M="export jamais importé :" ;;
438
438
  dc.knip_dep) _M="dépendance jamais importée :" ;;
439
+ # Tests retirés par une PR (vérification tests)
440
+ ci.check.tests_down) _M="%s : %s → %s tests (avant → après) ; une PR ne retire pas de tests." ;;
441
+ ci.check.tests_fix) _M=" Corriger : remettre les tests retirés, ou l'écrire dans la description de la PR (ligne « Tests retirés : <raison> », « Tests removed: … » en anglais)." ;;
439
442
  # Fonctions ajoutées sans commentaire
440
443
  cm.section) _M="Fonctions sans commentaire (nouveau code uniquement)" ;;
441
444
  cm.no_base) _M="Pas de base : contrôle des commentaires ignoré." ;;
@@ -553,6 +556,8 @@ _msg_fr() {
553
556
  tk.automerge_armed) _M="PR #%s : merge automatique armé (squash), dès que les vérifications sont vertes." ;;
554
557
  tk.automerge_no_app) _M="PR #%s : merge automatique non armé, pas de GitHub App (repowarden app init) : avec le jeton des Actions, le merge ne déclencherait aucun workflow." ;;
555
558
  tk.automerge_failed) _M="PR #%s : merge automatique non armé (dépôt sans merge automatique autorisé : repowarden proteger)." ;;
559
+ tk.automerge_rules_stale) _M="PR #%s : merge automatique non armé (règles en ligne périmées : %s). Lancer : repowarden proteger --checks \"%s\"." ;;
560
+ tk.automerge_rules_unread) _M="PR #%s : règles en ligne illisibles (droits ou réseau) : merge automatique armé sans comparer requiredChecks." ;;
556
561
  app.no_node) _M="node est nécessaire (page locale de création de l'App) : https://nodejs.org" ;;
557
562
  app.step_create) _M="Création de la GitHub App" ;;
558
563
  app.exists) _M="App déjà configurée pour ce dépôt (client %s) : rien à recréer." ;;
@@ -750,6 +755,7 @@ _msg_fr() {
750
755
  garde.detache) _M="modification refusée : HEAD détaché dans %s.\n → Se placer sur la branche d'un ticket : repowarden ticket <n>" ;;
751
756
  garde.protegee) _M="modification refusée : « %s » est une branche protégée.\n → Travailler sur la branche d'un ticket : repowarden ticket <n>" ;;
752
757
  garde.protegee_libre) _M="modification refusée : « %s » est une branche protégée.\n → Créer une branche de travail : git switch -c <type>/<sujet>" ;;
758
+ garde.config_verification) _M="réglage d'un outil de vérification : décision du mainteneur ; corriger le code plutôt que la règle (« %s »).\n → Corriger le code, jamais la règle ; seul le mainteneur règle l'outil (réglage repowarden.agentsConfig = true)." ;;
753
759
  garde.carte) _M=" → Avant de modifier %s, une fois sur la bonne branche : repowarden carte %s (fonctions, tests, tickets)" ;;
754
760
  garde.hors_ticket) _M="modification refusée : la branche « %s » n'est pas celle d'un ticket (<type>/<n>-<sujet>).\n → Prendre le ticket : repowarden ticket <n>" ;;
755
761
  garde.branche) _M="commande refusée : « %s » crée une branche ; ici, la branche naît du ticket.\n → repowarden ticket <n>" ;;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "repowarden",
3
- "version": "4.13.2-next.1",
3
+ "version": "4.14.0-next.5",
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",