Skip to content

Commit 8987c39

Browse files
Planeshifterkgryte
andauthored
build: add workflow and script to leave comment with make instructions
PR-URL: #5640 Closes: stdlib-js/metr-issue-tracker#9 Co-authored-by: Athan Reines <[email protected]> Reviewed-by: Athan Reines <[email protected]> Signed-off-by: Athan Reines <[email protected]>
1 parent c2d2e46 commit 8987c39

File tree

3 files changed

+347
-2
lines changed

3 files changed

+347
-2
lines changed

Diff for: .github/workflows/pr_commands_comment.yml

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
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: pr_commands_comment
21+
22+
# Workflow triggers:
23+
on:
24+
# Allow the workflow to be triggered by other workflows
25+
workflow_call:
26+
# Define the input parameters for the workflow:
27+
inputs:
28+
pull_request_number:
29+
description: 'Pull request number'
30+
required: true
31+
type: number
32+
33+
# Define the secrets accessible by the workflow:
34+
secrets:
35+
STDLIB_BOT_GITHUB_TOKEN:
36+
description: 'stdlib-bot GitHub token to create pull request comments'
37+
required: true
38+
39+
# Allow the workflow to be manually triggered:
40+
workflow_dispatch:
41+
inputs:
42+
pull_request_number:
43+
description: 'Pull request number'
44+
required: true
45+
type: number
46+
47+
# Global permissions:
48+
permissions:
49+
# Allow read-only access to the repository contents:
50+
contents: read
51+
52+
# Workflow jobs:
53+
jobs:
54+
55+
# Define a job for leaving a comment on a PR with package make command instructions:
56+
pr_commands_comment:
57+
58+
# Define a display name:
59+
name: 'Leave comment with package make command instructions'
60+
61+
# Define the type of virtual host machine:
62+
runs-on: ubuntu-latest
63+
64+
# Define the sequence of job steps...
65+
steps:
66+
67+
# Leave comment with package make command instructions:
68+
- name: 'Leave comment with package make command instructions'
69+
env:
70+
PR_NUMBER: ${{ inputs.pull_request_number }}
71+
run: |
72+
. "$GITHUB_WORKSPACE/.github/workflows/scripts/package_commands_comment" "$PR_NUMBER"
73+
timeout-minutes: 30

Diff for: .github/workflows/scripts/package_commands_comment

+253
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
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 post a comment with relevant `make` commands tailored to packages changed in a PR.
20+
#
21+
# Usage: package_commands_comment <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+
# Resolve 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+
48+
# FUNCTIONS #
49+
50+
# Error handler.
51+
#
52+
# $1 - error status
53+
on_error() {
54+
echo 'ERROR: An error was encountered during execution.' >&2
55+
exit "$1"
56+
}
57+
58+
# Prints a success message.
59+
print_success() {
60+
echo 'Success!' >&2
61+
}
62+
63+
# Performs a GitHub API request.
64+
#
65+
# $1 - HTTP method (GET or POST)
66+
# $2 - API endpoint
67+
# $3 - data for POST requests
68+
github_api() {
69+
local method="$1"
70+
local endpoint="$2"
71+
local data="$3"
72+
73+
# Initialize an array to hold curl headers:
74+
local headers=()
75+
76+
# If GITHUB_TOKEN is set, add the Authorization header:
77+
if [ -n "${GITHUB_TOKEN}" ]; then
78+
headers+=("-H" "Authorization: token ${GITHUB_TOKEN}")
79+
fi
80+
81+
# Determine the HTTP method and construct the curl command accordingly...
82+
case "${method}" in
83+
GET)
84+
curl -s "${headers[@]}" "${github_api_url}${endpoint}"
85+
;;
86+
POST)
87+
# For POST requests, always set the Content-Type header:
88+
headers+=("-H" "Content-Type: application/json")
89+
90+
# If data is provided, include it in the request:
91+
if [ -n "${data}" ]; then
92+
curl -s -X POST "${headers[@]}" -d "${data}" "${github_api_url}${endpoint}"
93+
else
94+
# Handle cases where POST data is required but not provided:
95+
echo "ERROR: POST request requires data."
96+
on_error 1
97+
fi
98+
;;
99+
*)
100+
echo "ERROR: Invalid HTTP method: ${method}."
101+
on_error 1
102+
;;
103+
esac
104+
}
105+
106+
# Main execution sequence.
107+
main() {
108+
local directories
109+
local packages
110+
local response
111+
local c_files
112+
local comment
113+
local files
114+
115+
if [ -z "${pr_number}" ]; then
116+
echo "ERROR: Pull request number is required." >&2
117+
on_error 1
118+
fi
119+
120+
# Fetch changed files in pull request:
121+
response=$(github_api "GET" "/repos/${repo_owner}/${repo_name}/pulls/${pr_number}/files?per_page=100")
122+
files=$(echo "${response}" | jq -r '.[] | .filename')
123+
124+
# Extract files associated with native add-ons:
125+
c_files=$(echo "${files}" | grep -e '/benchmark/c' -e '/examples/c' -e '/binding.gyp' -e '/include.gypi' -e '/src/')
126+
127+
# Find unique package directories:
128+
directories=$(echo "${files}" | tr ' ' '\n' | \
129+
xargs dirname | \
130+
grep '^lib/node_modules/@stdlib' | \
131+
sed -E 's/\/(benchmark|bin|data|docs|etc|examples|include|lib|scripts|src|test)(\/.*)?$//' | \
132+
sort -u)
133+
134+
# Extract package names from changed package directories (e.g., @stdlib/math/base/special/sin) by removing the leading 'lib/node_modules/':
135+
packages=$(echo "${directories}" | sed -E 's/^lib\/node_modules\///')
136+
137+
# Documentation links:
138+
docs_links="
139+
## Documentation Links
140+
141+
- [make rules for running examples][make-docs-examples]
142+
- [make rules for running unit tests][make-docs-test]
143+
- [make rules for running benchmarks][make-docs-benchmark]
144+
145+
[make-docs-examples]: https://github.com/stdlib-js/stdlib/blob/develop/tools/make/lib/examples/README.md
146+
[make-docs-test]: https://github.com/stdlib-js/stdlib/blob/develop/tools/make/lib/test/README.md
147+
[make-docs-benchmark]: https://github.com/stdlib-js/stdlib/blob/develop/tools/make/lib/benchmark/README.md"
148+
149+
# Count the number of packages:
150+
package_count=$(echo "${packages}" | wc -l)
151+
152+
if [[ $package_count -gt 1 ]]; then
153+
# Multiple packages case:
154+
comment="Hello! 👋
155+
156+
Pro-tip: This PR changes multiple packages. You can use various \`make\` rules with \`*_FILTER\` environment variables to run tests, benchmarks, and examples for specific packages.
157+
158+
For each of the commands, please run them from the root stdlib repository directory (not the package folder!).
159+
160+
You can use pattern matching to target specific packages as follows:
161+
162+
\`\`\`bash
163+
# Run tests for all packages in the math namespace:
164+
make test TESTS_FILTER=\".*/@stdlib/math/.*\"
165+
166+
# Run benchmarks for specific packages (using OR pattern):
167+
make benchmark BENCHMARKS_FILTER=\".*/@stdlib/math/base/special/(sin|cos)/.*\"
168+
\`\`\`
169+
170+
## Changed Packages
171+
172+
$(echo "${packages}" | sed 's/^/- `/' | sed 's/$/`/')
173+
${docs_links}"
174+
175+
else
176+
# Single package case:
177+
if [[ "${#c_files[@]}" -eq 0 ]]; then
178+
comment="Hello! 👋
179+
180+
Pro-tip: Use the \`make\` commands below during local development to ensure that all tests, examples, and benchmark files in your PR run successfully.
181+
182+
For each of the commands, please run them from the root stdlib repository directory (not the package folder!).
183+
184+
To run unit tests,
185+
186+
\`\`\`bash
187+
make test TESTS_FILTER=\".*/${packages}/.*\"
188+
\`\`\`
189+
190+
To run benchmarks,
191+
192+
\`\`\`bash
193+
make benchmark BENCHMARKS_FILTER=\".*/${packages}/.*\"
194+
\`\`\`
195+
196+
To run examples,
197+
198+
\`\`\`bash
199+
make examples EXAMPLES_FILTER=\".*/${packages}/.*\"
200+
\`\`\`
201+
${docs_links}"
202+
else
203+
comment="Hello! 👋
204+
205+
Pro-tip: Use the \`make\` below commands during local development to ensure that all tests, examples, and benchmark files in your PR run successfully.
206+
207+
For each of the commands, please run them from the root stdlib repository directory (not the package folder!).
208+
209+
To build a native add-on,
210+
211+
\`\`\`bash
212+
NODE_ADDONS_PATTERN=\"${packages}\" make install-node-addons
213+
\`\`\`
214+
215+
To run unit tests,
216+
217+
\`\`\`bash
218+
make test TESTS_FILTER=\".*/${packages}/.*\"
219+
\`\`\`
220+
221+
To run benchmarks,
222+
223+
\`\`\`bash
224+
make benchmark BENCHMARKS_FILTER=\".*/${packages}/.*\"
225+
\`\`\`
226+
227+
\`\`\`bash
228+
make benchmark-c BENCHMARKS_FILTER=\".*/${packages}/.*\"
229+
\`\`\`
230+
231+
To run examples,
232+
233+
\`\`\`bash
234+
make examples EXAMPLES_FILTER=\".*/${packages}/.*\"
235+
\`\`\`
236+
237+
\`\`\`bash
238+
make examples-c EXAMPLES_FILTER=\".*/${packages}/.*\"
239+
\`\`\`
240+
${docs_links}"
241+
fi
242+
fi
243+
244+
if ! github_api "POST" "/repos/${repo_owner}/${repo_name}/issues/${pr_number}/comments" "{\"body\":$(echo "${comment}" | jq -R -s -c .)}"; then
245+
echo "Failed to post comment on PR."
246+
on_error 1
247+
fi
248+
249+
print_success
250+
exit 0
251+
}
252+
253+
main

Diff for: .github/workflows/slash_commands.yml

+21-2
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ jobs:
6464
github-token: ${{ secrets.STDLIB_BOT_PAT_REPO_WRITE }}
6565
script: |
6666
const commentBody = context.payload.comment.body.trim();
67-
const RE_COMMANDS = /^\/stdlib\s+(help|check-files|update-copyright-years|lint-autofix|merge|rebase)$/i;
67+
const RE_COMMANDS = /^\/stdlib\s+(help|check-files|update-copyright-years|lint-autofix|merge|rebase|make-commands)$/i;
6868
const isRecognizedCommand = RE_COMMANDS.test( commentBody );
6969
7070
if ( isRecognizedCommand ) {
@@ -107,6 +107,24 @@ jobs:
107107
secrets:
108108
STDLIB_BOT_GITHUB_TOKEN: ${{ secrets.STDLIB_BOT_PAT_REPO_WRITE }}
109109

110+
# Define a command for leaving a comment with make command instructions:
111+
make-commands:
112+
# Define a display name:
113+
name: 'Post a comment with make command instructions'
114+
115+
# Ensure initial reaction job has completed before running this job:
116+
needs: [ add_initial_reaction ]
117+
118+
# Define the conditions under which the job should run:
119+
if: github.event.issue.pull_request && startsWith(github.event.comment.body, '/stdlib make-commands')
120+
121+
# Run reusable workflow:
122+
uses: ./.github/workflows/pr_commands_comment.yml
123+
with:
124+
pull_request_number: ${{ github.event.issue.number }}
125+
secrets:
126+
STDLIB_BOT_GITHUB_TOKEN: ${{ secrets.STDLIB_BOT_PAT_REPO_WRITE }}
127+
110128
# Define a job for updating copyright header years:
111129
update_copyright_years:
112130

@@ -218,6 +236,7 @@ jobs:
218236
@${{ github.event.comment.user.login }}, available slash commands include:
219237
220238
- `/stdlib check-files` - Check for required files.
239+
- `/stdlib make-commands` - Print `make` commands for package changed in PR.
221240
- `/stdlib update-copyright-years` - Update copyright header years.
222241
- `/stdlib lint-autofix` - Auto-fix lint errors.
223242
- `/stdlib merge` - Merge changes from develop branch into this PR.
@@ -236,7 +255,7 @@ jobs:
236255
runs-on: ubuntu-latest
237256

238257
# Ensure all previous jobs have completed before running this job:
239-
needs: [ add_initial_reaction, check_files, update_copyright_years, fix_lint_errors, merge_develop, rebase_develop, help ]
258+
needs: [ add_initial_reaction, check_files, make-commands, update_copyright_years, fix_lint_errors, merge_develop, rebase_develop, help ]
240259

241260
# Define the conditions under which the job should run:
242261
if: |

0 commit comments

Comments
 (0)