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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/tickets CHANGED
@@ -70,6 +70,7 @@ type: fix|d73a4a|Correction de bug (branche fix/…)
70
70
  type: docs|0075ca|Documentation (branche docs/…)
71
71
  type: refactor|cfd3d7|Restructuration (branche refactor/…)
72
72
  type: chore|ededed|Maintenance (branche chore/…)
73
+ urgent|b60205|Livraison immédiate au merge (faille, régression bloquante)
73
74
  ETIQUETTES
74
75
 
75
76
  # 2. Modèle de ticket et workflow (jamais écrasés)
@@ -0,0 +1,38 @@
1
+ # shellcheck shell=bash
2
+ # Suivi des tickets, mise en route sur un dépôt existant (ci/tickets.sh
3
+ # adopter, appelé par repowarden tickets init).
4
+ # Chargé par ci/tickets.sh (après hooks/lib/common.sh et hooks/lib/tickets.sh).
5
+ # shellcheck disable=SC2154 # statuts, réglages et compteurs définis dans les autres modules
6
+
7
+ # Mise en route sur un dépôt existant (tickets init, relançable) : tickets
8
+ # ouverts sans statut rangés (validé ou ouvert par un mainteneur → backlog,
9
+ # sinon à valider) ; branches des PR ouvertes et mergées inscrites sur les
10
+ # tickets qu'elles citent. Aucune branche créée en masse : elle naît quand le
11
+ # ticket est pris (repowarden ticket N).
12
+ cmd_adopter() {
13
+ local n assoc labels pr head ranges=0 t
14
+ while IFS=$'\t' read -r n assoc; do
15
+ [ -n "$n" ] || continue
16
+ labels="$(etiquettes "$n")"
17
+ ! grep -q '^statut: ' <<<"$labels" || continue
18
+ if grep -qxF "$VALIDE" <<<"$labels"; then
19
+ statut "$n" "$S_BACKLOG"
20
+ elif [ "${AUTO_VALIDATE:-maintainers}" = maintainers ] && est_mainteneur "$assoc"; then
21
+ gh issue edit "$n" --add-label "$VALIDE" >/dev/null
22
+ statut "$n" "$S_BACKLOG"
23
+ else
24
+ statut "$n" "$S_AVALIDER"
25
+ fi
26
+ ranges=$((ranges + 1))
27
+ done < <(gh api "repos/$GH_REPO/issues?state=open&per_page=100" --paginate \
28
+ -q '.[] | select(.pull_request | not) | [.number, .author_association] | @tsv')
29
+ while IFS=$'\t' read -r pr head; do
30
+ [ -n "$pr" ] || continue
31
+ [[ "$head" != "${INTEGRATION:-develop}" && "$head" != release-please--* ]] || continue
32
+ pr_tickets_r "$(gh pr view "$pr" --json body -q .body)" "$head"
33
+ for t in $REPLY; do inscrire_branche "$t" "$head" "PR #$pr"; done
34
+ done < <(gh pr list --state all --limit 1000 --json number,headRefName,state \
35
+ -q '.[] | select(.state != "CLOSED") | [.number, .headRefName] | @tsv')
36
+ _tr tk.adopted "$ranges" "$INSCRITES"
37
+ echo "$_T"
38
+ }
@@ -0,0 +1,63 @@
1
+ # shellcheck shell=bash
2
+ # Suivi des tickets, outils communs : étiquettes et assignés d'un ticket,
3
+ # changement de statut, ticket validé, mainteneur, tickets cités par une PR,
4
+ # PR qui a fermé un ticket, notices.
5
+ # Chargé par ci/tickets.sh (après hooks/lib/common.sh et hooks/lib/tickets.sh).
6
+ # shellcheck disable=SC2154 # statuts, réglages et compteurs définis dans les autres modules
7
+
8
+ # REPLY = numéros de tickets (uniques) cités par la description $1 (« Ticket : #12 »,
9
+ # « Tickets : #3, #4 », ou un mot-clé de fermeture) et la branche $2 (type/12-sujet)
10
+ pr_tickets_r() {
11
+ local body="$1" branch="$2" found="" n line
12
+ while IFS= read -r line; do
13
+ if [[ "$line" =~ ^[[:space:]]*(Tickets?|Refs?|Closes|Fixes|Resolves)[[:space:]]*:?[[:space:]]*(.*)$ ]]; then
14
+ for n in $(grep -oE '#[0-9]+' <<<"${BASH_REMATCH[2]}" | tr -d '#'); do
15
+ [[ " $found " == *" $n "* ]] || found="$found $n"
16
+ done
17
+ fi
18
+ done <<<"$body"
19
+ branch_ticket_r "$branch"
20
+ n="$REPLY"
21
+ if [ -n "$n" ]; then
22
+ [[ " $found " == *" $n "* ]] || found="$found $n"
23
+ fi
24
+ REPLY="${found# }"
25
+ }
26
+
27
+ # Statut $2 sur le ticket $1 : remplace les autres étiquettes « statut: »
28
+ statut() {
29
+ local n="$1" s="$2" l retirer=()
30
+ while IFS= read -r l; do
31
+ [[ "$l" == "statut: "* && "$l" != "$s" ]] && retirer+=(--remove-label "$l")
32
+ done < <(gh issue view "$n" --json labels -q '.labels[].name' 2>/dev/null || true)
33
+ # ${a[@]+…} : tableau vide accepté sous set -u par le bash 3.2 de macOS
34
+ gh issue edit "$n" --add-label "$s" ${retirer[@]+"${retirer[@]}"} >/dev/null
35
+ }
36
+
37
+ etiquettes() { gh issue view "$1" --json labels -q '.labels[].name' 2>/dev/null || true; }
38
+
39
+ # REPLY = « <n° de PR> <branche cible> » de la PR dont le merge a fermé le
40
+ # ticket $1, vide si le ticket a été fermé autrement (à la main, release…)
41
+ fermee_par_pr_r() {
42
+ # shellcheck disable=SC2016 # variables GraphQL, pas du shell
43
+ REPLY="$(gh api graphql -f query='query($o: String!, $r: String!, $n: Int!) {
44
+ repository(owner: $o, name: $r) { issue(number: $n) {
45
+ timelineItems(last: 1, itemTypes: CLOSED_EVENT) { nodes { ... on ClosedEvent {
46
+ closer { ... on PullRequest { number baseRefName } } } } } } } }' \
47
+ -f o="${GH_REPO%%/*}" -f r="${GH_REPO#*/}" -F n="$1" \
48
+ -q '.data.repository.issue.timelineItems.nodes[0].closer // empty | "\(.number) \(.baseRefName)"' \
49
+ 2>/dev/null || true)"
50
+ }
51
+
52
+ # Personnes assignées au ticket $1 (une par ligne)
53
+ assignes() { gh issue view "$1" --json assignees -q '.assignees[].login' 2>/dev/null || true; }
54
+
55
+ est_mainteneur() { [[ "${1:-}" =~ ^(OWNER|MEMBER|COLLABORATOR)$ ]]; }
56
+
57
+ est_valide() { grep -qxF "$VALIDE" <<<"$(etiquettes "$1")"; }
58
+
59
+ notice() { echo "::notice title=repowarden::$*"; }
60
+
61
+ notice_t() { _tr "$@"; notice "$_T"; }
62
+
63
+ # Chargé par les tests (source) : fonctions seulement
@@ -0,0 +1,92 @@
1
+ # shellcheck shell=bash
2
+ # Suivi des tickets, événements de ticket (ci/tickets.sh issue) : validation
3
+ # (d'office pour un mainteneur), assignation, fermeture.
4
+ # Chargé par ci/tickets.sh (après hooks/lib/common.sh et hooks/lib/tickets.sh).
5
+ # shellcheck disable=SC2154 # statuts, réglages et compteurs définis dans les autres modules
6
+
7
+ # Ticket $1 validé : backlog s'il attendait la validation, sa branche, et
8
+ # vérification relancée sur les PR ouvertes qui l'attendaient
9
+ accepter() {
10
+ local n="$1" pr head sha body base brouillon
11
+ if grep -qxF "$S_AVALIDER" <<<"$(etiquettes "$n")"; then
12
+ statut "$n" "$S_BACKLOG"
13
+ fi
14
+ # La branche naît quand le ticket est pris (assigné) : un ticket validé
15
+ # pour plus tard n'a pas de branche qui vieillit en attendant
16
+ if [ -n "$(assignes "$n")" ]; then
17
+ creer_branche "$n"
18
+ else
19
+ _tr tk.validated_waiting "$n"
20
+ gh issue comment "$n" --body "$_T" >/dev/null
21
+ fi
22
+ while IFS=$'\t' read -r pr head sha base brouillon; do
23
+ [ -n "$pr" ] || continue
24
+ body="$(gh pr view "$pr" --json body -q .body)"
25
+ pr_tickets_r "$body" "$head"
26
+ [[ " $REPLY " == *" $n "* ]] || continue
27
+ if verifier "$pr" "$sha" "$REPLY"; then
28
+ merge_auto "$pr" "$base" "$brouillon"
29
+ fi
30
+ done < <(gh pr list --state open --json number,headRefName,headRefOid,baseRefName,isDraft \
31
+ -q '.[] | [.number, .headRefName, .headRefOid, .baseRefName, .isDraft] | @tsv')
32
+ }
33
+
34
+ cmd_issue() {
35
+ local pr sha head body
36
+ case "$ACTION" in
37
+ opened)
38
+ est_valide "$ISSUE" && return 0
39
+ # Ouvert par un mainteneur (droits d'écriture) : c'est déjà sa
40
+ # décision, validé d'office ; sinon, un mainteneur pose « validé »
41
+ if [ "${AUTO_VALIDATE:-maintainers}" = maintainers ] &&
42
+ est_mainteneur "${ISSUE_ASSOCIATION:-}"; then
43
+ gh issue edit "$ISSUE" --add-label "$VALIDE" >/dev/null
44
+ statut "$ISSUE" "$S_AVALIDER"
45
+ accepter "$ISSUE"
46
+ else
47
+ statut "$ISSUE" "$S_AVALIDER"
48
+ fi
49
+ ;;
50
+ labeled)
51
+ [ "${LABEL:-}" = "$VALIDE" ] || return 0
52
+ accepter "$ISSUE"
53
+ ;;
54
+ assigned)
55
+ # Pris en charge : sa branche (s'il est validé), en cours s'il
56
+ # attendait dans le backlog
57
+ est_valide "$ISSUE" && creer_branche "$ISSUE"
58
+ grep -qxF "$S_BACKLOG" <<<"$(etiquettes "$ISSUE")" && statut "$ISSUE" "$S_ENCOURS"
59
+ return 0
60
+ ;;
61
+ closed)
62
+ # Fermé par GitHub au merge d'une PR liée dans la branche par défaut
63
+ # (develop) : pas encore en production, rouvert en préprod. Une
64
+ # fermeture à la main (sans PR) est respectée.
65
+ if [ "${STATE_REASON:-}" = completed ] && ! grep -qxF "$S_DONE" <<<"$(etiquettes "$ISSUE")"; then
66
+ fermee_par_pr_r "$ISSUE"
67
+ local fpr="${REPLY%% *}" fbase="${REPLY#* }"
68
+ if [ -n "$fpr" ] && [ "$fbase" != "${MAIN:-main}" ]; then
69
+ gh issue reopen "$ISSUE" >/dev/null
70
+ statut "$ISSUE" "$S_PREPROD"
71
+ _tr tk.reopened "$fpr" "$fbase"
72
+ gh issue comment "$ISSUE" --body "$_T" >/dev/null
73
+ notice "$_T"
74
+ fi
75
+ return 0
76
+ fi
77
+ # Abandonné : PR fermées, branche supprimée (un ticket terminé est
78
+ # fermé par la release, sa branche est déjà supprimée au merge)
79
+ [ "${STATE_REASON:-}" = not_planned ] || return 0
80
+ branche_du_ticket_r "$ISSUE"
81
+ [ -n "$REPLY" ] || return 0
82
+ local branche="$REPLY"
83
+ while IFS= read -r pr; do
84
+ [ -n "$pr" ] || continue
85
+ _tr tk.abandoned "$ISSUE"
86
+ gh pr close "$pr" --comment "$_T" >/dev/null
87
+ done < <(gh pr list --state open --head "$branche" --json number -q '.[].number')
88
+ gh api -X DELETE "repos/$GH_REPO/git/refs/heads/$branche" >/dev/null
89
+ notice_t tk.branch_deleted "$branche" "$ISSUE"
90
+ ;;
91
+ esac
92
+ }
@@ -0,0 +1,70 @@
1
+ # shellcheck shell=bash
2
+ # Suivi des tickets, événements de PR (ci/tickets.sh pr) : ticket créé s'il
3
+ # manque, statuts, PR sans objet depuis main ou develop, vérification.
4
+ # Chargé par ci/tickets.sh (après hooks/lib/common.sh et hooks/lib/tickets.sh).
5
+ # shellcheck disable=SC2154 # statuts, réglages et compteurs définis dans les autres modules
6
+
7
+ cmd_pr() {
8
+ local body tickets n url
9
+ : "${PR:?}" "${HEAD:?}" "${HEAD_SHA:?}" "${ACTION:?}"
10
+ # PR partant d'une branche persistante : seules la livraison (develop →
11
+ # main) et le retour (main → develop, si main a un contenu propre) ont un
12
+ # sens ; les autres sont fermées, avec la marche à suivre
13
+ if [[ "$HEAD" == "${INTEGRATION:-develop}" || "$HEAD" == "${MAIN:-main}" ]] &&
14
+ [[ "$ACTION" == opened || "$ACTION" == reopened ]]; then
15
+ if [[ "$HEAD" == "${INTEGRATION:-develop}" && "${BASE:-}" != "${MAIN:-main}" ]] ||
16
+ [[ "$HEAD" == "${MAIN:-main}" && "${BASE:-}" != "${INTEGRATION:-develop}" ]]; then
17
+ _tr tk.pr_inverted "$HEAD" "${BASE:-}" "${INTEGRATION:-develop}"
18
+ gh pr close "$PR" --comment "$_T" >/dev/null
19
+ notice "$_T"
20
+ elif [ "$HEAD" = "${MAIN:-main}" ] &&
21
+ [ "$(gh api "repos/$GH_REPO/compare/$BASE...$HEAD" -q '.files | length')" = 0 ]; then
22
+ _tr tk.pr_backmerge_empty "$HEAD" "$BASE"
23
+ gh pr close "$PR" --comment "$_T" >/dev/null
24
+ notice "$_T"
25
+ fi
26
+ fi
27
+ # Bots de dépendances, PR de release, livraisons et retours : pas de ticket
28
+ # exigé. Les PR ouvertes par le suivi des tickets lui-même (jeton des
29
+ # Actions, github-actions[bot]) citent leur ticket : suivies normalement.
30
+ if [[ "${AUTHOR:-}" =~ ^(dependabot|renovate)(\[bot\])?$ || "$HEAD" == release-please--* ||
31
+ "$HEAD" == "${INTEGRATION:-develop}" || "$HEAD" == "${MAIN:-main}" ]]; then
32
+ gh api "repos/$GH_REPO/statuses/$HEAD_SHA" -f state=success -f context=ticket \
33
+ -f description="Sans ticket (bot, release ou livraison)" >/dev/null
34
+ return 0
35
+ fi
36
+ body="$(gh pr view "$PR" --json body -q .body)"
37
+ pr_tickets_r "$body" "$HEAD"
38
+ tickets="$REPLY"
39
+
40
+ if [ "$ACTION" = closed ]; then
41
+ [ "${MERGED:-false}" = true ] || return 0
42
+ for n in $tickets; do
43
+ statut "$n" "$S_PREPROD"
44
+ _tr tk.merged "$PR" "$BASE"
45
+ gh issue comment "$n" --body "$_T" >/dev/null
46
+ done
47
+ return 0
48
+ fi
49
+
50
+ # Aucun ticket : créé à partir de la PR, à valider, et relié à la PR
51
+ if [ -z "$tickets" ]; then
52
+ _tr tk.created_body "$PR"
53
+ url="$(gh issue create --title "$(gh pr view "$PR" --json title -q .title)" \
54
+ --body "$_T" --label "$S_AVALIDER")"
55
+ tickets="${url##*/}"
56
+ gh pr edit "$PR" --body "$(printf '%s\n\nTicket : #%s\n' "$body" "$tickets")" >/dev/null
57
+ notice_t tk.created "$tickets" "$PR"
58
+ fi
59
+
60
+ # Statut du travail (seulement pour un ticket validé)
61
+ for n in $tickets; do
62
+ est_valide "$n" || continue
63
+ if [ "${DRAFT:-false}" = true ]; then statut "$n" "$S_ENCOURS"; else statut "$n" "$S_RELECTURE"; fi
64
+ done
65
+ # Le blocage est porté par le statut « ticket » (exigé par la protection) :
66
+ # le job reste vert, un rouge signale une vraie panne
67
+ if verifier "$PR" "$HEAD_SHA" "$tickets"; then
68
+ merge_auto "$PR" "${BASE:-}" "${DRAFT:-false}"
69
+ fi
70
+ }
@@ -0,0 +1,49 @@
1
+ # shellcheck shell=bash
2
+ # Suivi des tickets, premier push d'une branche de ticket (PR brouillon) et
3
+ # revues (ci/tickets.sh push, revue).
4
+ # Chargé par ci/tickets.sh (après hooks/lib/common.sh et hooks/lib/tickets.sh).
5
+ # shellcheck disable=SC2154 # statuts, réglages et compteurs définis dans les autres modules
6
+
7
+ # Premier push sur la branche d'un ticket : PR brouillon vers la branche
8
+ # d'intégration, citant le ticket, assignée à l'auteur du push ; ticket en
9
+ # cours. Ouverte par le jeton des Actions, elle ne lance pas la CI : elle
10
+ # tourne au push suivant, ou au passage « prête pour revue ».
11
+ cmd_push() {
12
+ local n pr titre type url
13
+ : "${PUSH_BRANCH:?}" "${SHA:?}"
14
+ [ "${DELETED:-false}" != true ] || return 0
15
+ branch_ticket_r "$PUSH_BRANCH"
16
+ n="$REPLY"
17
+ [ -n "$n" ] || return 0
18
+ # Branche faite hors du ticket (à la main) : inscrite sur le ticket
19
+ inscrire_branche "$n" "$PUSH_BRANCH"
20
+ pr="$(gh pr list --state open --head "$PUSH_BRANCH" --json number -q '.[0].number // empty')"
21
+ [ -z "$pr" ] || return 0
22
+ [ "$(gh api "repos/$GH_REPO/compare/$INTEGRATION...$PUSH_BRANCH" -q .ahead_by)" != 0 ] || return 0
23
+ titre="$(gh issue view "$n" --json title -q .title)"
24
+ type="${PUSH_BRANCH%%/*}"
25
+ case "$type" in feature) type=feat ;; bugfix | hotfix) type=fix ;; esac
26
+ # Titre conventionnel (message du commit en squash) : type: titre du ticket
27
+ titre="$type: $(tr '[:upper:]' '[:lower:]' <<<"${titre:0:1}")${titre:1}"
28
+ authored_length "$titre"
29
+ [ "$REPLY" -le 72 ] || titre="${titre:0:72}"
30
+ url="$(gh pr create --draft --base "$INTEGRATION" --head "$PUSH_BRANCH" --title "$titre" \
31
+ --body "Ticket : #$n" ${PUSHER:+--assignee "$PUSHER"})"
32
+ [ -z "${PUSHER:-}" ] || gh issue edit "$n" --add-assignee "$PUSHER" >/dev/null || true
33
+ est_valide "$n" && statut "$n" "$S_ENCOURS"
34
+ notice_t tk.draft_opened "${url##*/}" "$n"
35
+ verifier "${url##*/}" "$SHA" "$n" || true
36
+ }
37
+
38
+ # Revue : corrections demandées → le ticket repasse en cours
39
+ cmd_revue() {
40
+ local body n
41
+ : "${PR:?}" "${HEAD:?}"
42
+ [ "${REVIEW_STATE:-}" = changes_requested ] || return 0
43
+ body="$(gh pr view "$PR" --json body -q .body)"
44
+ pr_tickets_r "$body" "$HEAD"
45
+ for n in $REPLY; do
46
+ est_valide "$n" && statut "$n" "$S_ENCOURS"
47
+ done
48
+ return 0
49
+ }
@@ -0,0 +1,83 @@
1
+ # shellcheck shell=bash
2
+ # Suivi des tickets, branche du ticket : registre des branches (repère dans
3
+ # les commentaires du bot), branche existante, création liée au ticket.
4
+ # Chargé par ci/tickets.sh (après hooks/lib/common.sh et hooks/lib/tickets.sh).
5
+ # shellcheck disable=SC2154 # statuts, réglages et compteurs définis dans les autres modules
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
+ }
21
+
22
+ # Inscrit la branche $2 sur le ticket $1, une seule fois ($3 : origine, ex. PR #7).
23
+ # Une seconde branche pour le même ticket est inscrite et signalée.
24
+ INSCRITES=0
25
+
26
+ inscrire_branche() {
27
+ local n="$1" b="$2" origine="${3:-}"
28
+ branches_inscrites_r "$n"
29
+ [[ " $REPLY " != *" $b "* ]] || return 0
30
+ if [ -n "$REPLY" ]; then
31
+ _tr tk.branch_second "$b" "${REPLY// /, }"
32
+ notice "$_T"
33
+ else
34
+ _tr tk.branch_registered "$b"
35
+ fi
36
+ [ -z "$origine" ] || _T="$_T ($origine)"
37
+ gh issue comment "$n" --body "$_T"$'\n\n'"$(repere "$b")" >/dev/null
38
+ INSCRITES=$((INSCRITES + 1))
39
+ }
40
+
41
+ # REPLY = branche du dépôt portant le ticket $1 (type/<n>-…), vide si aucune
42
+ branche_du_ticket_r() {
43
+ local ref
44
+ REPLY=""
45
+ while IFS= read -r ref; do
46
+ ref="${ref#refs/heads/}"
47
+ branch_ticket_r "$ref"
48
+ if [ "$REPLY" = "$1" ]; then REPLY="$ref"; return 0; fi
49
+ REPLY=""
50
+ done < <(gh api "repos/$GH_REPO/git/matching-refs/heads/" --paginate -q '.[].ref' 2>/dev/null || true)
51
+ }
52
+
53
+ # Ticket $1 validé : sa branche naît de la branche d'intégration, liée au
54
+ # ticket (panneau « Development »). Rien si elle existe déjà, ou si une PR
55
+ # ouverte cite déjà le ticket (travail commencé hors de sa branche).
56
+ creer_branche() {
57
+ local n="$1" titre labels branche pr head body
58
+ # Travail déjà mergé ou en production : plus de branche à (re)créer
59
+ travail_termine "$(etiquettes "$n")" && return 0
60
+ branche_du_ticket_r "$n"
61
+ [ -z "$REPLY" ] || return 0
62
+ while IFS=$'\t' read -r pr head; do
63
+ [ -n "$pr" ] || continue
64
+ body="$(gh pr view "$pr" --json body -q .body)"
65
+ pr_tickets_r "$body" "$head"
66
+ [[ " $REPLY " != *" $n "* ]] || return 0
67
+ done < <(gh pr list --state open --json number,headRefName -q '.[] | [.number, .headRefName] | @tsv')
68
+ titre="$(gh issue view "$n" --json title -q .title)"
69
+ labels="$(etiquettes "$n")"
70
+ ticket_type_r "$labels"
71
+ ticket_branch_r "$n" "$titre" "$REPLY"
72
+ branche="$REPLY"
73
+ # shellcheck disable=SC2016 # variables GraphQL, pas du shell
74
+ gh api graphql -f query='mutation($issue: ID!, $repo: ID!, $oid: GitObjectID!, $name: String!) {
75
+ createLinkedBranch(input: {issueId: $issue, repositoryId: $repo, oid: $oid, name: $name}) { linkedBranch { id } }
76
+ }' -f issue="$(gh issue view "$n" --json id -q .id)" \
77
+ -f repo="$(gh api "repos/$GH_REPO" -q .node_id)" \
78
+ -f oid="$(gh api "repos/$GH_REPO/git/ref/heads/$INTEGRATION" -q .object.sha)" \
79
+ -f name="$branche" >/dev/null
80
+ _tr tk.branch_created "$branche" "$INTEGRATION" "$n" "$branche"
81
+ gh issue comment "$n" --body "$_T"$'\n\n'"$(repere "$branche")" >/dev/null
82
+ notice_t tk.branch_notice "$branche" "$n"
83
+ }
@@ -0,0 +1,20 @@
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.
4
+ # Chargé par ci/tickets.sh (après hooks/lib/common.sh et hooks/lib/tickets.sh).
5
+ # shellcheck disable=SC2154 # statuts, réglages et compteurs définis dans les autres modules
6
+
7
+ cmd_release() {
8
+ local n
9
+ while IFS= read -r n; do
10
+ [ -n "$n" ] || continue
11
+ if [[ "$TAG" == *-* ]]; then
12
+ _tr tk.prerelease "$TAG"
13
+ gh issue comment "$n" --body "$_T" >/dev/null
14
+ else
15
+ _tr tk.released "$TAG"
16
+ statut "$n" "$S_DONE"
17
+ gh issue close "$n" --comment "$_T" >/dev/null
18
+ fi
19
+ done < <(gh issue list --state open --label "$S_PREPROD" --limit 500 --json number -q '.[].number')
20
+ }
@@ -0,0 +1,47 @@
1
+ # shellcheck shell=bash
2
+ # Suivi des tickets, vérification « ticket » d'une PR (statut de commit
3
+ # exigé par la protection) et merge automatique des PR de travail.
4
+ # Chargé par ci/tickets.sh (après hooks/lib/common.sh et hooks/lib/tickets.sh).
5
+ # shellcheck disable=SC2154 # statuts, réglages et compteurs définis dans les autres modules
6
+
7
+ # Vérification « ticket » (statut de commit) de la PR $1, commit $2
8
+ verifier() {
9
+ local pr="$1" sha="$2" tickets="$3" n attente="" liste
10
+ for n in $tickets; do est_valide "$n" || attente="$attente #$n"; done
11
+ if [ -z "$tickets" ]; then
12
+ _tr tk.none; etat=failure
13
+ elif [ -n "$attente" ]; then
14
+ _tr tk.waiting "${attente# }" "$VALIDE"; etat=failure
15
+ else
16
+ liste=""
17
+ for n in $tickets; do liste="$liste #$n"; done
18
+ _tr tk.ok "${liste# }"; etat=success
19
+ fi
20
+ echo "$_T"
21
+ gh api "repos/$GH_REPO/statuses/$sha" -f state="$etat" -f context=ticket \
22
+ -f description="${_T:0:140}" >/dev/null
23
+ [ "$etat" = success ]
24
+ }
25
+
26
+ # Merge automatique (squash) de la PR de travail $1, cible $2, brouillon $3 :
27
+ # armé quand elle est prête, vise la branche d'intégration et que son ticket
28
+ # est validé (appelé après une vérification réussie) ; désarmé en brouillon.
29
+ # Seulement avec le jeton de l'App : un merge fait avec le jeton des Actions ne
30
+ # déclencherait aucun workflow (préversion, livraison, tickets).
31
+ merge_auto() {
32
+ local pr="$1" base="$2" brouillon="$3"
33
+ [ "${AUTO_MERGE:-true}" = true ] && [ "$base" = "${INTEGRATION:-develop}" ] || return 0
34
+ if [ "$brouillon" = true ]; then
35
+ gh pr merge "$pr" --disable-auto >/dev/null 2>&1 || true
36
+ return 0
37
+ fi
38
+ if [ "${APP_TOKEN:-false}" != true ]; then
39
+ notice_t tk.automerge_no_app "$pr"
40
+ return 0
41
+ fi
42
+ if gh pr merge "$pr" --auto --squash >/dev/null 2>&1; then
43
+ notice_t tk.automerge_armed "$pr"
44
+ else
45
+ notice_t tk.automerge_failed "$pr"
46
+ fi
47
+ }
package/ci/tickets.sh ADDED
@@ -0,0 +1,68 @@
1
+ #!/usr/bin/env bash
2
+ # Suivi des tickets (issues GitHub) : chaque PR est reliée à un ticket validé,
3
+ # et le ticket suit le travail jusqu'à la production.
4
+ #
5
+ # ci/tickets.sh pr événement pull_request : ticket créé s'il manque,
6
+ # statut du ticket, vérification « ticket » de la PR
7
+ # ci/tickets.sh issue événement issues : nouveau ticket à valider ;
8
+ # étiquette « validé » → backlog, branche du ticket
9
+ # créée (liée au ticket), PR débloquées ; assigné →
10
+ # en cours ; fermé « not planned » → PR et branche
11
+ # fermées
12
+ # ci/tickets.sh push premier push sur la branche d'un ticket : PR
13
+ # brouillon ouverte, ticket en cours
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
17
+ # ci/tickets.sh adopter mise en route (tickets init) : tickets existants
18
+ # rangés, branches des PR inscrites sur leurs tickets
19
+ #
20
+ # Le ticket d'abord, la branche en découle : à valider → (validé : branche
21
+ # type/12-titre) backlog → en cours → en relecture → préprod → done. Seul un
22
+ # mainteneur pose « validé » (droits GitHub).
23
+ # Lien PR → ticket : « Ticket : #12 » dans la description, ou une branche
24
+ # feat/12-sujet. « Closes #12 » fermerait le ticket dès le merge dans develop :
25
+ # c'est la release qui le ferme.
26
+ #
27
+ # Variables : GH_TOKEN, GH_REPO, INTEGRATION ; pr : PR, ACTION, MERGED, DRAFT,
28
+ # HEAD, BASE, HEAD_SHA, AUTHOR ; issue : ISSUE, ACTION, LABEL, STATE_REASON ;
29
+ # push : PUSH_BRANCH, DELETED, PUSHER, SHA ; revue : PR, HEAD, REVIEW_STATE ;
30
+ # release : TAG
31
+ set -euo pipefail
32
+
33
+ REPOWARDEN_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)"
34
+ # shellcheck source=../hooks/lib/common.sh
35
+ source "$REPOWARDEN_DIR/hooks/lib/common.sh"
36
+ # shellcheck source=../hooks/lib/tickets.sh
37
+ source "$REPOWARDEN_DIR/hooks/lib/tickets.sh"
38
+ INTEGRATION="${INTEGRATION:-develop}"
39
+
40
+ # Modules (un rôle chacun, voir leur en-tête)
41
+ # shellcheck source=tickets/commun.sh
42
+ source "$REPOWARDEN_DIR/ci/tickets/commun.sh"
43
+ # shellcheck source=tickets/registre.sh
44
+ source "$REPOWARDEN_DIR/ci/tickets/registre.sh"
45
+ # shellcheck source=tickets/verification.sh
46
+ source "$REPOWARDEN_DIR/ci/tickets/verification.sh"
47
+ # shellcheck source=tickets/pr.sh
48
+ source "$REPOWARDEN_DIR/ci/tickets/pr.sh"
49
+ # shellcheck source=tickets/issue.sh
50
+ source "$REPOWARDEN_DIR/ci/tickets/issue.sh"
51
+ # shellcheck source=tickets/push.sh
52
+ source "$REPOWARDEN_DIR/ci/tickets/push.sh"
53
+ # shellcheck source=tickets/release.sh
54
+ source "$REPOWARDEN_DIR/ci/tickets/release.sh"
55
+ # shellcheck source=tickets/adopter.sh
56
+ source "$REPOWARDEN_DIR/ci/tickets/adopter.sh"
57
+
58
+ [[ "${BASH_SOURCE[0]}" == "$0" ]] || return 0
59
+
60
+ case "${1:-}" in
61
+ pr) cmd_pr ;;
62
+ issue) cmd_issue ;;
63
+ push) cmd_push ;;
64
+ revue) cmd_revue ;;
65
+ release) cmd_release ;;
66
+ adopter) cmd_adopter ;;
67
+ *) echo "usage : ci/tickets.sh pr|issue|push|revue|release|adopter" >&2; exit 2 ;;
68
+ esac
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "repowarden",
3
- "version": "4.3.0-next.6",
3
+ "version": "4.4.0-next.4",
4
4
  "description": "Hooks git et CI multi-langages : messages de commit, nommage des branches, secrets, formatage, tests ciblés, code mort, releases.",
5
5
  "keywords": [
6
6
  "git-hooks",
@@ -24,6 +24,8 @@
24
24
  },
25
25
  "files": [
26
26
  "bin/",
27
+ "ci/tickets.sh",
28
+ "ci/tickets/",
27
29
  "hooks/",
28
30
  "install.sh"
29
31
  ],