Skip to content

Commit 4b5c155

Browse files
committed
2 parents c4399db + 27a9e20 commit 4b5c155

1 file changed

Lines changed: 164 additions & 0 deletions

File tree

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
name: Sponsor Priority Label
2+
3+
on:
4+
issues:
5+
types: [opened, reopened]
6+
workflow_dispatch:
7+
inputs:
8+
issue_number:
9+
description: "Issue number to evaluate (manual dry-run)"
10+
required: true
11+
type: number
12+
dry_run:
13+
description: "If true, log the decision without applying the label"
14+
required: false
15+
default: true
16+
type: boolean
17+
18+
permissions:
19+
contents: read
20+
issues: write
21+
22+
jobs:
23+
label-sponsor-issues:
24+
runs-on: ubuntu-latest
25+
steps:
26+
- name: Evaluate sponsorship and label
27+
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
28+
env:
29+
SPONSORABLE_LOGIN: ArcadeData
30+
LABEL_NAME: high_priority
31+
# 6-char hex without the leading '#'; that's the form the REST labels API expects.
32+
LABEL_COLOR: B60205
33+
LABEL_DESCRIPTION: Issue opened by a GitHub Sponsor of ArcadeData
34+
with:
35+
script: |
36+
const sponsorable = process.env.SPONSORABLE_LOGIN;
37+
const labelName = process.env.LABEL_NAME;
38+
const labelColor = process.env.LABEL_COLOR;
39+
const labelDescription = process.env.LABEL_DESCRIPTION;
40+
41+
// Resolve the target issue. For issues events, use the payload directly.
42+
// For workflow_dispatch, fetch the issue specified by the input.
43+
let issue;
44+
let dryRun = false;
45+
if (context.eventName === "workflow_dispatch") {
46+
dryRun = context.payload.inputs.dry_run !== "false";
47+
const issueNumber = Number(context.payload.inputs.issue_number);
48+
try {
49+
const { data } = await github.rest.issues.get({
50+
owner: context.repo.owner,
51+
repo: context.repo.repo,
52+
issue_number: issueNumber,
53+
});
54+
issue = data;
55+
} catch (err) {
56+
// audit() needs the issue user/association; on dispatch fetch failure we don't
57+
// have those yet, so emit a substitute info line keyed by issue_number instead.
58+
core.warning(`sponsor-priority: dispatch issues.get failed: ${err.message}`);
59+
core.info(
60+
`sponsor-priority: issue_number=${context.payload.inputs.issue_number}, ` +
61+
`isSponsor=unknown, action=errored` +
62+
(dryRun ? " (dry_run)" : "")
63+
);
64+
return;
65+
}
66+
} else {
67+
issue = context.payload.issue;
68+
}
69+
70+
const login = issue.user.login;
71+
const userType = issue.user.type;
72+
const association = issue.author_association;
73+
74+
const audit = (isSponsor, action) => {
75+
core.info(
76+
`sponsor-priority: user=${login}, association=${association}, ` +
77+
`isSponsor=${isSponsor}, action=${action}` +
78+
(dryRun ? " (dry_run)" : "")
79+
);
80+
};
81+
82+
// Skip bots and internal contributors before any external call.
83+
const isBot = userType === "Bot" || login.endsWith("[bot]");
84+
const isInternal =
85+
association === "OWNER" ||
86+
association === "MEMBER" ||
87+
association === "COLLABORATOR";
88+
if (isBot || isInternal) {
89+
audit("n/a", "skipped");
90+
return;
91+
}
92+
93+
// Probe sponsorship.
94+
let isSponsor = null;
95+
try {
96+
const result = await github.graphql(
97+
`query($login: String!, $sponsorable: String!) {
98+
organization(login: $sponsorable) {
99+
isSponsoredBy(accountLogin: $login)
100+
}
101+
}`,
102+
{ login, sponsorable }
103+
);
104+
isSponsor = Boolean(result?.organization?.isSponsoredBy);
105+
} catch (err) {
106+
core.warning(`sponsor-priority: graphql failed: ${err.message}`);
107+
audit("unknown", "errored");
108+
return;
109+
}
110+
111+
if (!isSponsor) {
112+
audit(false, "skipped");
113+
return;
114+
}
115+
116+
if (dryRun) {
117+
audit(true, "dry_run");
118+
return;
119+
}
120+
121+
// Ensure the label exists, then apply it.
122+
try {
123+
await github.rest.issues.getLabel({
124+
owner: context.repo.owner,
125+
repo: context.repo.repo,
126+
name: labelName,
127+
});
128+
} catch (err) {
129+
if (err.status === 404) {
130+
try {
131+
await github.rest.issues.createLabel({
132+
owner: context.repo.owner,
133+
repo: context.repo.repo,
134+
name: labelName,
135+
color: labelColor,
136+
description: labelDescription,
137+
});
138+
} catch (createErr) {
139+
// 422 = lost the race to a concurrent run; the label now exists, fall through.
140+
if (createErr.status !== 422) {
141+
core.warning(`sponsor-priority: createLabel failed: ${createErr.message}`);
142+
audit(true, "errored");
143+
return;
144+
}
145+
}
146+
} else {
147+
core.warning(`sponsor-priority: getLabel failed: ${err.message}`);
148+
audit(true, "errored");
149+
return;
150+
}
151+
}
152+
153+
try {
154+
await github.rest.issues.addLabels({
155+
owner: context.repo.owner,
156+
repo: context.repo.repo,
157+
issue_number: issue.number,
158+
labels: [labelName],
159+
});
160+
audit(true, "labeled");
161+
} catch (err) {
162+
core.warning(`sponsor-priority: addLabels failed: ${err.message}`);
163+
audit(true, "errored");
164+
}

0 commit comments

Comments
 (0)