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 +86 -25
- package/bin/commit +12 -6
- package/bin/pr-checks +35 -0
- package/bin/pr-label +51 -0
- package/bin/proteger +87 -9
- package/bin/repowarden +13 -1
- package/bin/run-list +58 -0
- package/bin/run-rerun +65 -0
- package/bin/statut +1 -0
- package/bin/ticket +5 -0
- package/bin/ticket-comment +32 -0
- package/bin/ticket-label +52 -0
- package/bin/tickets +14 -1
- package/ci/tickets/issue.sh +19 -4
- package/ci/tickets/pr.sh +23 -7
- package/ci/tickets/push.sh +95 -13
- package/ci/tickets/registre.sh +19 -0
- package/ci/tickets/release.sh +1 -0
- package/hooks/commit-msg +33 -0
- package/hooks/lib/common.sh +17 -0
- package/hooks/lib/i18n/en.sh +51 -2
- package/hooks/lib/i18n/fr.sh +51 -2
- package/hooks/lib/tickets.sh +73 -5
- package/hooks/lib/ui.sh +21 -1
- package/hooks/pre-push +89 -2
- package/hooks/prepare-commit-msg +4 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
# repowarden
|
|
2
2
|
|
|
3
|
-
[](https://github.com/simdevWarden/repowarden/actions/workflows/ci.yml)
|
|
4
|
+
[](https://github.com/simdevWarden/repowarden/actions/workflows/e2e.yml)
|
|
5
|
+
[](https://github.com/simdevWarden/repowarden/releases)
|
|
6
6
|
[](https://www.npmjs.com/package/repowarden)
|
|
7
|
-
[](https://
|
|
8
|
-
[](https://simdevwarden.github.io/repowarden/)
|
|
8
|
+
[](https://scorecard.dev/viewer/?uri=github.com/simdevWarden/repowarden)
|
|
9
9
|
[](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://
|
|
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://
|
|
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
|
|
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://
|
|
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
|
|
36
|
-
repowarden install --global
|
|
37
|
-
repowarden
|
|
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
|
|
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/
|
|
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
|
-
**
|
|
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:
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
###
|
|
148
|
+
### 4. Contribuer à repowarden
|
|
89
149
|
|
|
90
150
|
```bash
|
|
91
|
-
git clone https://github.com/
|
|
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
|
-
|
|
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.
|
|
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/
|
|
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://
|
|
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 "
|
|
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
|
-
|
|
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
|
-
|
|
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 :
|
|
186
|
-
#
|
|
187
|
-
#
|
|
188
|
-
#
|
|
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"
|
|
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,
|
|
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
|