Skip to content

Commit a4e3d96

Browse files
authored
Add GitHub auto-label workflow (Stanzin7#216)
1 parent c9dfda7 commit a4e3d96

2 files changed

Lines changed: 285 additions & 0 deletions

File tree

.github/labeler.yml

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
"area: backend":
2+
- changed-files:
3+
- any-glob-to-any-file:
4+
- "src/extension_shield/**"
5+
- "tests/api/**"
6+
- "tests/scoring/**"
7+
- "tests/governance/**"
8+
- "tests/workflow/**"
9+
- "tests/llm/**"
10+
- "tests/test_*.py"
11+
- "pyproject.toml"
12+
13+
"area: frontend":
14+
- changed-files:
15+
- any-glob-to-any-file:
16+
- "frontend/**"
17+
18+
"area: extension":
19+
- changed-files:
20+
- any-glob-to-any-file:
21+
- "packages/extension/**"
22+
23+
"area: cli":
24+
- changed-files:
25+
- any-glob-to-any-file:
26+
- "packages/cli/**"
27+
- "src/extension_shield/cli/**"
28+
29+
"area: docs":
30+
- changed-files:
31+
- any-glob-to-any-file:
32+
- "README.md"
33+
- "docs/**"
34+
- "scripts/README.md"
35+
- "packages/**/README.md"
36+
- "supabase/README.md"
37+
38+
"area: infra":
39+
- changed-files:
40+
- any-glob-to-any-file:
41+
- ".github/**"
42+
- "Dockerfile"
43+
- "docker-compose.yml"
44+
- "railway.toml"
45+
- "deploy/**"
46+
- "supabase/**"
47+
- "Makefile"
48+
- "package.json"
49+
- "package-lock.json"
50+
- "uv.lock"
51+
52+
"area: tests":
53+
- changed-files:
54+
- any-glob-to-any-file:
55+
- "tests/**"
56+
- "frontend/tests/**"

.github/workflows/auto-label.yml

Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
name: Auto Label
2+
3+
on:
4+
pull_request_target:
5+
types: [opened, synchronize, reopened, edited]
6+
issues:
7+
types: [opened, edited]
8+
workflow_dispatch:
9+
10+
permissions:
11+
contents: read
12+
issues: write
13+
pull-requests: write
14+
15+
jobs:
16+
ensure-labels:
17+
name: Ensure Labels Exist
18+
runs-on: ubuntu-latest
19+
20+
steps:
21+
- name: Create or update repository labels
22+
uses: actions/github-script@v7
23+
with:
24+
script: |
25+
const desiredLabels = [
26+
{
27+
name: "area: backend",
28+
color: "0e8a16",
29+
description: "Changes to the Python backend and scanning pipeline",
30+
},
31+
{
32+
name: "area: frontend",
33+
color: "1d76db",
34+
description: "Changes to the React frontend",
35+
},
36+
{
37+
name: "area: extension",
38+
color: "5319e7",
39+
description: "Changes to the browser extension package",
40+
},
41+
{
42+
name: "area: cli",
43+
color: "006b75",
44+
description: "Changes to the CLI package or entrypoints",
45+
},
46+
{
47+
name: "area: docs",
48+
color: "fbca04",
49+
description: "Documentation-only changes",
50+
},
51+
{
52+
name: "area: infra",
53+
color: "d4c5f9",
54+
description: "CI, deployment, database, or repository automation changes",
55+
},
56+
{
57+
name: "area: tests",
58+
color: "bfd4f2",
59+
description: "Changes to test coverage or test infrastructure",
60+
},
61+
{
62+
name: "kind: bug",
63+
color: "d73a4a",
64+
description: "Bug report or bug fix related work",
65+
},
66+
{
67+
name: "kind: feature",
68+
color: "a2eeef",
69+
description: "Feature request or feature implementation",
70+
},
71+
{
72+
name: "kind: documentation",
73+
color: "0075ca",
74+
description: "Documentation request or docs-focused work",
75+
},
76+
{
77+
name: "kind: question",
78+
color: "cc317c",
79+
description: "Question, support, or clarification request",
80+
},
81+
{
82+
name: "kind: security",
83+
color: "b60205",
84+
description: "Security-sensitive issue or change",
85+
},
86+
];
87+
88+
const { owner, repo } = context.repo;
89+
const existingLabels = await github.paginate(
90+
github.rest.issues.listLabelsForRepo,
91+
{
92+
owner,
93+
repo,
94+
per_page: 100,
95+
},
96+
);
97+
const existingByName = new Map(
98+
existingLabels.map((label) => [label.name.toLowerCase(), label]),
99+
);
100+
101+
for (const desired of desiredLabels) {
102+
const current = existingByName.get(desired.name.toLowerCase());
103+
104+
if (!current) {
105+
await github.rest.issues.createLabel({
106+
owner,
107+
repo,
108+
...desired,
109+
});
110+
core.notice(`Created label: ${desired.name}`);
111+
continue;
112+
}
113+
114+
const currentDescription = current.description || "";
115+
const needsUpdate =
116+
current.color.toLowerCase() !== desired.color.toLowerCase() ||
117+
currentDescription !== desired.description;
118+
119+
if (needsUpdate) {
120+
await github.rest.issues.updateLabel({
121+
owner,
122+
repo,
123+
name: current.name,
124+
new_name: desired.name,
125+
color: desired.color,
126+
description: desired.description,
127+
});
128+
core.notice(`Updated label: ${desired.name}`);
129+
}
130+
}
131+
132+
pr-path-labels:
133+
name: Label Pull Requests
134+
if: github.event_name == 'pull_request_target'
135+
needs: ensure-labels
136+
runs-on: ubuntu-latest
137+
138+
steps:
139+
- name: Apply labels from changed paths
140+
uses: actions/labeler@v5
141+
with:
142+
repo-token: ${{ secrets.GITHUB_TOKEN }}
143+
configuration-path: .github/labeler.yml
144+
sync-labels: true
145+
146+
issue-keyword-labels:
147+
name: Label Issues
148+
if: github.event_name == 'issues'
149+
needs: ensure-labels
150+
runs-on: ubuntu-latest
151+
152+
steps:
153+
- name: Apply labels from issue keywords
154+
uses: actions/github-script@v7
155+
with:
156+
script: |
157+
const issue = context.payload.issue;
158+
const text = `${issue.title || ""}\n${issue.body || ""}`.toLowerCase();
159+
const rules = [
160+
{
161+
label: "kind: security",
162+
patterns: [
163+
/\bsecurity\b/,
164+
/\bvulnerability\b/,
165+
/\bxss\b/,
166+
/\bcsrf\b/,
167+
/\binjection\b/,
168+
/\bmalware\b/,
169+
],
170+
},
171+
{
172+
label: "kind: bug",
173+
patterns: [
174+
/\bbug\b/,
175+
/\bregression\b/,
176+
/\berror\b/,
177+
/\bcrash\b/,
178+
/\bbroken\b/,
179+
/\bfailing\b/,
180+
/\bfailed\b/,
181+
],
182+
},
183+
{
184+
label: "kind: documentation",
185+
patterns: [
186+
/\bdocs?\b/,
187+
/\breadme\b/,
188+
/\bdocumentation\b/,
189+
/\bguide\b/,
190+
],
191+
},
192+
{
193+
label: "kind: feature",
194+
patterns: [
195+
/\bfeature\b/,
196+
/\benhancement\b/,
197+
/\brequest\b/,
198+
/\bidea\b/,
199+
/\bproposal\b/,
200+
],
201+
},
202+
{
203+
label: "kind: question",
204+
patterns: [
205+
/\bquestion\b/,
206+
/\bhow do i\b/,
207+
/\bhow to\b/,
208+
/\bhelp\b/,
209+
],
210+
},
211+
];
212+
213+
const labelsToAdd = rules
214+
.filter((rule) => rule.patterns.some((pattern) => pattern.test(text)))
215+
.map((rule) => rule.label);
216+
217+
if (labelsToAdd.length === 0) {
218+
core.info("No issue keyword labels matched.");
219+
return;
220+
}
221+
222+
await github.rest.issues.addLabels({
223+
owner: context.repo.owner,
224+
repo: context.repo.repo,
225+
issue_number: issue.number,
226+
labels: labelsToAdd,
227+
});
228+
229+
core.notice(`Added labels: ${labelsToAdd.join(", ")}`);

0 commit comments

Comments
 (0)