Skip to content

Commit 9dd5e06

Browse files
authored
Add workflow to welcome first-time discussion authors
1 parent fbd4e83 commit 9dd5e06

1 file changed

Lines changed: 170 additions & 0 deletions

File tree

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
name: Welcome & Label First-Time Discussion Author
2+
3+
on:
4+
discussion:
5+
types: [created]
6+
7+
permissions:
8+
contents: read
9+
discussions: write
10+
issues: write
11+
12+
jobs:
13+
label_welcome_discussion:
14+
runs-on: ubuntu-latest
15+
steps:
16+
- uses: actions/checkout@v4
17+
18+
- uses: actions/setup-python@v5
19+
with:
20+
python-version: "3.11"
21+
22+
- name: Check if GitHub employee (member of org "github")
23+
id: check_employee
24+
uses: actions/github-script@v7
25+
with:
26+
github-token: ${{ secrets.READ_GITHUB_ORG_MEMBERS_TOKEN }}
27+
result-encoding: string
28+
script: |
29+
const username = (context.payload.discussion?.user?.login || "").trim();
30+
31+
if (!username) {
32+
console.log("No username found in discussion payload; treating as not-employee.");
33+
return "false";
34+
}
35+
36+
try {
37+
const response = await github.rest.orgs.checkMembershipForUser({
38+
org: "github",
39+
username
40+
});
41+
42+
if (response.status === 204) {
43+
console.log(`'${username}' IS a member of org 'github' (treat as employee).`);
44+
return "true";
45+
}
46+
47+
console.log(`Unexpected status ${response.status}; treating as not-employee.`);
48+
return "false";
49+
} catch (error) {
50+
if (error.status === 404) {
51+
console.log(`'${username}' is NOT a member of org 'github'.`);
52+
return "false";
53+
}
54+
55+
console.log("Employee check failed; treating as not-employee.");
56+
return "false";
57+
}
58+
59+
- name: Check first-time status (Python)
60+
id: first_time
61+
if: steps.check_employee.outputs.result != 'true'
62+
shell: bash
63+
env:
64+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
65+
USERNAME: ${{ github.event.discussion.user.login }}
66+
OWNER: ${{ github.repository_owner }}
67+
REPO: ${{ github.event.repository.name }}
68+
CURRENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }}
69+
run: |
70+
set -euo pipefail
71+
python .github/workflows/scripts/first_time_discussion_author_live.py
72+
73+
- name: Resolve Discussion ID + Label ID ("Welcome 🎉")
74+
id: ids
75+
if: steps.check_employee.outputs.result != 'true' && steps.first_time.outputs.should_welcome == 'true'
76+
uses: actions/github-script@v7
77+
env:
78+
LABEL_NAME: "Welcome 🎉"
79+
with:
80+
github-token: ${{ secrets.GITHUB_TOKEN }}
81+
result-encoding: string
82+
script: |
83+
const owner = context.repo.owner;
84+
const repo = context.repo.repo;
85+
86+
const discussionNumber = context.payload?.discussion?.number;
87+
if (!discussionNumber) {
88+
core.setFailed("Missing discussion number in event payload.");
89+
return "";
90+
}
91+
92+
const labelName = process.env.LABEL_NAME;
93+
94+
// 1) Get Discussion node ID
95+
const discussionResp = await github.graphql(
96+
`
97+
query($owner: String!, $repo: String!, $number: Int!) {
98+
repository(owner: $owner, name: $repo) {
99+
discussion(number: $number) { id number url title }
100+
}
101+
}
102+
`,
103+
{ owner, repo, number: discussionNumber }
104+
);
105+
106+
const discussionNode = discussionResp?.repository?.discussion;
107+
if (!discussionNode?.id) {
108+
throw new Error(`Unable to resolve discussion node id for #${discussionNumber}`);
109+
}
110+
111+
// 2) Paginate labels to find the correct label node
112+
let cursor = null;
113+
let foundLabel = null;
114+
115+
do {
116+
const labelsResp = await github.graphql(
117+
`
118+
query($owner: String!, $repo: String!, $after: String) {
119+
repository(owner: $owner, name: $repo) {
120+
labels(first: 100, after: $after) {
121+
pageInfo { hasNextPage endCursor }
122+
nodes { name id }
123+
}
124+
}
125+
}
126+
`,
127+
{ owner, repo, after: cursor }
128+
);
129+
130+
const labelsConnection = labelsResp?.repository?.labels;
131+
const labels = labelsConnection?.nodes || [];
132+
133+
foundLabel = labels.find(l => l?.name === labelName);
134+
135+
const pageInfo = labelsConnection?.pageInfo;
136+
cursor = pageInfo?.hasNextPage ? pageInfo.endCursor : null;
137+
} while (!foundLabel && cursor);
138+
139+
if (!foundLabel) {
140+
throw new Error(`Label "${labelName}" not found in ${owner}/${repo} (checked all pages)`);
141+
}
142+
143+
core.setOutput("discussion_id", discussionNode.id);
144+
core.setOutput("label_id", foundLabel.id);
145+
146+
return "ok";
147+
148+
- name: Apply label via GraphQL mutation
149+
if: steps.check_employee.outputs.result != 'true' && steps.first_time.outputs.should_welcome == 'true'
150+
env:
151+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
152+
shell: bash
153+
run: |
154+
set -euo pipefail
155+
echo "Applying label via GraphQL mutation..."
156+
gh api graphql -f query='
157+
mutation($labelableId:ID!,$labelIds:[ID!]!) {
158+
addLabelsToLabelable(input:{labelableId:$labelableId,labelIds:$labelIds}) {
159+
labelable {
160+
... on Discussion {
161+
number
162+
title
163+
labels(first:10) { nodes { name } }
164+
}
165+
}
166+
}
167+
}
168+
' \
169+
-F labelableId='${{ steps.ids.outputs.discussion_id }}' \
170+
-F labelIds[]='${{ steps.ids.outputs.label_id }}'

0 commit comments

Comments
 (0)