-
Notifications
You must be signed in to change notification settings - Fork 1
142 lines (118 loc) · 4.82 KB
/
issue-triage.yml
File metadata and controls
142 lines (118 loc) · 4.82 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
name: Issue Triage
on:
workflow_call:
inputs:
issue_number:
description: "Issue number to triage (optional, auto-detected from event)"
required: false
type: number
secrets:
COPILOT_GITHUB_TOKEN:
required: true
description: "Fine-grained PAT from a GitHub user with a Copilot license and Copilot Requests permission"
# The following trigger is commented out since this is a reusable workflow.
# Uncomment it if you want to use this workflow directly in this repository.
# issues:
# types: [opened]
permissions:
contents: read
issues: write
jobs:
triage-issue:
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: read
issues: write
steps:
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 22
- name: Install Copilot CLI
run: npm install -g @github/copilot
- name: Collect issue context
env:
GH_TOKEN: ${{ github.token }}
ISSUE_NUMBER: ${{ inputs.issue_number || github.event.issue.number }}
run: |
set -euo pipefail
gh label list \
--repo "$GITHUB_REPOSITORY" \
--limit 200 \
--json name,description \
> labels.json
gh issue view "$ISSUE_NUMBER" \
--repo "$GITHUB_REPOSITORY" \
--json number,title,body,comments,labels,author,state,stateReason,url \
> issue.json
- name: Select labels with Copilot
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
run: |
set -euo pipefail
copilot -s \
--no-ask-user \
--allow-tool=read \
-p "$(cat <<'PROMPT'
You are a GitHub issue label triage assistant.
Read ./issue.json and ./labels.json.
Select labels that accurately describe the issue. Use only label names that already exist in labels.json.
Do not invent labels. Do not comment on the issue. Do not ask questions.
Prefer this taxonomy when the labels exist:
- type: bug, type: feature, type: docs, type: build, type: ci, type: tests, type: maintenance, type: question
- area:* labels for affected subsystem
- platform:* labels only when the issue is platform-specific
- priority:* only when urgency is explicit in the issue metadata
- impact:* only for clear user-visible impact such as crash, memory leak, or visual diff
- contrib:* only when the issue is suitable for external contribution
- status:* only if already present or explicitly justified by the issue metadata
Use the issue title, body, comments, and existing labels.
If unsure, choose fewer labels.
Output only a JSON array of label names, for example:
["type: bug","area: layout"]
PROMPT
)" > copilot-output.txt
python3 <<'PY'
import json
import re
from pathlib import Path
raw = Path("copilot-output.txt").read_text()
labels = {label["name"] for label in json.loads(Path("labels.json").read_text())}
issue = json.loads(Path("issue.json").read_text())
existing = {label["name"] for label in issue.get("labels", [])}
try:
parsed = json.loads(raw)
except json.JSONDecodeError:
match = re.search(r"```(?:json)?\s*(\[[\s\S]*?\])\s*```", raw)
if match is None:
match = re.search(r"(\[[\s\S]*\])", raw)
if match is None:
raise SystemExit(f"Copilot output did not contain a JSON array:\n{raw}")
parsed = json.loads(match.group(1))
if not isinstance(parsed, list):
raise SystemExit(f"Copilot output must be a JSON array, got: {type(parsed).__name__}")
selected = []
for label in parsed:
if not isinstance(label, str):
continue
if label in labels and label not in existing and label not in selected:
selected.append(label)
Path("selected-labels.json").write_text(json.dumps(selected, indent=2) + "\n")
print(json.dumps(selected))
PY
- name: Apply labels
env:
GH_TOKEN: ${{ github.token }}
ISSUE_NUMBER: ${{ inputs.issue_number || github.event.issue.number }}
run: |
set -euo pipefail
if [ "$(jq 'length' selected-labels.json)" -eq 0 ]; then
echo "No new labels selected."
exit 0
fi
labels="$(jq -c '{labels: .}' selected-labels.json)"
gh api \
--method POST \
"/repos/$GITHUB_REPOSITORY/issues/$ISSUE_NUMBER/labels" \
--input - <<< "$labels"