@@ -20,61 +20,103 @@ jobs:
2020 pull-requests : write
2121
2222 steps :
23+ - name : Create organization membership token
24+ id : membership-token
25+ uses : actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
26+ with :
27+ client-id : ${{ vars.CODEQL_ORG_MEMBERS_APP_CLIENT_ID }}
28+ private-key : ${{ secrets.CODEQL_ORG_MEMBERS_APP_PRIVATE_KEY }}
29+ owner : ${{ github.repository_owner }}
30+ permission-members : read
31+
2332 - name : Label external contributions
33+ uses : actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
2434 env :
25- GH_TOKEN : ${{ github.token }}
26- REPO : ${{ github.repository }}
27- run : |
28- set -euo pipefail
35+ MEMBERS_TOKEN : ${{ steps.membership-token.outputs.token }}
36+ with :
37+ github-token : ${{ github.token }}
38+ retries : 3
39+ script : |
40+ const label = "external-contribution";
41+ const { owner, repo } = context.repo;
42+ const fullRepo = `${owner}/${repo}`;
43+ const since = new Date(Date.now() - 60 * 60 * 1000).toISOString();
44+ const membershipGithub = getOctokit(process.env.MEMBERS_TOKEN);
45+ let membershipCheckFailed = false;
46+
47+ const issues = await github.paginate("GET /repos/{owner}/{repo}/issues", {
48+ owner,
49+ repo,
50+ state: "open",
51+ since,
52+ sort: "updated",
53+ direction: "desc",
54+ per_page: 100,
55+ });
56+
57+ for (const issue of issues) {
58+ if (!issue.pull_request) {
59+ continue;
60+ }
61+
62+ const { data: pull } = await github.request(
63+ "GET /repos/{owner}/{repo}/pulls/{pull_number}",
64+ { owner, repo, pull_number: issue.number },
65+ );
2966
30- label="external-contribution"
31- updated_cutoff=$(date -u -d "1 hour ago" "+%Y-%m-%dT%H:%M:%SZ")
67+ if (
68+ pull.state !== "open" ||
69+ pull.draft ||
70+ pull.base.repo.full_name !== fullRepo ||
71+ !pull.head.repo?.full_name ||
72+ pull.head.repo.full_name === fullRepo ||
73+ pull.user?.type !== "User" ||
74+ !pull.user.login ||
75+ pull.labels.some(({ name }) => name === label)
76+ ) {
77+ continue;
78+ }
3279
33- while IFS= read -r pr_number; do
34- if [[ ! "$pr_number" =~ ^[1-9][0-9]*$ ]]; then
35- echo "Skipping malformed pull request number."
36- continue
37- fi
80+ const events = await github.paginate(
81+ "GET /repos/{owner}/{repo}/issues/{issue_number}/events",
82+ { owner, repo, issue_number: issue.number, per_page: 100 },
83+ );
3884
39- pr_json=$(gh api "repos/$REPO/pulls/$pr_number")
40- if ! jq -e \
41- --arg repo "$REPO" \
42- --arg label "$label" \
43- '.state == "open" and
44- .draft == false and
45- .base.repo.full_name == $repo and
46- (.head.repo.full_name | type == "string") and
47- .head.repo.full_name != $repo and
48- .user.type == "User" and
49- .author_association != "MEMBER" and
50- .author_association != "OWNER" and
51- (any(.labels[]?; .name == $label) | not)' \
52- >/dev/null <<<"$pr_json"; then
53- continue
54- fi
85+ if (
86+ events.some(
87+ (event) =>
88+ event.event === "labeled" && event.label?.name === label,
89+ )
90+ ) {
91+ continue;
92+ }
5593
56- events=$(gh api --paginate \
57- "repos/$REPO/issues/$pr_number/events?per_page=100" |
58- jq -cs 'add')
94+ try {
95+ await membershipGithub.request(
96+ "GET /orgs/{org}/members/{username}",
97+ { org: owner, username: pull.user.login },
98+ );
99+ core.info(
100+ `Pull request #${issue.number} was opened by an organization member; skipping.`,
101+ );
102+ continue;
103+ } catch (error) {
104+ if (error.status !== 404) {
105+ core.error(
106+ `Membership check failed for pull request #${issue.number}: ${error.message}`,
107+ );
108+ membershipCheckFailed = true;
109+ continue;
110+ }
111+ }
59112
60- if jq -e --arg label "$label" \
61- 'any(.[]; .event == "labeled" and .label.name == $label)' \
62- >/dev/null <<<"$events"; then
63- continue
64- fi
113+ await github.request(
114+ "POST /repos/{owner}/{repo}/issues/{issue_number}/labels",
115+ { owner, repo, issue_number: issue.number, labels: [label] },
116+ );
117+ core.info(`Labelled pull request #${issue.number}.`);
118+ }
65119
66- jq -n --arg label "$label" '{labels: [$label]}' |
67- gh api --method POST \
68- "repos/$REPO/issues/$pr_number/labels" \
69- --input - \
70- >/dev/null
71- echo "Labelled pull request #$pr_number."
72- done < <(
73- gh api --method GET --paginate "repos/$REPO/issues" \
74- -f state=open \
75- -f since="$updated_cutoff" \
76- -f sort=updated \
77- -f direction=desc \
78- -f per_page=100 |
79- jq -r '.[] | select(.pull_request != null) | .number'
80- )
120+ if (membershipCheckFailed) {
121+ core.setFailed("One or more organization membership checks failed.");
122+ }
0 commit comments