Skip to content

Commit 2eaf06c

Browse files
committed
build: move benchmark execution to script and build native add-ons
--- type: pre_commit_static_analysis_report description: Results of running static analysis checks when committing changes. report: - task: lint_filenames status: passed - task: lint_editorconfig status: passed - task: lint_markdown status: na - task: lint_package_json status: na - task: lint_repl_help status: na - task: lint_javascript_src status: na - task: lint_javascript_cli status: na - task: lint_javascript_examples status: na - task: lint_javascript_tests status: na - task: lint_javascript_benchmarks status: na - task: lint_python status: na - task: lint_r status: na - task: lint_c_src status: na - task: lint_c_examples status: na - task: lint_c_benchmarks status: na - task: lint_c_tests_fixtures status: na - task: lint_shell status: passed - task: lint_typescript_declarations status: na - task: lint_typescript_tests status: na - task: lint_license_headers status: passed --- --- type: pre_push_report description: Results of running various checks prior to pushing changes. report: - task: run_javascript_examples status: na - task: run_c_examples status: na - task: run_cpp_examples status: na - task: run_javascript_readme_examples status: na - task: run_c_benchmarks status: na - task: run_cpp_benchmarks status: na - task: run_fortran_benchmarks status: na - task: run_javascript_benchmarks status: na - task: run_julia_benchmarks status: na - task: run_python_benchmarks status: na - task: run_r_benchmarks status: na - task: run_javascript_tests status: na ---
1 parent 895313e commit 2eaf06c

File tree

2 files changed

+188
-19
lines changed

2 files changed

+188
-19
lines changed

.github/workflows/run_affected_benchmarks.yml

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ on:
3535
# List paths for which changes should trigger this workflow:
3636
- 'lib/**/benchmark/**'
3737

38+
workflow_dispatch:
39+
inputs:
40+
directories:
41+
description: 'List of changed directories for which to run affected benchmarks (space-separated)'
42+
3843
# Global permissions:
3944
permissions:
4045
# Allow read-only access to the repository contents:
@@ -103,9 +108,10 @@ jobs:
103108
make init
104109
timeout-minutes: 5
105110

106-
# Get list of changed files:
107-
- name: 'Get list of changed files'
108-
id: changed-files
111+
# Get list of changed directories from PR and push events:
112+
- name: 'Get list of changed directories'
113+
if: github.event_name != 'workflow_dispatch'
114+
id: changed-directories
109115
continue-on-error: true
110116
run: |
111117
if [ -n "${{ github.event.pull_request.number }}" ]; then
@@ -120,24 +126,24 @@ jobs:
120126
files=$(git diff --diff-filter=AM --name-only ${{ github.event.before }} ${{ github.event.after }})
121127
fi
122128
fi
123-
# Keep only benchmark files:
124-
files=$(echo "$files" | grep -E 'benchmark/' | tr '\n' ' ' | sed 's/ $//')
125-
echo "files=${files}" >> $GITHUB_OUTPUT
129+
directories=$(for file in $files; do dirname $file; done | uniq | tr '\n' ' ' | sed 's/ $//')
130+
echo "directories=${directories}" >> $GITHUB_OUTPUT
126131
127-
# Run JavaScript benchmarks:
128-
- name: 'Run JavaScript benchmarks'
132+
# Get list of changed directories from workflow dispatch event:
133+
- name: 'Get list of changed directories (from user input)'
134+
if: github.event_name == 'workflow_dispatch'
135+
id: changed-directories-user-input
129136
run: |
130-
files=$(echo "${{ steps.changed-files.outputs.files }}" | tr ' ' '\n' | grep -E '\.js$' | tr '\n' ' ' | sed 's/ $//')
131-
if [ -n "$files" ]; then
132-
make benchmark-javascript-files FILES="${files}"
133-
fi
134-
timeout-minutes: 30
137+
echo "directories=${{ github.event.inputs.directories }}" >> $GITHUB_OUTPUT
138+
timeout-minutes: 5
135139

136-
# Run C benchmarks:
137-
- name: 'Run C benchmarks'
140+
# Run affected benchmarks:
141+
- name: 'Run affected benchmarks'
138142
run: |
139-
files=$(echo "${{ steps.changed-files.outputs.files }}" | tr ' ' '\n' | grep -E '\.c$' | sed "s|^|${GITHUB_WORKSPACE}/|" | tr '\n' ' ' | sed 's/ $//')
140-
if [ -n "$files" ]; then
141-
make benchmark-c-files FILES="${files}"
143+
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
144+
directories="${{ steps.changed-directories-user-input.outputs.directories }}"
145+
else
146+
directories="${{ steps.changed-directories.outputs.directories }}"
142147
fi
143-
timeout-minutes: 15
148+
. "$GITHUB_WORKSPACE/.github/workflows/scripts/run_affected_benchmarks" "$directories"
149+
timeout-minutes: 30
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
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 run affected benchmarks for a given list of changed paths.
20+
#
21+
# Usage: run_affected_benchmarks path1 [path2 path3 ...]
22+
#
23+
# Arguments:
24+
#
25+
# path1 File name or directory path.
26+
# path2 File name or directory path.
27+
# path3 File name or directory path.
28+
#
29+
#
30+
# Environment variables:
31+
#
32+
# LOG_FILE Log file.
33+
#
34+
35+
# shellcheck disable=SC2034,SC2153,SC2317
36+
37+
# Ensure that the exit status of pipelines is non-zero in the event that at least one of the commands in a pipeline fails:
38+
set -o pipefail
39+
40+
41+
# VARIABLES #
42+
43+
# Get the list of changed files:
44+
changed="$*"
45+
46+
# Get the path to a log file as the third argument to the build script:
47+
log_file="${LOG_FILE}"
48+
49+
# Define a heartbeat interval to periodically print messages in order to prevent CI from prematurely ending a build due to long-running commands:
50+
heartbeat_interval='30s'
51+
52+
# Declare a variable for storing the heartbeat process id:
53+
heartbeat_pid=""
54+
55+
56+
# FUNCTIONS #
57+
58+
# Error handler.
59+
#
60+
# $1 - error status
61+
on_error() {
62+
echo 'ERROR: An error was encountered during execution.' >&2
63+
cleanup
64+
exit "$1"
65+
}
66+
67+
# Runs clean-up tasks.
68+
cleanup() {
69+
stop_heartbeat
70+
}
71+
72+
# Starts a heartbeat.
73+
#
74+
# $1 - heartbeat interval
75+
start_heartbeat() {
76+
echo 'Starting heartbeat...' >&2
77+
78+
# Create a heartbeat and send to background:
79+
heartbeat "$1" &
80+
81+
# Capture the heartbeat pid:
82+
heartbeat_pid=$!
83+
echo "Heartbeat pid: ${heartbeat_pid}" >&2
84+
}
85+
86+
# Runs an infinite print loop.
87+
#
88+
# $1 - heartbeat interval
89+
heartbeat() {
90+
while true; do
91+
echo "$(date) - heartbeat..." >&2
92+
sleep "$1"
93+
done
94+
}
95+
96+
# Stops the heartbeat print loop.
97+
stop_heartbeat() {
98+
echo 'Stopping heartbeat...' >&2
99+
kill "${heartbeat_pid}"
100+
}
101+
102+
# Prints a success message.
103+
print_success() {
104+
echo 'Success!' >&2
105+
}
106+
107+
# Main execution sequence.
108+
main() {
109+
start_heartbeat "${heartbeat_interval}"
110+
111+
# Only keep files which reside in package directories:
112+
changed=$(echo "${changed}" | tr ' ' '\n' | grep '^lib/node_modules/@stdlib') || true
113+
114+
# Find unique package directories:
115+
directories=$(echo "${changed}" | tr ' ' '\n' | sed -E 's/\/(bin|data|etc|include|lib|src|test)\/?$//' | uniq)
116+
117+
if [ -z "${directories}" ]; then
118+
echo 'No packages to run benchmarks for.' >&2
119+
cleanup
120+
print_success
121+
exit 0
122+
fi
123+
124+
# Extract package names from changed package directories (e.g., @stdlib/math/base/special/sin) by removing the leading 'lib/node_modules/':
125+
packages=$(echo "${directories}" | sed -E 's/^lib\/node_modules\///')
126+
127+
# Build native add-ons for packages (if applicable):
128+
for pkg in ${packages}; do
129+
if [ -f "lib/node_modules/${pkg}/binding.gyp" ]; then
130+
NODE_ADDONS_PATTERN="${pkg}" make install-node-addons
131+
fi
132+
done
133+
134+
# Find all benchmark files in package directories:
135+
js_bench_files=$(find "${directories}" -maxdepth 3 -wholename '**/benchmark/benchmark*.js' | grep -v '/fixtures/' | sort -u | tr '\n' ' ') || true
136+
137+
# Run JS benchmarks:
138+
if [ -n "${js_bench_files}" ]; then
139+
make benchmark-javascript-files FILES="${js_bench_files}"
140+
else
141+
echo 'No JavaScript benchmarks to run.' >&2
142+
fi
143+
144+
# Run C benchmarks:
145+
echo "Finding C benchmark files in ${directories}..."
146+
c_bench_files=$(find "${directories}" -maxdepth 4 -wholename '**/benchmark/c/benchmark*.c' -exec realpath {} \; | grep -v '/fixtures/' | sort -u | tr '\n' ' ') || true
147+
148+
if [ -n "${c_bench_files}" ]; then
149+
make benchmark-c-files FILES="${c_bench_files}"
150+
else
151+
echo 'No C benchmarks to run.' >&2
152+
fi
153+
154+
cleanup
155+
print_success
156+
exit 0
157+
}
158+
159+
# Set an error handler to print captured output and perform any clean-up tasks:
160+
trap 'on_error' ERR
161+
162+
# Run main:
163+
main

0 commit comments

Comments
 (0)