repowarden 4.4.0 → 4.7.1

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/README.md CHANGED
@@ -1,18 +1,18 @@
1
1
  # repowarden
2
2
 
3
- [![CI](https://github.com/SimBienvenueHoulBoumi/repowarden/actions/workflows/ci.yml/badge.svg)](https://github.com/SimBienvenueHoulBoumi/repowarden/actions/workflows/ci.yml)
4
- [![e2e](https://github.com/SimBienvenueHoulBoumi/repowarden/actions/workflows/e2e.yml/badge.svg)](https://github.com/SimBienvenueHoulBoumi/repowarden/actions/workflows/e2e.yml)
5
- [![Release](https://img.shields.io/github/v/release/SimBienvenueHoulBoumi/repowarden)](https://github.com/SimBienvenueHoulBoumi/repowarden/releases)
3
+ [![CI](https://github.com/simdevWarden/repowarden/actions/workflows/ci.yml/badge.svg)](https://github.com/simdevWarden/repowarden/actions/workflows/ci.yml)
4
+ [![e2e](https://github.com/simdevWarden/repowarden/actions/workflows/e2e.yml/badge.svg)](https://github.com/simdevWarden/repowarden/actions/workflows/e2e.yml)
5
+ [![Release](https://img.shields.io/github/v/release/simdevWarden/repowarden)](https://github.com/simdevWarden/repowarden/releases)
6
6
  [![npm](https://img.shields.io/npm/v/repowarden)](https://www.npmjs.com/package/repowarden)
7
- [![Documentation](https://img.shields.io/badge/docs-site-indigo)](https://simbienvenuehoulboumi.github.io/repowarden/)
8
- [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/SimBienvenueHoulBoumi/repowarden/badge)](https://scorecard.dev/viewer/?uri=github.com/SimBienvenueHoulBoumi/repowarden)
7
+ [![Documentation](https://img.shields.io/badge/docs-site-indigo)](https://simdevwarden.github.io/repowarden/)
8
+ [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/simdevWarden/repowarden/badge)](https://scorecard.dev/viewer/?uri=github.com/simdevWarden/repowarden)
9
9
  [![Licence MIT](https://img.shields.io/badge/licence-MIT-blue)](LICENSE)
10
10
 
11
11
  **repowarden** garde l'entrée de vos dépôts Git : messages de commit, nommage des branches, secrets, formatage et tests, **quel que soit le langage** — sur les postes (hooks), en CI (GitHub, GitLab) et côté serveur.
12
12
 
13
- 📖 **Documentation : https://simbienvenuehoulboumi.github.io/repowarden/** · 🇬🇧 **English: https://simbienvenuehoulboumi.github.io/repowarden/en/**
13
+ 📖 **Documentation : https://simdevwarden.github.io/repowarden/** · 🇬🇧 **English: https://simdevwarden.github.io/repowarden/en/**
14
14
 
15
- > **v4 : repogarde devient repowarden** (paquet npm `repowarden`) et lefthook n'est plus pris en charge. Les anciens noms restent lus en v4. Marche à suivre : [Passer à la v4](https://simbienvenuehoulboumi.github.io/repowarden/migration-v4/).
15
+ > **v4 : repogarde devient repowarden** (paquet npm `repowarden`) et lefthook n'est plus pris en charge. Les anciens noms restent lus en v4. Marche à suivre : [Passer à la v4](https://simdevwarden.github.io/repowarden/migration-v4/).
16
16
 
17
17
  ## Ce qu'il fait
18
18
 
@@ -23,18 +23,51 @@
23
23
  - **Tests** : au push et en CI, seulement les projets touchés, sur le commit poussé.
24
24
  - **Code mort** : seul le nouveau code mort est signalé, prouvé (bloquant) ou candidat (à vérifier) ; `repowarden code-mort` en local.
25
25
  - **Tickets** : chaque PR est reliée à un ticket validé (créé automatiquement s'il manque), suivi de « à valider » à « done » à la release ; `repowarden tickets init`.
26
- - **Versions et releases** : les commits étant conventionnels, la version suivante (semver) et le changelog se déduisent de l'historique ; un workflow réutilisable en fait des releases automatiques (PR de release validée par la CI puis mergée, tag, release), sans jeton ni intervention.
26
+ - **Versions et releases** : les commits étant conventionnels, la version suivante (semver) et le changelog se déduisent de l'historique ; un workflow réutilisable en fait des releases automatiques (PR de release validée par la CI puis mergée, tag, release), sans jeton ; dans le flux `develop` → `main`, un clic d'approbation par livraison.
27
27
 
28
- **Plus de 25 technologies** : Java (Maven, Gradle), JavaScript / TypeScript, Python, Go, Rust, PHP, Ruby, .NET, Dart / Flutter, Swift, Elixir, C / C++, Shell, Terraform, Packer, Ansible, Helm, Kubernetes, Docker, GitHub Actions — [détail](https://simbienvenuehoulboumi.github.io/repowarden/technologies/). Linux, macOS, Windows.
28
+ **Plus de 25 technologies** : Java (Maven, Gradle), JavaScript / TypeScript, Python, Go, Rust, PHP, Ruby, .NET, Dart / Flutter, Swift, Elixir, C / C++, Shell, Terraform, Packer, Ansible, Helm, Kubernetes, Docker, GitHub Actions — [détail](https://simdevwarden.github.io/repowarden/technologies/). Linux, macOS, Windows.
29
+
30
+ ## Le cycle d'une modification
31
+
32
+ De l'idée à la production, chaque changement suit le même chemin. Tu n'agis qu'aux étapes **en gras** ; le reste est automatique, et chaque étape est vérifiée par la CI.
33
+
34
+ ```mermaid
35
+ flowchart LR
36
+ A[Ticket] -->|validé| B[Conception]
37
+ B --> C[Branche du ticket]
38
+ C -->|commits + push| D[PR brouillon]
39
+ D -->|Ready for review| E{CI verte ?}
40
+ E -->|oui| F[develop]
41
+ E -->|non| C
42
+ F -->|vendredi, ou à la main| G[Demande de livraison]
43
+ G -->|Approve and deploy| H[main : release]
44
+ ```
45
+
46
+ | # | Étape | Qui | Ce qui se passe | Statut du ticket |
47
+ |---|---|---|---|---|
48
+ | 1 | Ouvrir un ticket (issue) | toi | rien ne se code sans ticket | à valider |
49
+ | 2 | **Valider le ticket** | toi | étiquette `validé` (posée d'office si un mainteneur ouvre le ticket) | backlog |
50
+ | 3 | **Écrire la conception** | toi | commentaire « Conception retenue » sur le ticket : ce qui change, fichiers touchés, hors périmètre. Sans elle, la vérification `ticket` de la PR échoue | backlog |
51
+ | 4 | **Prendre le ticket** | toi | `repowarden ticket 12` : assigné, branche `fix/12-sujet` créée depuis `develop` | en cours |
52
+ | 5 | Coder et committer | toi | `git cc` ; les hooks vérifient message, branche, secrets, formatage, puis les tests au push | en cours |
53
+ | 6 | Premier push | auto | PR **brouillon** vers `develop` ouverte, titre et description tirés du ticket | en cours |
54
+ | 7 | **Sortir du brouillon** | toi | bouton *Ready for review* (`gh pr ready`) | en relecture |
55
+ | 8 | Vérifier et intégrer | auto | CI complète ; verte → **merge automatique** (squash) dans `develop`. Rouge → corriger et pousser, retour en 5 | préprod |
56
+ | 9 | Préversion | auto | chaque soir si `develop` a changé : `vX.Y.Z-next.N` (paquet `repowarden@next`), pour tester avant la prod | préprod |
57
+ | 10 | Demande de livraison | auto | le vendredi matin, ou à la main (*Actions → release → Run workflow*, ou étiquette `urgent` sur une PR `fix/…`) : instantané `livraison/vX.Y.Z` et sa PR vers `main` | préprod |
58
+ | 11 | **Approuver la livraison** | toi | sur la page du run : **Review deployments → Approve and deploy** (pas « Approve » sur la PR, qui est une simple relecture) | préprod |
59
+ | 12 | Publier | auto | la GitHub App merge vers `main` : tag `vX.Y.Z`, release, paquet npm, documentation | done, ticket fermé |
60
+
61
+ Seul `develop` entre dans `main`, et jamais sans l'approbation de l'étape 11. Une PR `develop` → `main` ouverte à la main est fermée automatiquement, avec la marche à suivre. Pas à pas, commandes et pièges : [où cliquer](https://simdevwarden.github.io/repowarden/validation/#ou-cliquer).
29
62
 
30
63
  ## Démarrage
31
64
 
32
65
  ### 1. Sur ton poste, en une minute (tous tes dépôts)
33
66
 
34
67
  ```bash
35
- npm install -g repowarden # 1. le paquet (yarn global add, pnpm add -g : idem)
36
- repowarden install --global # 2. active les hooks : demande la langue, installe git cc
37
- repowarden # 3. vérifie : état du poste et étape suivante
68
+ npm install -g repowarden # 1. le paquet (yarn global add, pnpm add -g : idem)
69
+ repowarden install --global # 2. active les hooks : demande la langue, installe git cc
70
+ repowarden # 3. vérifie : état du poste et étape suivante
38
71
  ```
39
72
 
40
73
  L'étape 2 est indispensable : par sécurité, le paquet n'exécute rien à l'installation (aucun script `postinstall`, que npm 12 bloque d'ailleurs). Ensuite, dans **n'importe quel dépôt**, les hooks s'appliquent dès le prochain commit. Rien n'est ajouté aux projets ni à leur `package.json`.
@@ -44,7 +77,7 @@ cd mon-projet
44
77
  git switch -c feat/panier # nom de branche vérifié
45
78
  git add . && git cc # assistant : type, scope, description… puis commit
46
79
  git push -u origin feat/panier # tests des projets touchés avant l'envoi
47
- repowarden code-mort # nouveau code mort de ta branche
80
+ repowarden code-mort # nouveau code mort de ta branche
48
81
  ```
49
82
 
50
83
  | Action | Commande |
@@ -52,11 +85,17 @@ repowarden code-mort # nouveau code mort de ta branche
52
85
  | État du poste, étape suivante | `repowarden` (ou `repowarden statut`) |
53
86
  | Toutes les commandes | `repowarden --help` (`cc`, `code-mort`, `proteger`, `npm-publication`…) |
54
87
  | Mettre à jour | `npm update -g repowarden` (les hooks suivent) |
88
+ | Tester la prochaine version | `npm install -g repowarden@next` (retour : `repowarden@latest`) ; en CI : `uses: simdevWarden/repowarden@develop` |
55
89
  | Désinstaller | `repowarden uninstall --global`, **puis** `npm uninstall -g repowarden` (dans cet ordre : npm n'exécute rien à la désinstallation, les hooks resteraient branchés sur un dossier supprimé) |
56
90
 
57
- Sans Node : `git clone https://github.com/SimBienvenueHoulBoumi/repowarden.git ~/repowarden && ~/repowarden/install.sh --global` (mise à jour : `git -C ~/repowarden pull`).
91
+ Sans Node : `git clone https://github.com/simdevWarden/repowarden.git ~/repowarden && ~/repowarden/install.sh --global` (mise à jour : `git -C ~/repowarden pull`).
58
92
 
59
- **Tester la prochaine version** (avant sa sortie en production) : `npm install -g repowarden@next`, ou `uses: SimBienvenueHoulBoumi/repowarden@develop` en CI. Retour à la version stable : `npm install -g repowarden@latest`. Les problèmes se signalent en [issue](https://github.com/SimBienvenueHoulBoumi/repowarden/issues).
93
+ **Nouvelle machine** : ces trois commandes suffisent. Les règles du dépôt, l'environnement de livraison, la GitHub App, ses secrets et la publication npm vivent sur GitHub et npm : rien à reconfigurer. La clé privée de l'App ne doit jamais se trouver sur un poste.
94
+
95
+ > [!NOTE]
96
+ > **GitHub CLI (`gh`) : facultatif.**
97
+ > Sans `gh` : tous les hooks (commits, branches, secrets, formatage, tests), `git cc`, `repowarden code-mort`, `repowarden statut`, et la CI (GitHub ou GitLab), qui n'utilise jamais le `gh` du poste.
98
+ > Avec `gh` connecté (`gh auth login`), en plus : les tickets (`repowarden ticket`, `tickets`, branches de tes tickets créées en local) et la gestion du dépôt (`proteger`, `app init`, `npm-publication`, `pr-*`, `run-*`). Utile au mainteneur du dépôt GitHub ; chaque commande le signale s'il manque.
60
99
 
61
100
  ### 2. Dans un projet d'équipe : les mêmes règles pour tous
62
101
 
@@ -71,34 +110,56 @@ L'essentiel des deux fichiers :
71
110
 
72
111
  ```yaml
73
112
  # .github/workflows/repowarden.yml : vérifications de la PR (dernière version 4.x)
74
- - uses: SimBienvenueHoulBoumi/repowarden@v4
113
+ - uses: simdevWarden/repowarden@v4
75
114
  with: { strict: true } # un outil manquant fait échouer la CI
76
115
  ```
77
116
 
78
117
  ```ini
79
118
  # .repowarden.conf : un poste en retard est prévenu, avec la commande de mise à jour
80
119
  [repowarden]
81
- version = 3
120
+ version = 4
121
+ ```
122
+
123
+ Puis rendre la CI obligatoire pour merger (`gh` connecté, admin du dépôt) :
124
+
125
+ ```bash
126
+ repowarden proteger --checks "repowarden,tests" # toutes les vérifications exigées, séparées par des virgules
127
+ repowarden proteger --checks "repowarden,tests" --verifier # plus tard : règles en ligne à jour ? (lecture seule)
128
+ ```
129
+
130
+ Toujours passer **toutes** les vérifications à `--checks` : sans lui, seule `repowarden` reste exigée. Avec un flux `develop` → `main`, `proteger` crée aussi l'étiquette `urgent` et exige l'approbation de chaque livraison : utiliser le [modèle de release du flux develop](https://simdevwarden.github.io/repowarden/releases/), sinon `main` attendrait un déploiement que rien ne crée (`proteger` le signale).
131
+
132
+ Exemple complet : [repowarden-demo](https://github.com/simdevWarden/repowarden-demo) · Releases automatiques : [documentation](https://simdevwarden.github.io/repowarden/releases/).
133
+
134
+ ### 3. GitHub App pour l'automatisation (merge auto, tickets, releases)
135
+
136
+ Pour que les workflows déclenchent les autres workflows (merge auto, tickets, releases), il faut une GitHub App au lieu du `GITHUB_TOKEN` :
137
+
138
+ ```bash
139
+ repowarden app init
82
140
  ```
83
141
 
84
- Puis rendre la CI obligatoire pour merger : `repowarden proteger` (protection de `main`, check `repowarden` exigé).
142
+ 1. Ouvre la page locale → clique « Create GitHub App » sur GitHub
143
+ 2. Clique « Install » sur ton dépôt ou ton organisation
144
+ 3. Les secrets `REPOWARDEN_APP_CLIENT_ID` / `REPOWARDEN_APP_KEY` sont configurés automatiquement
85
145
 
86
- Exemple complet : [repowarden-demo](https://github.com/SimBienvenueHoulBoumi/repowarden-demo) · Releases automatiques : [documentation](https://simbienvenuehoulboumi.github.io/repowarden/releases/).
146
+ Sans cette étape, le `GITHUB_TOKEN` des Actions est utilisé : **ce qu'il fait ne déclenche pas les autres workflows** (merge auto, tickets, préversions).
87
147
 
88
- ### 3. Contribuer à repowarden
148
+ ### 4. Contribuer à repowarden
89
149
 
90
150
  ```bash
91
- git clone https://github.com/SimBienvenueHoulBoumi/repowarden.git && cd repowarden
151
+ git clone https://github.com/simdevWarden/repowarden.git && cd repowarden
92
152
  ./install.sh # repowarden se vérifie lui-même (hooks de ce dépôt)
93
- bats test/ # tests (bats-core), aussi lancés au push
153
+ brew install shellcheck bats-core actionlint gitleaks # outils des vérifications (ou équivalent Linux)
154
+ bats test/ # tests, aussi lancés au push
94
155
  ```
95
156
 
96
- Les PR visent `develop` ; `main` ne reçoit que les livraisons, qui déclenchent les releases. Voir [CONTRIBUTING.md](CONTRIBUTING.md).
157
+ Les PR visent `develop` ; `main` ne reçoit que les livraisons, qui déclenchent les releases. Tout part d'un ticket : voir [AGENTS.md](AGENTS.md) et [CONTRIBUTING.md](CONTRIBUTING.md). Les problèmes se signalent en [issue](https://github.com/simdevWarden/repowarden/issues).
97
158
 
98
- **Dans VS Code** : l'extension [repowarden-vscode](https://github.com/SimBienvenueHoulBoumi/repowarden-vscode) guide la rédaction des commits, signale une branche mal nommée et des hooks inactifs. En attendant sa publication sur le Marketplace : `.vsix` dans ses [releases](https://github.com/SimBienvenueHoulBoumi/repowarden-vscode/releases), puis `code --install-extension repowarden-X.Y.Z.vsix`.
159
+ **Dans VS Code** : l'extension [repowarden-vscode](https://github.com/simdevWarden/repowarden-vscode) guide la rédaction des commits, signale une branche mal nommée et des hooks inactifs. En attendant sa publication sur le Marketplace : `.vsix` dans ses [releases](https://github.com/simdevWarden/repowarden-vscode/releases), puis `code --install-extension repowarden-X.Y.Z.vsix`.
99
160
 
100
161
  ## Liens
101
162
 
102
- [Démarrage rapide](https://simbienvenuehoulboumi.github.io/repowarden/demarrage/) · [Hooks et règles](https://simbienvenuehoulboumi.github.io/repowarden/hooks/) · [Technologies](https://simbienvenuehoulboumi.github.io/repowarden/technologies/) · [Configuration](https://simbienvenuehoulboumi.github.io/repowarden/configuration/) · [CI](https://simbienvenuehoulboumi.github.io/repowarden/ci/) · [Versions et releases](https://simbienvenuehoulboumi.github.io/repowarden/releases/) · [Validation humaine](https://simbienvenuehoulboumi.github.io/repowarden/validation/) · [Code mort](https://simbienvenuehoulboumi.github.io/repowarden/code-mort/) · [En organisation](https://simbienvenuehoulboumi.github.io/repowarden/industrialisation/) · [Sources internes (Nexus, Artifactory)](https://simbienvenuehoulboumi.github.io/repowarden/industrialisation/#8-sources-internes-reseau-ferme-nexus-artifactory) · [Désinstallation](https://simbienvenuehoulboumi.github.io/repowarden/desinstallation/) · [Sécurité](SECURITY.md) · [Contribuer](CONTRIBUTING.md) · [Changelog](CHANGELOG.md)
163
+ [Démarrage rapide](https://simdevwarden.github.io/repowarden/demarrage/) · [Hooks et règles](https://simdevwarden.github.io/repowarden/hooks/) · [Technologies](https://simdevwarden.github.io/repowarden/technologies/) · [Configuration](https://simdevwarden.github.io/repowarden/configuration/) · [CI](https://simdevwarden.github.io/repowarden/ci/) · [Versions et releases](https://simdevwarden.github.io/repowarden/releases/) · [Validation humaine](https://simdevwarden.github.io/repowarden/validation/) · [Code mort](https://simdevwarden.github.io/repowarden/code-mort/) · [En organisation](https://simdevwarden.github.io/repowarden/industrialisation/) · [Sources internes (Nexus, Artifactory)](https://simdevwarden.github.io/repowarden/industrialisation/#8-sources-internes-reseau-ferme-nexus-artifactory) · [Désinstallation](https://simdevwarden.github.io/repowarden/desinstallation/) · [Sécurité](SECURITY.md) · [Contribuer](CONTRIBUTING.md) · [Changelog](CHANGELOG.md)
103
164
 
104
165
  Licence [MIT](LICENSE).
package/bin/commit CHANGED
@@ -6,7 +6,7 @@
6
6
  # sans cela, la ligne suivante serait lue
7
7
  # comme une nouvelle réponse)
8
8
  # git cc -m "description" [--type T] [--scope S] [--breaking NOTE]
9
- # [--body TEXTE] [--refs "Closes #12"]
9
+ # [--body TEXTE] [--refs "Ticket : #12"]
10
10
  # sans question (scripts, agents) : mêmes
11
11
  # contrôles, toute réponse refusée arrête
12
12
  # tout ; type et scope déduits de la
@@ -173,14 +173,19 @@ while :; do
173
173
  type="$answer"
174
174
  break
175
175
  fi
176
- t cc.type_invalid "$answer" "${#TYPES[@]}"
176
+ type_proche_r "$(printf '%s' "$answer" | tr '[:upper:]' '[:lower:]')"
177
+ if [ -n "$REPLY" ]; then
178
+ t cc.type_invalid_suggest "$answer" "$REPLY" "${#TYPES[@]}"
179
+ else
180
+ t cc.type_invalid "$answer" "${#TYPES[@]}"
181
+ fi
177
182
  refuse "$REPLY"
178
183
  done
179
184
 
180
185
  # 2. Scope (optionnel)
181
186
  t cc.step2
182
187
  etape 2 "$REPLY"
183
- t cc.scope_hint
188
+ if [ -n "$default_scope" ]; then t cc.scope_hint_default "$default_scope"; else t cc.scope_hint; fi
184
189
  hint "$REPLY"
185
190
  while :; do
186
191
  if [ -n "$default_scope" ]; then
@@ -335,9 +340,9 @@ ticket=""
335
340
  branch_ticket_r "$branch"
336
341
  [ -z "$REPLY" ] || ticket="Ticket : #$REPLY"
337
342
  # Branche de travail sans ticket : prévenu, jamais bloqué. Création proposée
338
- # seulement si repowarden gère la vie des tickets du projet (et sans -m)
343
+ # seulement si repowarden gère la vie des tickets du projet ET ticketAutoCreate (et sans -m)
339
344
  if [ -z "$ticket" ] && [ -z "$DRY_RUN" ] && ! skipped tickets && work_branch "$branch"; then
340
- if [ -z "$BATCH" ] && tickets_managed && has gh; then
345
+ if [ -z "$BATCH" ] && tickets_managed && ticket_auto_create && has gh; then
341
346
  t cc.ticket_offer
342
347
  ask_yn "$REPLY" n
343
348
  if [ -n "$YN" ] && n="$(bash "$ROOT/bin/ticket" nouveau "$description" --type "$type" --porcelain)" && [ -n "$n" ]; then
@@ -352,7 +357,8 @@ fi
352
357
  if [ -n "$ticket" ]; then
353
358
  t cc.refs_prompt_ticket "$ticket"
354
359
  else
355
- t cc.refs_prompt "$FORGE_REFS"
360
+ cfg_r integrationBranch ""
361
+ if [ -n "$REPLY" ]; then t cc.refs_prompt "Ticket : #12"; else t cc.refs_prompt "$FORGE_REFS"; fi
356
362
  fi
357
363
  ANSWER="$OPT_REFS"
358
364
  ask "$REPLY"
package/bin/pr-checks ADDED
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env bash
2
+ # Vérifier les checks d'une PR
3
+ # repowarden pr checks <n>
4
+ set -euo pipefail
5
+
6
+ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)"
7
+ # shellcheck source=../hooks/lib/common.sh
8
+ source "$ROOT/hooks/lib/common.sh"
9
+ # shellcheck source=../hooks/lib/i18n.sh
10
+ source "$ROOT/hooks/lib/i18n.sh"
11
+
12
+ aide() {
13
+ cat <<EOF
14
+ Usage: repowarden pr checks <n>
15
+
16
+ Affiche l'état des checks de la PR #<n>.
17
+
18
+ Exemple:
19
+ repowarden pr checks 123
20
+ EOF
21
+ }
22
+
23
+ [ $# -eq 1 ] || { aide >&2; exit 1; }
24
+ if [[ "$1" == "--help" || "$1" == "-h" ]]; then
25
+ aide
26
+ exit 0
27
+ fi
28
+ command -v gh >/dev/null || { err_t bot.no_gh; exit 1; }
29
+
30
+ n="${1}"
31
+
32
+ step_t pr.checks.checking "$n"
33
+ gh api "repos/{{owner}}/{{repo}}/pulls/$n/check-runs" --jq '.check_runs[] | {name: .name, conclusion: .conclusion, status: .status, url: .html_url}' 2>/dev/null | while IFS= read -r line; do
34
+ echo "$line"
35
+ done
package/bin/pr-label ADDED
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env bash
2
+ # Gérer les labels d'une PR GitHub
3
+ # repowarden pr label <n> <label> [--remove]
4
+ set -euo pipefail
5
+
6
+ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)"
7
+ # shellcheck source=../hooks/lib/common.sh
8
+ source "$ROOT/hooks/lib/common.sh"
9
+ # shellcheck source=../hooks/lib/i18n.sh
10
+ source "$ROOT/hooks/lib/i18n.sh"
11
+
12
+ aide() {
13
+ cat <<EOF
14
+ Usage: repowarden pr label <n> <label> [--remove]
15
+
16
+ Ajoute ou retire un label sur la PR #<n>.
17
+
18
+ Options:
19
+ --remove Retire le label au lieu de l'ajouter
20
+
21
+ Exemples:
22
+ repowarden pr label 123 "ready-for-review"
23
+ repowarden pr label 123 "needs-changes" --remove
24
+ EOF
25
+ }
26
+
27
+ [ $# -ge 2 ] || { aide >&2; exit 1; }
28
+ command -v gh >/dev/null || { err_t bot.no_gh; exit 1; }
29
+
30
+ n="${1}"
31
+ label="${2}"
32
+ remove=false
33
+
34
+ shift 2
35
+ while [ $# -gt 0 ]; do
36
+ case "$1" in
37
+ --remove) remove=true ;;
38
+ *) err_t install.unknown_arg "$1"; exit 1 ;;
39
+ esac
40
+ shift
41
+ done
42
+
43
+ if [ "$remove" = true ]; then
44
+ step_t pr.label.removing "$label" "$n"
45
+ gh pr edit "$n" --remove-label "$label"
46
+ ok_t pr.label.removed "$label" "$n"
47
+ else
48
+ step_t pr.label.adding "$label" "$n"
49
+ gh pr edit "$n" --add-label "$label"
50
+ ok_t pr.label.added "$label" "$n"
51
+ fi
package/bin/proteger CHANGED
@@ -11,8 +11,10 @@
11
11
  #
12
12
  # bin/proteger [--repo propriétaire/nom] [--checks "repowarden,tests (ubuntu-latest)"] [--relecteurs N]
13
13
  # [--codeowners] [--environnement NOM] [--approbateurs "login …"]
14
- # [--sans-tags] [--dry-run]
14
+ # [--sans-tags] [--dry-run] [--verifier]
15
15
  # --sans-tags : dépôt qui déplace lui-même un tag (tag majeur flottant v2…).
16
+ # --verifier : compare les règles en ligne à celles attendues, sans rien modifier
17
+ # (après une mise à jour de repowarden ou de .repowarden.conf).
16
18
  # Réglages équivalents dans .repowarden.conf (requiredReviews, codeOwnerReview,
17
19
  # unattributedApproval, deliveryApproval, environment, environmentReviewers). Demande un gh connecté, admin du dépôt.
18
20
  set -euo pipefail
@@ -38,17 +40,20 @@ APPROVERS="$REPLY"
38
40
  REPO=""
39
41
  CHECKS="repowarden"
40
42
  DRY_RUN=""
43
+ VERIFY=""
44
+ CHECKS_SET=""
41
45
  NO_TAGS=""
42
46
  while [ $# -gt 0 ]; do
43
47
  case "$1" in
44
48
  --repo) shift; REPO="${1:?--repo attend propriétaire/nom}" ;;
45
- --checks) shift; CHECKS="${1:?--checks attend une liste}" ;;
49
+ --checks) shift; CHECKS="${1:?--checks attend une liste}"; CHECKS_SET=1 ;;
46
50
  --relecteurs) shift; REVIEWS="${1:?--relecteurs attend un nombre}" ;;
47
51
  --codeowners) CODEOWNERS=true ;;
48
52
  --environnement) shift; ENVIRONMENT="${1:?--environnement attend un nom}" ;;
49
53
  --approbateurs) shift; APPROVERS="${1:?--approbateurs attend des logins}" ;;
50
54
  --sans-tags) NO_TAGS=1 ;;
51
55
  --dry-run) DRY_RUN=1 ;;
56
+ --verifier) VERIFY=1 ;;
52
57
  -h | --help) sed -n '2,18p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
53
58
  *) err_t install.unknown_arg "$1"; exit 1 ;;
54
59
  esac
@@ -58,9 +63,14 @@ done
58
63
  [[ "$REVIEWS" =~ ^[0-9]$ ]] || { err_t ci.proteger.reviews_invalid; exit 1; }
59
64
  [[ "$CODEOWNERS" =~ ^(true|false)$ ]] || { err_t ci.proteger.codeowners_invalid; exit 1; }
60
65
  [[ "$UNATTRIBUTED" =~ ^(true|false)$ ]] || { err_t ci.proteger.unattributed_invalid; exit 1; }
66
+ # Dev solo (une seule personne avec l'écriture) : aucune règle n'exige une
67
+ # autre personne, qui n'existe pas. Nombre inconnu (appel en échec) : pas solo.
68
+ solo=false
69
+ [ "$(gh api "repos/$REPO/collaborators?per_page=100" \
70
+ --jq '[.[] | select(.permissions.push)] | length' 2>/dev/null)" = 1 ] && solo=true
61
71
  # Avec relecture : le dernier à pousser ne peut pas approuver son propre push
62
72
  last_push=false
63
- [ "$REVIEWS" = 0 ] || last_push=true
73
+ [ "$REVIEWS" = 0 ] || [ "$solo" = true ] || last_push=true
64
74
 
65
75
  cfg_r protectedBranches "main master"
66
76
  protected="$REPLY"
@@ -161,8 +171,10 @@ if [ -n "$integration" ]; then
161
171
  # Livraison vers main = passage à une nouvelle version : approbation
162
172
  # humaine obligatoire (la PR de livraison est ouverte par le workflow,
163
173
  # le mainteneur peut donc l'approuver), sans exiger un autre approbateur
164
- # que l'auteur du dernier push sauf si des relectures sont configurées
174
+ # que l'auteur du dernier push sauf si des relectures sont configurées.
175
+ # Dev solo : pas de minimum (l'auteur de la PR ne peut pas l'approuver)
165
176
  livraison=$((REVIEWS > 0 ? REVIEWS : 1))
177
+ [ "$solo" = false ] || livraison="$REVIEWS"
166
178
  # deliveryApproval = deployment (défaut) : la validation humaine est
167
179
  # l'approbation de l'environnement de livraison (« Approve and deploy »),
168
180
  # exigée par une règle de main (déploiement réussi) ; la revue de PR n'est
@@ -182,12 +194,12 @@ if [ -n "$integration" ]; then
182
194
  # release). Une livraison n'apporte que develop, vérifiée par la CI ; le
183
195
  # retour reste fait quand main a un contenu propre (correctif direct).
184
196
  [ -z "$others" ] || rulesets+=("$(branch_ruleset repowarden "$others" '"merge"' false "$livraison" "$last_push" "$deploiement")")
185
- # Branche d'intégration : PR à jour non exigée. Le retour de main (après
186
- # une release) serait sinon presque toujours en retard, et « mettre à jour »
187
- # fusionnerait develop dans main en contournant la livraison ; la CI
188
- # vérifie de toute façon chaque push sur develop.
197
+ # Branche d'intégration : squash seul (un commit par PR de travail, titre =
198
+ # message : les commits intermédiaires n'entrent jamais) ; plus de retour
199
+ # main → develop à merger, seuls develop et ses instantanés livraison/*
200
+ # entrant dans main. PR à jour non exigée : la CI vérifie chaque push.
189
201
  [[ " $existing " != *" $integration "* ]] ||
190
- rulesets+=("$(branch_ruleset "repowarden ($integration)" "$integration" '"squash", "merge"' false)")
202
+ rulesets+=("$(branch_ruleset "repowarden ($integration)" "$integration" '"squash"' false)")
191
203
  merge_commit=true
192
204
  # Nettoyage par le workflow nettoyage-branches (garde une branche encore
193
205
  # source d'une autre PR ouverte)
@@ -202,6 +214,19 @@ if [ -n "$integration" ]; then
202
214
  if ! grep -qs "nettoyage-branches.yml" "$GIT_TOPLEVEL"/.github/workflows/*.y*ml; then
203
215
  attention_t ci.proteger.no_cleanup
204
216
  fi
217
+ # Règle de déploiement sans workflow qui crée ce déploiement : main
218
+ # bloquée pour toujours (la livraison n'aurait jamais d'approbation)
219
+ if [ "$DELIVERY" = deployment ] &&
220
+ ! grep -qs "delivery-environment:" "$GIT_TOPLEVEL"/.github/workflows/*.y*ml; then
221
+ attention_t ci.proteger.no_delivery_env "$ENVIRONMENT"
222
+ fi
223
+ # Demande de livraison (manuelle, planifiée) qui ne remplace pas celle en
224
+ # attente : une demande oubliée bloquerait en silence les suivantes
225
+ for wf in "$GIT_TOPLEVEL"/.github/workflows/*.y*ml; do
226
+ grep -qs "release-auto.yml" "$wf" && grep -qsE "workflow_dispatch|schedule:" "$wf" &&
227
+ grep -qs "cancel-in-progress: false" "$wf" &&
228
+ attention_t ci.proteger.stale_concurrency "${wf#"$GIT_TOPLEVEL"/}"
229
+ done
205
230
  t ci.proteger.methods_flow "$integration"
206
231
  methods="$REPLY"
207
232
  else
@@ -238,6 +263,7 @@ fi
238
263
  echo_t ci.proteger.5 "$tags_msg"
239
264
  [ -z "$integration" ] || echo_t ci.proteger.6 "$integration"
240
265
  echo_t ci.proteger.7 "$REVIEWS" "$CODEOWNERS" "$UNATTRIBUTED"
266
+ [ "$solo" = false ] || echo_t ci.proteger.solo
241
267
  if [ "$REVIEWS" != 0 ] || [ "$CODEOWNERS" = true ]; then
242
268
  echo_t ci.proteger.8
243
269
  fi
@@ -271,6 +297,56 @@ if [ -n "$ENVIRONMENT" ]; then
271
297
  \"deployment_branch_policy\": {\"protected_branches\": false, \"custom_branch_policies\": true}}"
272
298
  echo_t ci.proteger.9 "$ENVIRONMENT" "$APPROVERS"
273
299
  fi
300
+ # Signature des champs posés par ce script : attendue (JSON produit ci-dessus)
301
+ # et en ligne (gh --jq), comparables sans jq ni python
302
+ sig_attendue() {
303
+ local r="$1" deps checks
304
+ deps="$(sed -n 's/.*"required_deployment_environments": \[\(.*\)\].*/\1/p' <<<"$r" | tr -d '" ')"
305
+ checks="$(grep -o '"context": "[^"]*"' <<<"$r" | sed 's/"context": "\(.*\)"/\1/' | LC_ALL=C sort | paste -sd, -)"
306
+ printf 'approbations=%s dernier_push=%s codeowners=%s deploiement=%s verifications=%s' \
307
+ "$(sed -n 's/.*"required_approving_review_count": \([0-9]*\).*/\1/p' <<<"$r")" \
308
+ "$(sed -n 's/.*"require_last_push_approval": \([a-z]*\).*/\1/p' <<<"$r")" \
309
+ "$(sed -n 's/.*"require_code_owner_review": \([a-z]*\).*/\1/p' <<<"$r")" "$deps" "$checks"
310
+ }
311
+ # shellcheck disable=SC2016 # expression jq (gh --jq), pas du shell
312
+ SIG_EN_LIGNE='(.rules | map(select(.type == "pull_request"))[0].parameters) as $p
313
+ | "approbations=\($p.required_approving_review_count) dernier_push=\($p.require_last_push_approval) codeowners=\($p.require_code_owner_review) deploiement=\([.rules[] | select(.type == "required_deployments") | .parameters.required_deployment_environments[]] | join(",")) verifications=\([.rules[] | select(.type == "required_status_checks") | .parameters.required_status_checks[].context] | sort | join(","))"'
314
+
315
+ if [ -n "$VERIFY" ]; then
316
+ ecarts=0
317
+ for ruleset in "${rulesets[@]}"; do
318
+ grep -q '"target": "branch"' <<<"$ruleset" || continue
319
+ name="$(sed -n 's/^ "name": "\(.*\)",$/\1/p' <<<"$ruleset")"
320
+ attendu="$(sig_attendue "$ruleset")"
321
+ id="$(gh api "repos/$REPO/rulesets" --jq ".[] | select(.name == \"$name\") | .id")"
322
+ en_ligne=""
323
+ [ -z "$id" ] || en_ligne="$(gh api "repos/$REPO/rulesets/$id" --jq "$SIG_EN_LIGNE")"
324
+ # Vérifications exigées : comparées seulement si --checks est donné (la
325
+ # liste n'est pas dans .repowarden.conf ; sinon faux écart)
326
+ if [ -z "$CHECKS_SET" ]; then
327
+ attendu="${attendu% verifications=*}"
328
+ en_ligne="${en_ligne% verifications=*}"
329
+ fi
330
+ if [ -z "$en_ligne" ]; then
331
+ err_t ci.proteger.verify_missing "$name"
332
+ ecarts=$((ecarts + 1))
333
+ elif [ "$en_ligne" != "$attendu" ]; then
334
+ err_t ci.proteger.verify_diff "$name" "$en_ligne" "$attendu"
335
+ ecarts=$((ecarts + 1))
336
+ fi
337
+ done
338
+ [ "$ecarts" = 0 ] || { err_t ci.proteger.verify_fix; exit 1; }
339
+ ok_t ci.proteger.verify_ok
340
+ exit 0
341
+ fi
342
+
343
+ # Étiquette « urgent » (flux develop) : son merge lance la demande de livraison
344
+ urgent=""
345
+ if [ -n "$integration" ] && ! gh api "repos/$REPO/labels/urgent" --silent 2>/dev/null; then
346
+ urgent=1
347
+ info_t ci.proteger.urgent
348
+ fi
349
+
274
350
  if [ -n "$DRY_RUN" ]; then
275
351
  printf '%s\n' "${rulesets[@]}"
276
352
  [ -z "$env_json" ] || printf '%s\n' "$env_json"
@@ -311,4 +387,6 @@ if [ -n "$env_json" ]; then
311
387
  done
312
388
  ok_t ci.proteger.12 "$ENVIRONMENT"
313
389
  fi
390
+ [ -z "$urgent" ] || gh label create urgent -R "$REPO" --color B60205 \
391
+ --description "Livraison immédiate après merge" >/dev/null
314
392
  ok_t ci.proteger.13
package/bin/repowarden CHANGED
@@ -8,6 +8,12 @@
8
8
  # repowarden code-mort nouveau code mort de la branche
9
9
  # repowarden ticket <n> branche du ticket (créée, liée, si besoin)
10
10
  # repowarden ticket nouveau "<titre>" nouveau ticket
11
+ # repowarden ticket comment <n> "<texte>" commentaire sur un ticket
12
+ # repowarden ticket label <n> <label> [--remove] gère les labels d'un ticket
13
+ # repowarden pr label <n> <label> [--remove] gère les labels d'une PR
14
+ # repowarden pr checks <n> vérifie les checks d'une PR
15
+ # repowarden run rerun <id|--failed|--workflow> relance un workflow
16
+ # repowarden run list [--workflow <nom>] liste les runs récents
11
17
  # repowarden tickets init suivi des tickets (GitHub)
12
18
  # repowarden proteger [options] protection des branches (GitHub)
13
19
  # repowarden npm-publication publication npm automatique
@@ -29,7 +35,7 @@ source "$ROOT/hooks/lib/ui.sh"
29
35
  # shellcheck source=../hooks/lib/i18n.sh
30
36
  source "$ROOT/hooks/lib/i18n.sh"
31
37
 
32
- aide() { sed -n '2,15p' "$ROOT/bin/repowarden" | sed 's/^# \{0,1\}//'; }
38
+ aide() { sed -n '2,21p' "$ROOT/bin/repowarden" | sed 's/^# \{0,1\}//'; }
33
39
 
34
40
  commande="${1:-}"
35
41
  [ $# -eq 0 ] || shift
@@ -48,6 +54,12 @@ case "$commande" in
48
54
  code-mort | deadcode) exec bash "$ROOT/bin/code-mort" "$@" ;;
49
55
  tickets) exec bash "$ROOT/bin/tickets" "$@" ;;
50
56
  ticket) exec bash "$ROOT/bin/ticket" "$@" ;;
57
+ ticket-comment) exec bash "$ROOT/bin/ticket-comment" "$@" ;;
58
+ ticket-label) exec bash "$ROOT/bin/ticket-label" "$@" ;;
59
+ pr-label) exec bash "$ROOT/bin/pr-label" "$@" ;;
60
+ run-rerun) exec bash "$ROOT/bin/run-rerun" "$@" ;;
61
+ run-list) exec bash "$ROOT/bin/run-list" "$@" ;;
62
+ pr-checks) exec bash "$ROOT/bin/pr-checks" "$@" ;;
51
63
  proteger | protect) exec bash "$ROOT/bin/proteger" "$@" ;;
52
64
  npm-publication | npm-publish) exec bash "$ROOT/bin/npm-publication" "$@" ;;
53
65
  app) exec bash "$ROOT/bin/app" "$@" ;;
package/bin/run-list ADDED
@@ -0,0 +1,58 @@
1
+ #!/usr/bin/env bash
2
+ # Lister les runs de workflow GitHub Actions
3
+ # repowarden run list [--workflow <nom>] [--limit N]
4
+ set -euo pipefail
5
+
6
+ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)"
7
+ # shellcheck source=../hooks/lib/common.sh
8
+ source "$ROOT/hooks/lib/common.sh"
9
+ # shellcheck source=../hooks/lib/i18n.sh
10
+ source "$ROOT/hooks/lib/i18n.sh"
11
+
12
+ aide() {
13
+ cat <<EOF
14
+ Usage: repowarden run list [--workflow <nom>] [--limit N]
15
+
16
+ Liste les runs de workflows récents.
17
+
18
+ Options:
19
+ --workflow <nom> Filtre par nom de workflow
20
+ --limit N Nombre max de runs (défaut: 20)
21
+
22
+ Exemples:
23
+ repowarden run list
24
+ repowarden run list --workflow "CI" --limit 10
25
+ EOF
26
+ }
27
+
28
+ if [[ "$1" == "--help" || "$1" == "-h" ]]; then
29
+ aide
30
+ exit 0
31
+ fi
32
+ command -v gh >/dev/null || { err_t bot.no_gh; exit 1; }
33
+
34
+ workflow=""
35
+ limit=20
36
+
37
+ while [ $# -gt 0 ]; do
38
+ case "$1" in
39
+ --workflow) shift; workflow="${1:?--workflow attend un nom}" ;;
40
+ --limit) shift; limit="${1:?--limit attend un nombre}" ;;
41
+ *) err_t install.unknown_arg "$1"; exit 1 ;;
42
+ esac
43
+ shift
44
+ done
45
+
46
+ repo="${GH_REPO:-$(gh repo view --json nameWithOwner -q .nameWithOwner)}"
47
+
48
+ if [ -n "$workflow" ]; then
49
+ step_t run.listing.workflow "$workflow"
50
+ gh run list --repo "$repo" --workflow "$workflow" --limit "$limit" \
51
+ --json databaseId,name,conclusion,createdAt,headBranch \
52
+ --template '{{range .}}{{.databaseId}} {{.name}} {{.conclusion}} {{.createdAt}} {{.headBranch}}{{"\n"}}{{end}}'
53
+ else
54
+ step_t run.listing.all
55
+ gh run list --repo "$repo" --limit "$limit" \
56
+ --json databaseId,name,conclusion,createdAt,headBranch \
57
+ --template '{{range .}}{{.databaseId}} {{.name}} {{.conclusion}} {{.createdAt}} {{.headBranch}}{{"\n"}}{{end}}'
58
+ fi