Skip to content

Commit dba2065

Browse files
authored
build: add workflow to label applicable PRs as good first PRs
PR-URL: #5470 Closes: stdlib-js/metr-issue-tracker#44 Reviewed-by: Athan Reines <[email protected]>
1 parent b6624d0 commit dba2065

File tree

2 files changed

+222
-0
lines changed

2 files changed

+222
-0
lines changed
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
#/
2+
# @license Apache-2.0
3+
#
4+
# Copyright (c) 2025 The Stdlib Authors.
5+
#
6+
# Licensed under the Apache License, Version 2.0 (the "License");
7+
# you may not use this file except in compliance with the License.
8+
# You may obtain a copy of the License at
9+
#
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing, software
13+
# distributed under the License is distributed on an "AS IS" BASIS,
14+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
# See the License for the specific language governing permissions and
16+
# limitations under the License.
17+
#/
18+
19+
# Workflow name:
20+
name: label_good_first_prs
21+
22+
# Workflow triggers:
23+
on:
24+
pull_request_target:
25+
types:
26+
- opened
27+
- closed
28+
- synchronize
29+
- reopened
30+
- edited
31+
32+
# Workflow jobs:
33+
jobs:
34+
35+
# Define a job which automatically labels pull requests based on whether they reference good first issues
36+
labeler:
37+
38+
# Define job name:
39+
name: 'Label PRs for issues with label "Good First Issue" as "Good First PR"s'
40+
41+
# Only run this job if the pull request did not have label `automated-pr`:
42+
if: contains(github.event.pull_request.labels.*.name, 'automated-pr') == false
43+
44+
# Define job permissions:
45+
permissions:
46+
contents: read
47+
pull-requests: write
48+
49+
# Define the type of virtual host machine:
50+
runs-on: ubuntu-latest
51+
52+
# Define the sequence of job steps:
53+
steps:
54+
# Check whether any of the referenced issues is a "Good First Issue":
55+
- name: 'Check whether any of the referenced issues is a "Good First Issue"'
56+
id: 'check-pr'
57+
env:
58+
PR_NUMBER: ${{ github.event.pull_request.number }}
59+
run: |
60+
bool=$(. "$GITHUB_WORKSPACE/.github/workflows/scripts/references_good_first_issue $PR_NUMBER)
61+
echo "good-first-pr=$bool" >> $GITHUB_OUTPUT
62+
63+
# Add "Good First PR" label if PR references a "Good First Issue"
64+
- name: 'Add "Good First PR" label if PR references a "Good First Issue"'
65+
if: ${{ steps.check-pr.outputs.good-first-pr == 'true' }}
66+
# Pin action to full-length commit SHA
67+
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
68+
with:
69+
github-token: ${{ secrets.STDLIB_BOT_PAT_REPO_WRITE }}
70+
script: |
71+
const { data: pr } = await github.rest.pulls.get({
72+
'owner': context.repo.owner,
73+
'repo': context.repo.repo,
74+
'pull_number': context.payload.pull_request.number
75+
});
76+
const labels = context.payload.pull_request.labels.map( label => label.name );
77+
if ( !labels.includes( 'Good First PR' ) ) {
78+
await github.rest.issues.addLabels({
79+
'owner': context.repo.owner,
80+
'repo': context.repo.repo,
81+
'issue_number': context.payload.pull_request.number,
82+
'labels': [ 'Good First PR' ]
83+
});
84+
}
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
#!/usr/bin/env bash
2+
#
3+
# @license Apache-2.0
4+
#
5+
# Copyright (c) 2025 The Stdlib Authors.
6+
#
7+
# Licensed under the Apache License, Version 2.0 (the "License");
8+
# you may not use this file except in compliance with the License.
9+
# You may obtain a copy of the License at
10+
#
11+
# http://www.apache.org/licenses/LICENSE-2.0
12+
#
13+
# Unless required by applicable law or agreed to in writing, software
14+
# distributed under the License is distributed on an "AS IS" BASIS,
15+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
# See the License for the specific language governing permissions and
17+
# limitations under the License.
18+
19+
# Script to check whether a PR references an issue with label "Good First Issue".
20+
#
21+
# Usage: references_good_first_issue PR_NUMBER
22+
#
23+
# Arguments:
24+
#
25+
# PR_NUMBER Pull request number.
26+
#
27+
# Environment variables:
28+
#
29+
# GITHUB_TOKEN GitHub token for authentication.
30+
31+
# Ensure that the exit status of pipelines is non-zero in the event that at least one of the commands in a pipeline fails:
32+
set -o pipefail
33+
34+
35+
# VARIABLES #
36+
37+
# Get the pull request number:
38+
pr_number="$1"
39+
40+
# GitHub API base URL:
41+
GITHUB_API_URL="https://api.github.com"
42+
43+
# Repository owner and name:
44+
REPO_OWNER="stdlib-js"
45+
REPO_NAME="stdlib"
46+
47+
# Exit codes:
48+
SUCCESS=0
49+
ERROR=1
50+
51+
52+
# FUNCTIONS #
53+
54+
# Error handler.
55+
#
56+
# $1 - error status
57+
on_error() {
58+
echo 'ERROR: An error was encountered during execution.' >&2
59+
exit "$1"
60+
}
61+
62+
# Makes GitHub API requests.
63+
#
64+
# $1 - HTTP method (GET or POST)
65+
# $2 - API endpoint
66+
# $3 - data for POST requests
67+
github_api() {
68+
local method="$1"
69+
local endpoint="$2"
70+
local data="$3"
71+
72+
# Initialize an array to hold curl headers:
73+
local headers=()
74+
75+
# If GITHUB_TOKEN is set, add the Authorization header:
76+
if [ -n "$GITHUB_TOKEN" ]; then
77+
headers+=("-H" "Authorization: token $GITHUB_TOKEN")
78+
fi
79+
80+
# Determine the HTTP method and construct the curl command accordingly...
81+
case "$method" in
82+
GET)
83+
curl -s "${headers[@]}" "$GITHUB_API_URL$endpoint"
84+
;;
85+
POST)
86+
# For POST requests, always set the Content-Type header:
87+
headers+=("-H" "Content-Type: application/json")
88+
89+
# If data is provided, include it in the request:
90+
if [ -n "$data" ]; then
91+
curl -s -X POST "${headers[@]}" -d "$data" "$GITHUB_API_URL$endpoint"
92+
else
93+
# Handle cases where POST data is required but not provided:
94+
echo "POST request requires data."
95+
on_error $ERROR
96+
fi
97+
;;
98+
*)
99+
echo "Invalid HTTP method: $method"
100+
on_error $ERROR
101+
;;
102+
esac
103+
}
104+
105+
# Main execution sequence.
106+
main() {
107+
if [ -z "$pr_number" ]; then
108+
echo "ERROR: Pull request number is required" >&2
109+
on_error $ERROR
110+
fi
111+
112+
# Fetch pull request details:
113+
pr_details=$(github_api "GET" "/repos/$REPO_OWNER/$REPO_NAME/pulls/$pr_number")
114+
pr_title=$(echo "$pr_details" | jq -r '.title')
115+
pr_body=$(echo "$pr_details" | jq -r '.body')
116+
117+
issue_numbers=$(echo $pr_body | grep -Ei '#[0-9]+' | grep -oEi '[0-9]+' | sort | uniq)
118+
if [ -z "$issue_numbers" ]; then
119+
echo 'false'
120+
exit $SUCCESS
121+
fi
122+
123+
for issue in $issue_numbers; do
124+
issue_details=$(github_api "GET" "/repos/$REPO_OWNER/$REPO_NAME/issues/$issue")
125+
126+
bool=$(echo $issue_details | jq '.labels | any(.name == "Good First Issue")')
127+
if [ "$bool" == 'true' ]; then
128+
echo 'true'
129+
exit $SUCCESS
130+
fi
131+
done
132+
133+
echo 'false'
134+
exit $SUCCESS
135+
}
136+
137+
# Call main with all command-line arguments:
138+
main "$@"

0 commit comments

Comments
 (0)