repowarden 4.10.2 → 4.11.0-next.21

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.
@@ -4,20 +4,8 @@
4
4
  # Chargé par ci/tickets.sh (après hooks/lib/common.sh et hooks/lib/tickets.sh).
5
5
  # shellcheck disable=SC2154 # statuts, réglages et compteurs définis dans les autres modules
6
6
 
7
- # Registre des branches d'un ticket : repère invisible dans les commentaires
8
- # du bot. GitHub ne lie une branche au ticket qu'à sa création, et une branche
9
- # liée supprimée y perd son nom : le registre garde le nom de chaque branche.
10
- REPERE="repowarden:branche"
11
-
12
- repere() { printf '<!-- %s %s -->' "$REPERE" "$1"; }
13
-
14
- # REPLY = branches inscrites sur le ticket $1 (séparées par des espaces)
15
- branches_inscrites_r() {
16
- local corps
17
- corps="$(gh issue view "$1" --json comments -q '.comments[].body' 2>/dev/null || true)"
18
- REPLY="$(grep -oE "<!-- $REPERE [^ ]+ -->" <<<"$corps" | awk '{ printf "%s ", $3 }' || true)"
19
- REPLY="${REPLY% }"
20
- }
7
+ # Registre (REPERE, repere, branches_inscrites_r) : hooks/lib/tickets.sh,
8
+ # partagé avec repowarden ticket.
21
9
 
22
10
  # Inscrit la branche $2 sur le ticket $1, une seule fois ($3 : origine, ex. PR #7).
23
11
  # Une seconde branche pour le même ticket est inscrite et signalée.
@@ -34,7 +22,7 @@ inscrire_branche() {
34
22
  _tr tk.branch_registered "$b"
35
23
  fi
36
24
  [ -z "$origine" ] || _T="$_T ($origine)"
37
- gh issue comment "$n" --body "$_T"$'\n\n'"$(repere "$b")" >/dev/null
25
+ commenter "$n" "$_T"$'\n\n'"$(repere "$b")"
38
26
  INSCRITES=$((INSCRITES + 1))
39
27
  }
40
28
 
@@ -65,19 +53,23 @@ nettoyer_branche() {
65
53
  notice_t tk.branch_deleted "$branche" "$n"
66
54
  else
67
55
  _tr tk.branch_kept "$branche" "${avance:-?}" "${prs:--}"
68
- gh issue comment "$n" --body "$_T" >/dev/null
56
+ commenter "$n" "$_T"
69
57
  fi
70
58
  }
71
59
 
72
60
  # Ticket $1 validé : sa branche naît de la branche d'intégration, liée au
73
- # ticket (panneau « Development »). Rien si elle existe déjà, ou si une PR
74
- # ouverte cite déjà le ticket (travail commencé hors de sa branche).
61
+ # ticket (panneau « Development »). Rien si une PR ouverte cite déjà le ticket
62
+ # (travail commencé hors de sa branche) ; si elle existe déjà, jamais recréée :
63
+ # faite avant la validation, elle n'est pas liée et son lien est inscrit.
75
64
  creer_branche() {
76
65
  local n="$1" titre labels branche pr head body
77
66
  # Travail déjà mergé ou en production : plus de branche à (re)créer
78
67
  travail_termine "$(etiquettes "$n")" && return 0
79
68
  branche_du_ticket_r "$n"
80
- [ -z "$REPLY" ] || return 0
69
+ if [ -n "$REPLY" ]; then
70
+ ticket_note_unlinked_branch "$n" "$REPLY" || true
71
+ return 0
72
+ fi
81
73
  while IFS=$'\t' read -r pr head; do
82
74
  [ -n "$pr" ] || continue
83
75
  body="$(gh pr view "$pr" --json body -q .body)"
@@ -97,6 +89,6 @@ creer_branche() {
97
89
  -f oid="$(gh api "repos/$GH_REPO/git/ref/heads/$INTEGRATION" -q .object.sha)" \
98
90
  -f name="$branche" >/dev/null
99
91
  _tr tk.branch_created "$branche" "$INTEGRATION" "$n" "$branche"
100
- gh issue comment "$n" --body "$_T"$'\n\n'"$(repere "$branche")" >/dev/null
92
+ commenter "$n" "$_T"$'\n\n'"$(repere "$branche")"
101
93
  notice_t tk.branch_notice "$branche" "$n"
102
94
  }
@@ -1,20 +1,31 @@
1
1
  # shellcheck shell=bash
2
- # Suivi des tickets, préversion et release (ci/tickets.sh release) : tickets
3
- # prévenus, ou passés en done et fermés.
2
+ # Suivi des tickets, préversion, release ou livraison sans nouvelle version
3
+ # (ci/tickets.sh release) : tickets prévenus, ou passés en done et fermés.
4
4
  # Chargé par ci/tickets.sh (après hooks/lib/common.sh et hooks/lib/tickets.sh).
5
5
  # shellcheck disable=SC2154 # statuts, réglages et compteurs définis dans les autres modules
6
6
 
7
+ # TAG : release (vX.Y.Z) ou préversion (vX.Y.Z-next.N) ; sans TAG,
8
+ # DELIVERED_SHA : commit livré sur la branche principale sans nouvelle version
9
+ # (livraison sans fix ni feat), les tickets livrés sont fermés tout de même
7
10
  cmd_release() {
8
- local n
11
+ local n tag="${TAG:-}" sha="${DELIVERED_SHA:-}"
12
+ if [ -n "$tag" ]; then
13
+ if [[ "$tag" == *-* ]]; then _tr tk.prerelease "$tag"; else _tr tk.released "$tag"; fi
14
+ elif [ -n "$sha" ]; then
15
+ _tr tk.delivered "${MAIN:-main}" "${sha:0:7}"
16
+ else
17
+ echo "ci/tickets.sh release : TAG ou DELIVERED_SHA requis" >&2
18
+ return 2
19
+ fi
20
+ local msg="$_T"
9
21
  while IFS= read -r n; do
10
22
  [ -n "$n" ] || continue
11
- if [[ "$TAG" == *-* ]]; then
12
- _tr tk.prerelease "$TAG"
13
- gh issue comment "$n" --body "$_T" >/dev/null
23
+ if [[ "$tag" == *-* ]]; then
24
+ commenter "$n" "$msg"
14
25
  else
15
- _tr tk.released "$TAG"
16
26
  statut "$n" "$S_DONE"
17
- gh issue close "$n" --comment "$_T" >/dev/null
27
+ pied_r "$msg"
28
+ gh issue close "$n" --comment "$REPLY" >/dev/null
18
29
  nettoyer_branche "$n"
19
30
  fi
20
31
  done < <(gh issue list --state open --label "$S_PREPROD" --limit 500 --json number -q '.[].number')
package/ci/tickets.sh CHANGED
@@ -12,8 +12,9 @@
12
12
  # ci/tickets.sh push premier push sur la branche d'un ticket : PR
13
13
  # brouillon ouverte, ticket en cours
14
14
  # ci/tickets.sh revue corrections demandées en revue → en cours
15
- # ci/tickets.sh release release publiée : tickets en préprod → done (fermés),
16
- # ou prévenus de la préversion
15
+ # ci/tickets.sh release release publiée, ou livraison sur la branche
16
+ # principale sans nouvelle version : tickets en
17
+ # préprod → done (fermés) ; ou prévenus de la préversion
17
18
  # ci/tickets.sh adopter mise en route (tickets init) : tickets existants
18
19
  # rangés, branches des PR inscrites sur leurs tickets
19
20
  #
@@ -24,10 +25,10 @@
24
25
  # feat/12-sujet. « Closes #12 » fermerait le ticket dès le merge dans develop :
25
26
  # c'est la release qui le ferme.
26
27
  #
27
- # Variables : GH_TOKEN, GH_REPO, INTEGRATION ; pr : PR, ACTION, MERGED, DRAFT,
28
- # HEAD, BASE, HEAD_SHA, AUTHOR ; issue : ISSUE, ACTION, LABEL, STATE_REASON ;
28
+ # Variables : GH_TOKEN, GH_REPO, INTEGRATION, APP_SLUG (pied des commentaires) ;
29
+ # pr : PR, ACTION, MERGED, DRAFT, HEAD, BASE, HEAD_SHA, AUTHOR ; issue : ISSUE, ACTION, LABEL, STATE_REASON ;
29
30
  # push : PUSH_BRANCH, DELETED, PUSHER, SHA ; revue : PR, HEAD, REVIEW_STATE ;
30
- # release : TAG
31
+ # release : TAG, ou DELIVERED_SHA et MAIN (livraison sans nouvelle version)
31
32
  set -euo pipefail
32
33
 
33
34
  REPOWARDEN_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)"
package/hooks/commit-msg CHANGED
@@ -74,13 +74,6 @@ Expected format: <type>(<optional scope>): <description>
74
74
  refactor!: change the public API (! = breaking change)
75
75
 
76
76
  Types:
77
- EOF
78
- types_help
79
- cat >&2 <<EOF
80
-
81
- 👉 Tip: name your branch <type>/<topic> (e.g. feat/user) and the prefix
82
- is added automatically: git commit -m "add sign-up" (or use git cc)
83
- Your message is not lost: git commit -e -F .git/COMMIT_EDITMSG
84
77
  EOF
85
78
  else
86
79
  cat >&2 <<EOF
@@ -93,9 +86,22 @@ Format attendu : <type>(<scope optionnel>): <description>
93
86
 
94
87
  Types :
95
88
  EOF
96
- types_help
89
+ fi
90
+ types_help
91
+ echo >&2
92
+ # HEAD détachée (bisect, essai, rebase) : pas de branche dont tirer le
93
+ # préfixe, le conseil de nommage ne s'applique pas
94
+ if ! git symbolic-ref -q HEAD >/dev/null; then
95
+ echo_t hook.commit_msg.detached >&2
96
+ echo_t hook.commit_msg.kept >&2
97
+ elif [ "${REPOWARDEN_LANG:-}" = en ]; then
98
+ cat >&2 <<EOF
99
+ 👉 Tip: name your branch <type>/<topic> (e.g. feat/user) and the prefix
100
+ is added automatically: git commit -m "add sign-up" (or use git cc)
101
+ Your message is not lost: git commit -e -F .git/COMMIT_EDITMSG
102
+ EOF
103
+ else
97
104
  cat >&2 <<EOF
98
-
99
105
  👉 Astuce : nomme ta branche <type>/<sujet> (ex. feat/user) et le préfixe
100
106
  sera ajouté automatiquement : git commit -m "ajoute l'inscription" (ou git cc)
101
107
  Ton message n'est pas perdu : git commit -e -F .git/COMMIT_EDITMSG
@@ -0,0 +1,123 @@
1
+ #!/usr/bin/env bash
2
+ # Fonctions ajoutées sans commentaire : chaque nouvelle fonction doit dire,
3
+ # juste au-dessus, ce qu'elle fait et pourquoi (docstring acceptée en Python).
4
+ # Comme pour le code mort, seul le code ajouté compte : une fonction dont le
5
+ # nom existait déjà dans le fichier n'est jamais signalée, même retouchée.
6
+ # Avertissement seulement (pre-commit et CI), désactivable par skip = comments.
7
+ # Langages : Shell (.sh, .bash, ou shebang sh/bash), Python, JavaScript/TypeScript.
8
+
9
+ # Une ligne « fichier:ligne<TAB>nom » par fonction ajoutée sans commentaire
10
+ # entre $1 (commit de base) et $2 (commit, ou vide pour le contenu stagé).
11
+ comments_missing() {
12
+ local base="$1" new="$2" st a b f old lang added tmp
13
+ tmp="$(mktemp -d)"
14
+ while IFS=$'\t' read -r st a b; do
15
+ [ -n "$a" ] || continue
16
+ # Renommage ou copie : l'ancien chemin donne les fonctions existantes
17
+ if [ -n "$b" ]; then old="$a" f="$b"; else old="$a" f="$a"; fi
18
+ is_excluded "$f" && continue
19
+ case "${f##*/}" in
20
+ *.sh | *.bash) lang=shell ;;
21
+ *.py) lang=py ;;
22
+ *.js | *.jsx | *.mjs | *.cjs | *.ts | *.tsx) lang=js ;;
23
+ *.*) continue ;;
24
+ *) lang=shebang ;;
25
+ esac
26
+ git show "$new:$f" >"$tmp/new" 2>/dev/null || continue
27
+ if [ "$lang" = shebang ]; then
28
+ # Script sans extension (bin/…) : seul un shebang sh ou bash compte
29
+ IFS= read -r st <"$tmp/new" || true
30
+ [[ "$st" =~ ^#!.*[/\ ](ba)?sh([[:space:]]|$) ]] || continue
31
+ lang=shell
32
+ fi
33
+ git show "$base:$old" >"$tmp/base" 2>/dev/null || : >"$tmp/base"
34
+ if [ -n "$new" ]; then
35
+ git diff -U0 --no-color --no-ext-diff "$base" "$new" -- "$f"
36
+ else
37
+ git diff --cached -U0 --no-color --no-ext-diff "$base" -- "$f"
38
+ fi 2>/dev/null >"$tmp/diff"
39
+ # Numéros des lignes ajoutées ou modifiées, d'après les en-têtes @@
40
+ added="$(awk '/^@@/ {
41
+ split($3, a, ","); s = substr(a[1], 2); n = (a[2] == "" ? 1 : a[2])
42
+ for (i = 0; i < n; i++) printf " %d", s + i
43
+ }' "$tmp/diff") "
44
+ [ "$added" != " " ] || continue
45
+ awk -v lang="$lang" -v added="$added" -v f="$f" -v q="'" "$COMMENTS_AWK" "$tmp/base" "$tmp/new"
46
+ done < <(
47
+ if [ -n "$new" ]; then
48
+ git diff --name-status -M --diff-filter=ACMR "$base" "$new"
49
+ else
50
+ git diff --cached --name-status -M --diff-filter=ACMR "$base"
51
+ fi 2>/dev/null
52
+ )
53
+ rm -rf "$tmp"
54
+ }
55
+
56
+ # Avertit des fonctions ajoutées sans commentaire, sans jamais bloquer : le
57
+ # commentaire aide la relecture, son absence n'est pas une faute. REPLY = nombre.
58
+ comments_check() {
59
+ local loc name n=0
60
+ while IFS=$'\t' read -r loc name; do
61
+ [ -n "$loc" ] || continue
62
+ n=$((n + 1))
63
+ attention_t cm.missing "$loc" "$name"
64
+ done < <(comments_missing "$1" "$2")
65
+ [ "$n" = 0 ] || info_t cm.summary "$n"
66
+ REPLY="$n"
67
+ }
68
+
69
+ # Programme awk de comments_missing : lit le fichier de base (noms des
70
+ # fonctions existantes) puis le nouveau, et signale les fonctions nouvelles
71
+ # dont aucune ligne de commentaire ne précède la déclaration
72
+ # shellcheck disable=SC2016 # programme awk, rien à développer
73
+ COMMENTS_AWK='
74
+ # Nom de la fonction déclarée par la ligne l (vide sinon), selon le langage
75
+ function nom(l) {
76
+ if (lang == "shell") {
77
+ if (l ~ /^[ \t]*function[ \t]+[A-Za-z_]/) {
78
+ sub(/^[ \t]*function[ \t]+/, "", l); sub(/[ \t(){}].*$/, "", l); return l
79
+ }
80
+ if (l ~ /^[ \t]*[A-Za-z_][A-Za-z0-9_:.@-]*[ \t]*\(\)/) {
81
+ sub(/^[ \t]*/, "", l); sub(/[ \t]*\(\).*$/, "", l); return l
82
+ }
83
+ } else if (lang == "py") {
84
+ if (l ~ /^[ \t]*(async[ \t]+)?def[ \t]+[A-Za-z_]/) {
85
+ sub(/^[ \t]*(async[ \t]+)?def[ \t]+/, "", l); sub(/[^A-Za-z0-9_].*$/, "", l); return l
86
+ }
87
+ } else {
88
+ if (l ~ /^[ \t]*(export[ \t]+(default[ \t]+)?)?(async[ \t]+)?function[ \t]*\*?[ \t]*[A-Za-z_$]/) {
89
+ sub(/^[ \t]*(export[ \t]+(default[ \t]+)?)?(async[ \t]+)?function[ \t]*\*?[ \t]*/, "", l)
90
+ sub(/[^A-Za-z0-9_$].*$/, "", l); return l
91
+ }
92
+ if (l ~ /^[ \t]*(export[ \t]+)?(const|let|var)[ \t]+[A-Za-z_$][A-Za-z0-9_$]*[ \t]*(:[^=]*)?=[ \t]*(async[ \t]+)?(function[^A-Za-z0-9_$]|function$|\([^)]*\)[^=]*=>|[A-Za-z_$][A-Za-z0-9_$]*[ \t]*=>)/) {
93
+ sub(/^[ \t]*(export[ \t]+)?(const|let|var)[ \t]+/, "", l); sub(/[^A-Za-z0-9_$].*$/, "", l); return l
94
+ }
95
+ }
96
+ return ""
97
+ }
98
+ # Directive d outil (shellcheck, eslint, noqa…) : n explique rien, on regarde au-dessus
99
+ function directive(l) {
100
+ return l ~ /^[ \t]*(#|\/\/)[ \t]*(shellcheck|noqa|type:|pylint:|eslint|@ts-|prettier-)/
101
+ }
102
+ # Commentaire juste au-dessus de la ligne i (décorateurs et directives sautés),
103
+ # ou docstring en tête du corps pour Python
104
+ function commentee(i, j, s) {
105
+ for (j = i - 1; j > 0 && (L[j] ~ /^[ \t]*@/ || directive(L[j])); j--) ;
106
+ if (j > 0 && L[j] !~ /^#!/ && L[j] ~ /^[ \t]*(#|\/\/|\/\*|\*)/) return 1
107
+ if (lang != "py") return 0
108
+ # Fin de la signature (deux-points en fin de ligne), puis première ligne non vide
109
+ for (j = i; j <= N && j < i + 20 && L[j] !~ /:[ \t]*(#.*)?$/; j++) ;
110
+ for (j++; j <= N && L[j] ~ /^[ \t]*$/; j++) ;
111
+ s = "^[ \t]*[rRuUbBfF]*[\"" q "]"
112
+ return j <= N && L[j] ~ s
113
+ }
114
+ FILENAME == ARGV[1] { n = nom($0); if (n != "") ancien[n] = 1; next }
115
+ { L[FNR] = $0; N = FNR }
116
+ END {
117
+ for (i = 1; i <= N; i++) {
118
+ if (index(added, " " i " ") == 0) continue
119
+ n = nom(L[i])
120
+ if (n == "" || (n in ancien) || commentee(i)) continue
121
+ print f ":" i "\t" n
122
+ }
123
+ }'
@@ -218,6 +218,30 @@ header_valid() {
218
218
  [ "$REPLY" -le 72 ]
219
219
  }
220
220
 
221
+ # Bots de mise à jour des dépendances : leurs titres (« bump <paquet> from X
222
+ # to Y ») dépassent souvent 72 caractères ; seul leur format est exigé.
223
+ BOTS='^[0-9]*\+?(dependabot|renovate)\[bot\]@'
224
+
225
+ # Règle des en-têtes de commit, commune au pre-push et à la CI : même verdict,
226
+ # même message, que le commit soit vu avant le push ou après.
227
+ # REPLY = clé du message de refus de l'en-tête $1 (e-mail de l'auteur $2),
228
+ # vide si l'en-tête est admis
229
+ commit_subject_problem_r() {
230
+ if [[ "$1" =~ ^(fixup|squash|amend)! ]]; then
231
+ REPLY=ci.check.3
232
+ elif [[ "$1" =~ ^(Merge|Revert) ]]; then
233
+ REPLY=""
234
+ elif ! [[ "$1" =~ $CC_PATTERN ]]; then
235
+ REPLY=ci.check.4
236
+ elif [[ "$2" =~ $BOTS ]]; then
237
+ REPLY=""
238
+ elif authored_length "$1" && [ "$REPLY" -gt 72 ]; then
239
+ REPLY=ci.check.5
240
+ else
241
+ REPLY=""
242
+ fi
243
+ }
244
+
221
245
  # REPLY = titre de PR conforme déduit de la branche $1 :
222
246
  # feat/ajout-panier → « feat: ajout panier », hotfix/crash → « fix: crash »,
223
247
  # release/1.2.0 → « chore(release): 1.2.0 », develop → livraison sur main
@@ -310,7 +334,6 @@ EOF
310
334
 
311
335
  Allowed exceptions: $allowed
312
336
  (change: git config repowarden.allowedBranches "main develop release/*")
313
- Disable for this repository: git config repowarden.skip branch-name
314
337
  EOF
315
338
  else
316
339
  cat >&2 <<EOF
@@ -333,7 +356,6 @@ EOF
333
356
 
334
357
  Exceptions autorisées : $allowed
335
358
  (modifier : git config repowarden.allowedBranches "main develop release/*")
336
- Désactiver pour ce dépôt : git config repowarden.skip branch-name
337
359
  EOF
338
360
  fi
339
361
  }
@@ -341,7 +363,7 @@ EOF
341
363
  # Vrai si l'élément est désactivé (repowarden.skip, git config ou .repowarden.conf).
342
364
  # Valeurs : true|all, ou liste séparée par espaces/virgules parmi
343
365
  # pre-commit prepare-commit-msg commit-msg pre-push post-checkout
344
- # branch-name protect-branch secrets format tests, ou un langage (node, python…)
366
+ # branch-name protect-branch secrets sensitive-files format tests, ou un langage (node, python…)
345
367
  skipped() {
346
368
  local v
347
369
  cfg_r skip
@@ -50,11 +50,13 @@ _msg_en() {
50
50
  cc.refs_prompt_ticket) _M="References (\"-\" for none) [%s]: " ;;
51
51
  cc.refs_prompt) _M="References (e.g. %s; Enter for none): " ;;
52
52
  tk.cli.no_ticket) _M="Branch %s has no ticket: this work will not be linked to any ticket (not blocking)." ;;
53
+ tk.cli.explain) _M="Ticket: the GitHub issue each change starts from (branch type/<n>-topic, PR citing it); its lifecycle: https://simdevwarden.github.io/repowarden/en/tickets/" ;;
53
54
  tk.cli.propose) _M="Start it from a ticket: repowarden ticket nouveau \"<title>\"." ;;
54
55
  tk.cli.propose_cc) _M="Start it from a ticket: repowarden ticket nouveau \"<title>\" (or answer yes in git cc)." ;;
55
56
  tk.cli.silence) _M="Project without ticket tracking? git config repowarden.skip tickets to stop seeing this." ;;
56
57
  tk.cli.fetch) _M="Branch of ticket #%s…" ;;
57
58
  tk.cli.switched) _M="On branch %s (ticket #%s)." ;;
59
+ tk.cli.unlinked) _M="Branch %s was created before validation: GitHub cannot link it to ticket #%s afterwards, its link is written on the ticket." ;;
58
60
  tk.cli.created_branch) _M="Branch %s created from %s, linked to ticket #%s." ;;
59
61
  tk.cli.not_validated) _M="Ticket #%s not validated yet: its branch will be created automatically on validation (\"%s\" label)." ;;
60
62
  tk.cli.closed) _M="Ticket #%s is closed: no branch to create." ;;
@@ -62,6 +64,7 @@ _msg_en() {
62
64
  tk.cli.issue_created) _M="Ticket #%s created, awaiting validation. Its branch will be created on validation; then: repowarden ticket %s" ;;
63
65
  tk.cli.issue_created_branch) _M="Ticket #%s created." ;;
64
66
  tk.cli.title_required) _M="Ticket title required: repowarden ticket nouveau \"<title>\"" ;;
67
+ tk.cli.bad_type) _M="Unknown ticket type: %s (types: %s)" ;;
65
68
  tk.cli.current) _M="Branch %s: ticket #%s." ;;
66
69
  tk.cli.no_ticket_here) _M="No ticket for this branch." ;;
67
70
  cc.ticket_offer) _M="No ticket for this branch. Create one now (title: the description)? [y/N]: " ;;
@@ -119,7 +122,7 @@ _msg_en() {
119
122
  help.code-mort) _M="Usage: repowarden code-mort [base]\n\nDead code introduced by your branch (like CI): only added/changed lines\nsince base (commits, staged/uncommitted, new files). Default: merge-base\nwith origin/main. See docs/code-mort.md (proven vs candidates)." ;;
120
123
  help.ticket) _M="Usage: repowarden ticket <n> branch for ticket (created, linked if needed)\n repowarden ticket ticket of current branch\n repowarden ticket nouveau \"<title>\" [--type fix] [--description \"…\"]\n repowarden ticket comment <n> \"<text>\" add comment\n repowarden ticket label <n> <label> [--remove] manage labels\n\nTicket first: taking <n> assigns to you and switches to its branch\n(created from develop if needed). \"nouveau\" creates a ticket (\"à valider\"\nif tracking is managed). Needs gh connected (https://cli.github.com)." ;;
121
124
  help.tickets) _M="Usage: repowarden tickets init [--repo owner/name] [--sans-validation-auto]\n repowarden tickets sync\n\nSets up ticket tracking: labels, issue template, workflow; classifies\nexisting tickets; records PR branches. sync checks out local branches for\ntickets assigned to you (auto with ticketSync=auto, manual by default)." ;;
122
- help.pr) _M="Usage: repowarden pr label <n> <label> [--remove]\n repowarden pr checks <n>\n\nlabel adds or removes a label from PR #<n>; checks shows its checks\n(name, conclusion, status, link).\nExamples:\n repowarden pr label 123 \"ready-for-review\"\n repowarden pr label 123 \"needs-changes\" --remove\n repowarden pr checks 123" ;;
125
+ help.pr) _M="Usage: repowarden pr label <n> <label> [--remove]\n repowarden pr checks <n>\n\nlabel adds or removes a label from PR #<n>; checks lists its failed\njobs (with their link) and pending ones, then the verdict; exit 1 if a job fails.\nExamples:\n repowarden pr label 123 \"ready-for-review\"\n repowarden pr label 123 \"needs-changes\" --remove\n repowarden pr checks 123" ;;
123
126
  help.run) _M="Usage: repowarden run rerun <run-id|--failed|--workflow <name>>\n repowarden run list [--workflow <name>] [--limit N]\n\nrerun restarts a GitHub Actions workflow. list shows recent runs\n(default limit 20).\nExamples:\n repowarden run rerun --failed\n repowarden run list --workflow \"CI\" --limit 10" ;;
124
127
  help.proteger) _M="Usage: repowarden proteger [--repo owner/name] [--checks \"repowarden,tests (ubuntu-latest)\"]\n [--relecteurs N] [--codeowners] [--environnement NAME] [--approbateurs \"login …\"]\n [--sans-tags] [--dry-run] [--verifier]\n\nProtects GitHub repo per .repowarden.conf: required PR, required checks,\nno force push/deletion, ruleset, squash merges, v* tags reserved to\nworkflows, optional human approvals. --verifier checks online rules\nwithout changes. Needs gh and admin rights." ;;
125
128
  help.migrer) _M="Usage: repowarden migrer [--pr] [--hooks]\n\nMigrates from repogarde to repowarden (docs/migration-v4.md): configs,\nworkflows, renames, lists manual steps left. Safe to rerun.\n --pr on chore/migration-repowarden-vN: commit, push and PR\n --hooks also move leftover lefthook hooks to .git/hooks-lefthook/" ;;
@@ -130,7 +133,6 @@ _msg_en() {
130
133
  # Hooks and language plugins
131
134
  hook.pre_commit.1) _M="Commit refused: branch name does not follow the convention." ;;
132
135
  hook.pre_commit.2) _M="Direct commit on '%s' forbidden. Create a branch: git switch -c feat/my-feature" ;;
133
- hook.pre_commit.3) _M=" (disable for this repository: git config repowarden.skip protect-branch)" ;;
134
136
  hook.pre_commit.4) _M="Secret detection (gitleaks)" ;;
135
137
  hook.pre_commit.5) _M="Secret detected: commit refused." ;;
136
138
  hook.secrets.where) _M=" %s:%s (%s)" ;;
@@ -141,7 +143,9 @@ _msg_en() {
141
143
  hook.secrets.todo_3) _M=" 3. Key that may have leaked elsewhere (already pushed, shared): revoke it at its provider." ;;
142
144
  hook.secrets.todo_4) _M=" False positive: add its fingerprint to .gitleaksignore:" ;;
143
145
  hook.secrets.failed) _M="gitleaks failed (code %s): commit refused, secret detection not done." ;;
144
- hook.pre_commit.6) _M="gitleaks not installed, secret detection skipped (install: %s)." ;;
146
+ hook.pre_commit.6) _M="gitleaks not installed: secrets not checked before the commit, the CI (secrets check) will do it. Install: %s" ;;
147
+ hook.sensitive.found) _M="Sensitive file neither tracked nor ignored: git add . would publish it. Lines to add to .gitignore:" ;;
148
+ hook.sensitive.hint) _M=" (names compared, content never read; patterns: repowarden.sensitiveFiles; disable: git config repowarden.skip sensitive-files)" ;;
145
149
  hook.pre_commit.7) _M="Pre-commit OK" ;;
146
150
  hook.commit_msg.1) _M="First line too long (%s > 72 characters)." ;;
147
151
  hook.commit_msg.type_unknown) _M="Unknown type \"%s\". Did you mean: %s" ;;
@@ -149,13 +153,16 @@ _msg_en() {
149
153
  hook.commit_msg.desc_empty) _M="Missing description after \"%s\": describe the change in the imperative mood." ;;
150
154
  hook.commit_msg.kept) _M=" Your message is not lost: git commit -e -F .git/COMMIT_EDITMSG" ;;
151
155
  hook.commit_msg.dot) _M="Final period removed from the description (same rule as git cc)." ;;
156
+ hook.commit_msg.detached) _M="👉 Detached HEAD (bisect, trial): no prefix comes from a branch, write the full header (e.g. fix: correct the total)." ;;
152
157
  hook.pre_push.1) _M="Push refused: branch name does not follow the convention." ;;
153
158
  hook.pre_push.2) _M="Direct push to '%s' refused: it only changes through pull requests." ;;
154
159
  hook.pre_push.3) _M=" Create a branch: git switch -c feat/my-feature, then open a PR." ;;
155
- hook.pre_push.4) _M=" (disable for this repository: git config repowarden.skip protect-branch)" ;;
156
160
  hook.pre_push.5) _M="%s: no changed file, nothing to test." ;;
157
161
  hook.pre_push.6) _M="Nothing to test (branch deletion only)." ;;
158
162
  hook.pre_push.7) _M="Pre-push OK" ;;
163
+ hook.pre_push.msg) _M="Push refused: commit message does not follow the convention (same rule as CI)." ;;
164
+ hook.pre_push.msg_fix) _M=" Fix: git rebase -i %s (reword), then push again." ;;
165
+ hook.pre_push.nothing) _M="Nothing to push." ;;
159
166
  hook.pre_push.workflow_scope) _M="gh token without the 'workflow' scope: GitHub will refuse a push touching .github/workflows/. Allow it (then sign in again): gh auth refresh -h github.com -s workflow" ;;
160
167
  hook.pre_push.lint.missing) _M="%s not installed, %s not checked (install: %s)." ;;
161
168
  hook.pre_push.lint.run) _M="%s: %s" ;;
@@ -287,6 +294,11 @@ _msg_en() {
287
294
  ci.fix_pr.2) _M="PR #%s retargeted: %s → %s (%s flow)" ;;
288
295
  ci.fix_pr.3) _M="Title follows the convention: %s" ;;
289
296
  ci.fix_pr.4) _M="Title \"%s\" replaced with \"%s\"" ;;
297
+ ci.verrous_pip.compile) _M="Lock file %s: re-running the command from its header" ;;
298
+ ci.verrous_pip.refused) _M="Lock file %s skipped: the output of its header command is not this file" ;;
299
+ ci.verrous_pip.compliant) _M="pip lock files match their header: no commit" ;;
300
+ ci.verrous_pip.committed) _M="Lock files regenerated and committed: %s" ;;
301
+ ci.verrous_pip.pushed) _M="Commit pushed to %s" ;;
290
302
  ci.version.1) _M="version announced by the delivery PR" ;;
291
303
  ci.version.2) _M="No feat, fix, perf or breaking change since %s: no version." ;;
292
304
  ci.version.3) _M="first version" ;;
@@ -420,6 +432,12 @@ _msg_en() {
420
432
  dc.knip_file) _M="file never imported" ;;
421
433
  dc.knip_export) _M="export never imported:" ;;
422
434
  dc.knip_dep) _M="dependency never imported:" ;;
435
+ # Functions added without a comment
436
+ cm.section) _M="Functions without a comment (new code only)" ;;
437
+ cm.no_base) _M="No base: comment check skipped." ;;
438
+ cm.missing) _M="%s: function %s has no comment right above it" ;;
439
+ cm.summary) _M="%s function(s) added without a comment: one or two lines right above, saying what it does and why (warning, not blocking)." ;;
440
+ cm.none) _M="Every added function has a comment." ;;
423
441
  ci.section.commits) _M="Commit messages (Conventional Commits)" ;;
424
442
  ci.section.branch) _M="Branch name" ;;
425
443
  ci.section.secrets) _M="Secrets (gitleaks)" ;;
@@ -486,6 +504,10 @@ _msg_en() {
486
504
  st.repo_version_ok) _M="Version required by the project (%s): met" ;;
487
505
  st.repo_forge) _M="Platform: %s" ;;
488
506
  st.repo_rules) _M="GitHub rules up to date? repowarden proteger --verifier (read-only)" ;;
507
+ st.wf_owner) _M="%s:%s: repowarden called from another repository than the official one (%s); expected: %s" ;;
508
+ st.wf_branch) _M="%s:%s: repowarden called on a branch (@%s), broken as soon as it is deleted; expected: %s" ;;
509
+ st.wf_old) _M="%s:%s: repowarden called in an outdated major version (@%s); expected: %s" ;;
510
+ st.wf_fix) _M="fix the uses: lines reported above" ;;
489
511
  st.tools) _M="Tools present: %s · missing: %s (depending on the projects' languages)" ;;
490
512
  st.next) _M="Next step:" ;;
491
513
  st.ready) _M="All set: git cc to commit, repowarden code-mort on a branch." ;;
@@ -541,6 +563,7 @@ _msg_en() {
541
563
  bot.ignored) _M="ignored (bot = off)" ;;
542
564
  bot.mode_invalid) _M="bot = %s: auto, pr or off expected" ;;
543
565
  bot.clone_failed) _M="clone failed: %s" ;;
566
+ bot.email_fallback) _M="Bot user %s not found: commits signed %s, linked to no account (set REPOWARDEN_BOT_EMAIL to fix the address)." ;;
544
567
  bot.prot_ok) _M="up to date" ;;
545
568
  bot.prot_stale) _M="⚠ outdated: repowarden proteger" ;;
546
569
  bot.prot_applied) _M="✔ applied" ;;
@@ -633,6 +656,7 @@ _msg_en() {
633
656
  tk.pr_backmerge_empty) _M="PR closed automatically: \`%s\` brings no content to \`%s\`. The back-merge is done automatically after a release, only when there is something to bring back." ;;
634
657
  tk.pr_delivery_manual) _M="PR closed automatically: the \`%s\` → \`%s\` delivery is opened by the release workflow; a PR opened by hand would never get a deployment approval. Run *Actions → release → Run workflow* on \`%s\` (or \`gh workflow run release.yml --ref %s\`), then **Approve and deploy** on the run." ;;
635
658
  tk.branch_registered) _M="Ticket branch: \`%s\`." ;;
659
+ tk.branch_unlinked) _M="Ticket branch: [\`%s\`](%s). Created before the ticket was validated: GitHub does not link an existing branch, so it does not show under *Development*; this comment keeps its link." ;;
636
660
  tk.branch_second) _M="Second branch for this ticket: \`%s\` (already: %s). One ticket, one branch: group the work, or open a separate ticket." ;;
637
661
  tk.adopted) _M="Existing tickets: %s sorted, %s branch(es) registered." ;;
638
662
  tk.init_adopt) _M="Existing tickets and branches" ;;
@@ -641,6 +665,8 @@ _msg_en() {
641
665
  tk.branch_kept) _M="Branch \`%s\` kept: %s commit(s) missing from the integration branch, open PR(s): %s. Delete it by hand if the work lives elsewhere." ;;
642
666
  tk.draft_opened) _M="Draft PR #%s opened for ticket #%s" ;;
643
667
  tk.draft_updated) _M="PR #%s (ticket #%s): title and description taken from the ticket" ;;
668
+ tk.pr_additions) _M="Ticket additions" ;;
669
+ tk.pr_design) _M="Design (summary)" ;;
644
670
  tk.created_body) _M="Ticket created automatically for PR #%s, which cited none. To be validated by a maintainer (\"validé\" label)." ;;
645
671
  tk.created) _M="Ticket #%s created for PR #%s and linked in its description: to be validated by a maintainer." ;;
646
672
  tk.init_labels) _M="Labels (statuses and validation)" ;;
@@ -654,6 +680,8 @@ _msg_en() {
654
680
  tk.init_next_wired) _M="Next: commit these files; require the \"ticket\" check (repowarden proteger --checks \"…,ticket\"). The tickets job of release.yml is already wired." ;;
655
681
  tk.prerelease) _M="Available in pre-release **%s**: to be tested before production." ;;
656
682
  tk.released) _M="Released in **%s**." ;;
683
+ tk.footer) _M="Posted by this repository's GitHub App ([repowarden docs](https://simdevwarden.github.io/repowarden/en/industrialisation/#github-app-for-workflows)): [%s](https://github.com/apps/%s)" ;;
684
+ tk.delivered) _M="Delivered to **%s**, without a new version (commit %s)." ;;
657
685
  # Ticket comment
658
686
  tk.comment.adding) _M="Adding comment to ticket #%s..." ;;
659
687
  tk.comment.added) _M="Comment added to ticket #%s." ;;
@@ -679,6 +707,11 @@ _msg_en() {
679
707
  run.listing.workflow) _M="Listing runs for workflow \"%s\"..." ;;
680
708
  # PR checks
681
709
  pr.checks.checking) _M="Checking checks for PR #%s..." ;;
710
+ pr.checks.failed) _M="Failed: %s %s" ;;
711
+ pr.checks.pending) _M="Pending: %s" ;;
712
+ pr.checks.verdict) _M="PR #%s: %s passed, %s failed, %s pending." ;;
713
+ pr.checks.green) _M="All checks of PR #%s are green." ;;
714
+ pr.checks.none) _M="No check on PR #%s." ;;
682
715
  # AI agent guard (repowarden garde, repowarden agents)
683
716
  garde.format) _M="Unknown format \"%s\": claude or opencode." ;;
684
717
  garde.no_jq) _M="repowarden garde: jq missing, action not checked (install jq: https://jqlang.org)." ;;