Skip to content

Commit 42074d2

Browse files
committed
Add targeted build/test + bisect scripts and workflow
- build_and_test_targets.sh: reproducible single-target configure/build/test (ninja, ctest, lit) - git_bisect.sh: wrapper to automate regression testing with minimal targets - Workflow/Bisect: GitHub Actions job to run bisect remotely and generate Markdown summaries
1 parent c230ecf commit 42074d2

File tree

5 files changed

+887
-0
lines changed

5 files changed

+887
-0
lines changed

.github/workflows/git-bisect.yml

Lines changed: 324 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,324 @@
1+
name: "Git Bisect"
2+
3+
defaults:
4+
run:
5+
shell: bash --noprofile --norc -euo pipefail {0}
6+
7+
on:
8+
workflow_dispatch:
9+
inputs:
10+
runner:
11+
description: "Runner label. Ex: 'linux-amd64-cpu16', 'linux-amd64-gpu-rtxa6000-latest-1'"
12+
required: true
13+
default: linux-amd64-cpu16
14+
type: string
15+
launch_args:
16+
description: "'.devcontainer/launch.sh -d' add'l args. Ex: '--ctk 12.9 --host gcc13'"
17+
required: false
18+
default: ""
19+
type: string
20+
preset:
21+
description: "CMake preset. Ex: cub-cpp20"
22+
required: true
23+
default: ""
24+
type: string
25+
cmake_options:
26+
description: "Additional options passed to CMake preset configure (e.g. -DVAR=ON)"
27+
required: false
28+
default: ""
29+
type: string
30+
build_targets:
31+
description: "Space separated ninja build targets to build."
32+
required: false
33+
default: ""
34+
type: string
35+
ctest_targets:
36+
description: "Space separated CTest -R regexes to run."
37+
required: false
38+
default: ""
39+
type: string
40+
lit_precompile_tests:
41+
description: "Space-separated libcudacxx lit test paths to compile without execution"
42+
required: false
43+
default: ""
44+
type: string
45+
lit_tests:
46+
description: "Space-separated libcudacxx lit test paths to compile and execute"
47+
required: false
48+
default: ""
49+
type: string
50+
good_ref:
51+
description: "Good ref/sha/tag/branch. Defaults to latest release tag. '-Nd' means 'N days ago on main'."
52+
required: false
53+
type: string
54+
bad_ref:
55+
description: "Bad ref/sha/tag/branch. Defaults to main. '-Nd' means 'N days ago on main'."
56+
required: false
57+
type: string
58+
workflow_call:
59+
inputs:
60+
runner:
61+
description: "Runner label. Ex: 'linux-amd64-cpu16', 'linux-amd64-gpu-rtxa6000-latest-1'"
62+
required: true
63+
default: linux-amd64-cpu16
64+
type: string
65+
launch_args:
66+
description: "'.devcontainer/launch.sh -d' add'l args. Ex: '--ctk 12.9 --host gcc13'"
67+
required: false
68+
default: ""
69+
type: string
70+
preset:
71+
description: "CMake preset. Ex: cub-cpp20"
72+
required: true
73+
default: ""
74+
type: string
75+
cmake_options:
76+
description: "Additional options passed to CMake preset configure (e.g. -DVAR=ON)"
77+
required: false
78+
default: ""
79+
type: string
80+
build_targets:
81+
description: "Space separated ninja build targets to build."
82+
required: false
83+
default: ""
84+
type: string
85+
ctest_targets:
86+
description: "Space separated CTest -R regexes to run."
87+
required: false
88+
default: ""
89+
type: string
90+
lit_precompile_tests:
91+
description: "Space-separated libcudacxx lit test paths to compile without execution"
92+
required: false
93+
default: ""
94+
type: string
95+
lit_tests:
96+
description: "Space-separated libcudacxx lit test paths to compile and execute"
97+
required: false
98+
default: ""
99+
type: string
100+
good_ref:
101+
description: "Good ref/sha/tag/branch. Defaults to latest release tag. '-Nd' means 'N days ago on main'."
102+
required: false
103+
type: string
104+
bad_ref:
105+
description: "Bad ref/sha/tag/branch. Defaults to main. '-Nd' means 'N days ago on main'."
106+
required: false
107+
type: string
108+
109+
jobs:
110+
# Hash all of the inputs and generate a descriptive, but unique, name for the bisect job.
111+
# This is necessary because GitHub Actions does not provide an easy way to get the numeric
112+
# job id from within a running job, unless the name is unique across the entire run.
113+
generate-job-name:
114+
name: Generate Unique Job Name
115+
runs-on: ubuntu-latest
116+
outputs:
117+
job-name: ${{ steps.set-name.outputs.unique-name }}
118+
steps:
119+
- name: Set unique name
120+
id: set-name
121+
run: |
122+
# Hash the inputs to create a unique identifier
123+
input_string=$(cat <<HASH
124+
${{ inputs.runner }}
125+
${{ inputs.launch_args }}
126+
${{ inputs.preset }}
127+
${{ inputs.cmake_options }}
128+
${{ inputs.build_targets }}
129+
${{ inputs.ctest_targets }}
130+
${{ inputs.lit_precompile_tests }}
131+
${{ inputs.lit_tests }}
132+
${{ inputs.good_ref }}
133+
${{ inputs.bad_ref }}
134+
HASH
135+
)
136+
hash=$(echo -n "$input_string" | sha256sum | awk '{print $1}')
137+
short_hash=${hash:0:8}
138+
139+
function sanitize() {
140+
echo "$1" | tr -cd '[:alnum:]-'
141+
}
142+
143+
function compress() {
144+
input=$1
145+
input_length=${#input}
146+
147+
# "begin.and.end" -> "begin_d.end"
148+
delim="_"
149+
keep_chars=5
150+
compressed_str=${input:0:keep_chars}$delim${input: -keep_chars}
151+
compressed_length=${#compressed_str}
152+
153+
if [[ $input_length -gt $compressed_length ]]; then
154+
echo "$compressed_str"
155+
else
156+
echo "$input"
157+
fi
158+
}
159+
160+
preset=$(sanitize "${{ inputs.preset }}")
161+
good_ref=$(sanitize "${{ inputs.good_ref }}")
162+
bad_ref=$(sanitize "${{ inputs.bad_ref }}")
163+
build_targets=$(sanitize "${{ inputs.build_targets }}")
164+
ctest_targets=$(sanitize "${{ inputs.ctest_targets }}")
165+
lit_precompile_tests=$(sanitize "${{ inputs.lit_precompile_tests }}")
166+
lit_tests=$(sanitize "${{ inputs.lit_tests }}")
167+
168+
build_targets_part=$(compress "$build_targets")
169+
ctest_targets_part=$(compress "$ctest_targets")
170+
lit_precompile_tests_part=$(compress "$lit_precompile_tests")
171+
lit_tests_part=$(compress "$lit_tests")
172+
173+
# Parse "--cuda XX.Y" / "-c XX.Y" and "--host <str>" / "-H <str>"
174+
launch_args="${{ inputs.launch_args }}"
175+
cuda_version="ctk$(grep -oP '(?:--cuda|-c)\s+\K\S+' <<< "$launch_args" || true)"
176+
host_name=$(grep -oP '(?:--host|-H)\s+\K\S+' <<< "$launch_args" || true)
177+
178+
# Parse `amd64/arm64` and -gpu-<gpu_name> from runner name.
179+
# Ex: 'linux-amd64-gpu-rtxa6000-latest-1'
180+
runner=${{ inputs.runner }}
181+
cpu_arch=$(echo "${runner}" | grep -oP '(?:^|-)\K(?:amd64|arm64)(?=-)' || true)
182+
gpu_name=$(echo "${runner}" | grep -oP '(?:-gpu-)\K[^-]+' || true)
183+
184+
# Vars to include in unique name:
185+
declare -a vars=(
186+
"cuda_version"
187+
"host_name"
188+
"preset"
189+
"build_targets_part"
190+
"ctest_targets_part"
191+
"lit_precompile_tests_part"
192+
"lit_tests_part"
193+
"gpu_name"
194+
"cpu_arch"
195+
"short_hash"
196+
)
197+
unique_name="bisect"
198+
for var in "${vars[@]}"; do
199+
val=${!var}
200+
if [[ -n "$val" ]]; then
201+
unique_name+="-$val"
202+
fi
203+
done
204+
205+
echo "Unique job name: $unique_name"
206+
echo "unique-name=$unique_name" >> "$GITHUB_OUTPUT"
207+
208+
bisect:
209+
needs: [generate-job-name]
210+
name: ${{ needs.generate-job-name.outputs.job-name }}
211+
runs-on: ${{ inputs.runner }}
212+
permissions:
213+
id-token: write
214+
contents: read
215+
steps:
216+
- name: Checkout repository
217+
uses: actions/checkout@v4
218+
with:
219+
persist-credentials: false
220+
fetch-depth: 0 # FULL history
221+
fetch-tags: true # Ensure tags are present
222+
223+
- name: Get AWS credentials for sccache bucket
224+
uses: aws-actions/configure-aws-credentials@v4
225+
with:
226+
role-to-assume: arn:aws:iam::279114543810:role/gha-oidc-NVIDIA
227+
aws-region: us-east-2
228+
role-duration-seconds: 43200 # 12 hours
229+
230+
- name: Prepare AWS config for devcontainer
231+
run: |
232+
# The devcontainer will mount this path to the home directory
233+
aws_dir="${{ github.workspace }}/.aws"
234+
mkdir -p "${aws_dir}"
235+
cat > "${aws_dir}/config" <<EOF
236+
[default]
237+
bucket=rapids-sccache-devs
238+
region=us-east-2
239+
EOF
240+
cat > "${aws_dir}/credentials" <<EOF
241+
[default]
242+
aws_access_key_id=${AWS_ACCESS_KEY_ID}
243+
aws_session_token=${AWS_SESSION_TOKEN}
244+
aws_secret_access_key=${AWS_SECRET_ACCESS_KEY}
245+
EOF
246+
chmod 0600 "${aws_dir}/credentials"
247+
chmod 0664 "${aws_dir}/config"
248+
- name: Run git bisect
249+
env:
250+
GITHUB_TOKEN: ${{ github.token }}
251+
LAUNCH_ARGS: ${{ inputs.launch_args }}
252+
GITHUB_REPOSITORY: ${{ github.repository }}
253+
# AWS credentials from the configure-aws-credentials action
254+
AWS_ACCESS_KEY_ID: ${{ env.AWS_ACCESS_KEY_ID }}
255+
AWS_SESSION_TOKEN: ${{ env.AWS_SESSION_TOKEN }}
256+
AWS_SECRET_ACCESS_KEY: ${{ env.AWS_SECRET_ACCESS_KEY }}
257+
AWS_REGION: ${{ env.AWS_REGION }}
258+
run: |
259+
echo -e "\e[1;34mLaunching devcontainer and running git bisect...\e[0m"
260+
261+
GPU_ARGS=""
262+
if [[ "${{ inputs.runner }}" == *"-gpu-"* ]]; then
263+
if [[ ! "${LAUNCH_ARGS}" =~ "--gpus" ]]; then
264+
echo "GPU runner detected; enabling '--gpus all'"
265+
GPU_ARGS="--gpus all"
266+
else
267+
echo "GPU runner detected, but '--gpus' already present in LAUNCH_ARGS"
268+
fi
269+
fi
270+
271+
# Grab the url for this step summary
272+
run="$GITHUB_RUN_ID"
273+
attempt="${GITHUB_RUN_ATTEMPT:-1}"
274+
server="${GITHUB_SERVER_URL:-https://github.com}"
275+
repo="$GITHUB_REPOSITORY"
276+
job_name="${{ needs.generate-job-name.outputs.job-name }}"
277+
job_id=$(
278+
gh api --paginate \
279+
repos/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}/jobs \
280+
--jq ".jobs[] | select(.name | endswith(\"${job_name}\")) | .id"
281+
) || :
282+
283+
GHA_LOG_URL="$server/$repo/actions/runs/$run/job/$job_id"
284+
STEP_SUMMARY_URL="$server/$repo/actions/runs/$run/attempts/$attempt#summary-$job_id"
285+
286+
echo -e "\e[1;34mGHA Log URL: $GHA_LOG_URL\e[0m"
287+
echo -e "\e[1;34mBisection Results: $STEP_SUMMARY_URL\e[0m"
288+
289+
mkdir -p /tmp/shared
290+
rc=0
291+
set -x
292+
.devcontainer/launch.sh -d ${LAUNCH_ARGS} ${GPU_ARGS} \
293+
--env "AWS_ROLE_ARN=" \
294+
--env "AWS_REGION=${AWS_REGION}" \
295+
--env "SCCACHE_REGION=${AWS_REGION}" \
296+
--env "AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}" \
297+
--env "AWS_SESSION_TOKEN=${AWS_SESSION_TOKEN}" \
298+
--env "AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}" \
299+
--env "LAUNCH_ARGS=${LAUNCH_ARGS} ${GPU_ARGS}" \
300+
--env "STEP_SUMMARY_URL=${STEP_SUMMARY_URL}" \
301+
--env "GHA_LOG_URL=${GHA_LOG_URL}" \
302+
--volume "/tmp/shared:/tmp/shared" \
303+
-- ./ci/util/git_bisect.sh \
304+
--summary-file "/tmp/shared/summary.md" \
305+
--good-ref "${{ inputs.good_ref }}" \
306+
--bad-ref "${{ inputs.bad_ref }}" \
307+
--preset "${{ inputs.preset }}" \
308+
--cmake-options "${{ inputs.cmake_options }}" \
309+
--build-targets "${{ inputs.build_targets }}" \
310+
--ctest-targets "${{ inputs.ctest_targets }}" \
311+
--lit-precompile-tests "${{ inputs.lit_precompile_tests }}" \
312+
--lit-tests "${{ inputs.lit_tests }}" \
313+
|| rc=$?
314+
set +x
315+
316+
# Append the summary (if any) to the GitHub step summary
317+
if [[ -d /tmp/shared ]]; then
318+
find /tmp/shared -type f -exec cat {} \; >> "$GITHUB_STEP_SUMMARY" || :
319+
fi
320+
321+
echo -e "\e[1;34mGHA Log URL: $GHA_LOG_URL\e[0m"
322+
echo -e "\e[1;34mBisection Results: $STEP_SUMMARY_URL\e[0m"
323+
324+
exit $rc

0 commit comments

Comments
 (0)