Git Bisect #15
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: "Git Bisect" | |
defaults: | |
run: | |
shell: bash --noprofile --norc -euo pipefail {0} | |
on: | |
workflow_dispatch: | |
inputs: | |
runner: | |
description: "Runner label. Ex: 'linux-amd64-cpu16', 'linux-amd64-gpu-rtxa6000-latest-1'" | |
required: true | |
default: linux-amd64-cpu16 | |
type: string | |
launch_args: | |
description: "'.devcontainer/launch.sh -d' add'l args. Ex: '--cuda 12.9 --host gcc13'" | |
required: false | |
default: "--cuda 13.0 --host gcc14" | |
type: string | |
preset: | |
description: "CMake preset. Ex: cub-cpp20" | |
required: true | |
default: "cub-cpp20" | |
type: string | |
cmake_options: | |
description: "Additional options passed to CMake preset configure (e.g. -DVAR=ON)" | |
required: false | |
default: "-DCMAKE_CUDA_ARCHITECTURES=80;90;100;120" | |
type: string | |
build_targets: | |
description: "Space separated ninja build targets to build." | |
required: false | |
default: "" | |
type: string | |
ctest_targets: | |
description: "Space separated CTest -R regexes to run." | |
required: false | |
default: "" | |
type: string | |
lit_precompile_tests: | |
description: "Space-separated libcudacxx lit test paths to compile without execution" | |
required: false | |
default: "" | |
type: string | |
lit_tests: | |
description: "Space-separated libcudacxx lit test paths to compile and execute" | |
required: false | |
default: "" | |
type: string | |
good_ref: | |
description: "Good ref/sha/tag/branch. Defaults to latest release tag. '-Nd' means 'N days ago on main'." | |
required: false | |
type: string | |
bad_ref: | |
description: "Bad ref/sha/tag/branch. Defaults to main. '-Nd' means 'N days ago on main'." | |
required: false | |
type: string | |
workflow_call: | |
inputs: | |
runner: | |
description: "Runner label. Ex: 'linux-amd64-cpu16', 'linux-amd64-gpu-rtxa6000-latest-1'" | |
required: true | |
default: linux-amd64-cpu16 | |
type: string | |
launch_args: | |
description: "'.devcontainer/launch.sh -d' add'l args. Ex: '--ctk 12.9 --host gcc13'" | |
required: false | |
default: "" | |
type: string | |
preset: | |
description: "CMake preset. Ex: cub-cpp20" | |
required: true | |
default: "" | |
type: string | |
cmake_options: | |
description: "Additional options passed to CMake preset configure (e.g. -DVAR=ON)" | |
required: false | |
default: "" | |
type: string | |
build_targets: | |
description: "Space separated ninja build targets to build." | |
required: false | |
default: "" | |
type: string | |
ctest_targets: | |
description: "Space separated CTest -R regexes to run." | |
required: false | |
default: "" | |
type: string | |
lit_precompile_tests: | |
description: "Space-separated libcudacxx lit test paths to compile without execution" | |
required: false | |
default: "" | |
type: string | |
lit_tests: | |
description: "Space-separated libcudacxx lit test paths to compile and execute" | |
required: false | |
default: "" | |
type: string | |
good_ref: | |
description: "Good ref/sha/tag/branch. Defaults to latest release tag. '-Nd' means 'N days ago on main'." | |
required: false | |
type: string | |
bad_ref: | |
description: "Bad ref/sha/tag/branch. Defaults to main. '-Nd' means 'N days ago on main'." | |
required: false | |
type: string | |
jobs: | |
# Hash all of the inputs and generate a descriptive, but unique, name for the bisect job. | |
# This is necessary because GitHub Actions does not provide an easy way to get the numeric | |
# job id from within a running job, unless the name is unique across the entire run. | |
generate-job-name: | |
name: Generate Unique Job Name | |
runs-on: ubuntu-latest | |
outputs: | |
job-name: ${{ steps.set-name.outputs.unique-name }} | |
steps: | |
- name: Set unique name | |
id: set-name | |
run: | | |
# Hash the inputs to create a unique identifier | |
input_string=$(cat <<HASH | |
${{ inputs.runner }} | |
${{ inputs.launch_args }} | |
${{ inputs.preset }} | |
${{ inputs.cmake_options }} | |
${{ inputs.build_targets }} | |
${{ inputs.ctest_targets }} | |
${{ inputs.lit_precompile_tests }} | |
${{ inputs.lit_tests }} | |
${{ inputs.good_ref }} | |
${{ inputs.bad_ref }} | |
HASH | |
) | |
hash=$(echo -n "$input_string" | sha256sum | awk '{print $1}') | |
short_hash=${hash:0:8} | |
function sanitize() { | |
echo "$1" | tr -cd '[:alnum:]-' | |
} | |
function compress() { | |
input=$1 | |
input_length=${#input} | |
# "begin.and.end" -> "begin_d.end" | |
delim="_" | |
keep_chars=5 | |
compressed_str=${input:0:keep_chars}$delim${input: -keep_chars} | |
compressed_length=${#compressed_str} | |
if [[ $input_length -gt $compressed_length ]]; then | |
echo "$compressed_str" | |
else | |
echo "$input" | |
fi | |
} | |
preset=$(sanitize "${{ inputs.preset }}") | |
good_ref=$(sanitize "${{ inputs.good_ref }}") | |
bad_ref=$(sanitize "${{ inputs.bad_ref }}") | |
build_targets=$(sanitize "${{ inputs.build_targets }}") | |
ctest_targets=$(sanitize "${{ inputs.ctest_targets }}") | |
lit_precompile_tests=$(sanitize "${{ inputs.lit_precompile_tests }}") | |
lit_tests=$(sanitize "${{ inputs.lit_tests }}") | |
build_targets_part=$(compress "$build_targets") | |
ctest_targets_part=$(compress "$ctest_targets") | |
lit_precompile_tests_part=$(compress "$lit_precompile_tests") | |
lit_tests_part=$(compress "$lit_tests") | |
# Parse "--cuda XX.Y" / "-c XX.Y" and "--host <str>" / "-H <str>" | |
launch_args="${{ inputs.launch_args }}" | |
cuda_version="ctk$(grep -oP '(?:--cuda|-c)\s+\K\S+' <<< "$launch_args" || true)" | |
host_name=$(grep -oP '(?:--host|-H)\s+\K\S+' <<< "$launch_args" || true) | |
# Parse `amd64/arm64` and -gpu-<gpu_name> from runner name. | |
# Ex: 'linux-amd64-gpu-rtxa6000-latest-1' | |
runner=${{ inputs.runner }} | |
cpu_arch=$(echo "${runner}" | grep -oP '(?:^|-)\K(?:amd64|arm64)(?=-)' || true) | |
gpu_name=$(echo "${runner}" | grep -oP '(?:-gpu-)\K[^-]+' || true) | |
# Vars to include in unique name: | |
declare -a vars=( | |
"cuda_version" | |
"host_name" | |
"preset" | |
"build_targets_part" | |
"ctest_targets_part" | |
"lit_precompile_tests_part" | |
"lit_tests_part" | |
"gpu_name" | |
"cpu_arch" | |
"short_hash" | |
) | |
unique_name="bisect" | |
for var in "${vars[@]}"; do | |
val=${!var} | |
if [[ -n "$val" ]]; then | |
unique_name+="-$val" | |
fi | |
done | |
echo "Unique job name: $unique_name" | |
echo "unique-name=$unique_name" >> "$GITHUB_OUTPUT" | |
bisect: | |
needs: [generate-job-name] | |
name: ${{ needs.generate-job-name.outputs.job-name }} | |
runs-on: ${{ github.repository == 'NVIDIA/cccl' && inputs.runner || 'ubuntu-latest' }} | |
permissions: | |
id-token: write | |
contents: read | |
steps: | |
- name: Checkout repository | |
uses: actions/checkout@v4 | |
with: | |
persist-credentials: false | |
fetch-depth: 0 # FULL history | |
fetch-tags: true # Ensure tags are present | |
- name: Get AWS credentials for sccache bucket | |
if: ${{ github.repository == 'NVIDIA/cccl' }} | |
uses: aws-actions/configure-aws-credentials@v4 | |
with: | |
role-to-assume: arn:aws:iam::279114543810:role/gha-oidc-NVIDIA | |
aws-region: us-east-2 | |
role-duration-seconds: 43200 # 12 hours | |
- name: Prepare AWS config for devcontainer | |
run: | | |
# The devcontainer will mount this path to the home directory | |
aws_dir="${{ github.workspace }}/.aws" | |
mkdir -p "${aws_dir}" | |
cat > "${aws_dir}/config" <<EOF | |
[default] | |
bucket=rapids-sccache-devs | |
region=us-east-2 | |
EOF | |
cat > "${aws_dir}/credentials" <<EOF | |
[default] | |
aws_access_key_id=${AWS_ACCESS_KEY_ID:-} | |
aws_session_token=${AWS_SESSION_TOKEN:-} | |
aws_secret_access_key=${AWS_SECRET_ACCESS_KEY:-} | |
EOF | |
chmod 0600 "${aws_dir}/credentials" | |
chmod 0664 "${aws_dir}/config" | |
- name: Fetch full history | |
run: | | |
# Add the NVIDIA/cccl repo in case we're running on a fork without tags: | |
git remote add upstream https://github.com/NVIDIA/cccl.git || : | |
git fetch upstream || : | |
- name: Run git bisect | |
env: | |
GITHUB_TOKEN: ${{ github.token }} | |
LAUNCH_ARGS: ${{ inputs.launch_args }} | |
GITHUB_REPOSITORY: ${{ github.repository }} | |
# AWS credentials from the configure-aws-credentials action | |
AWS_ACCESS_KEY_ID: ${{ env.AWS_ACCESS_KEY_ID }} | |
AWS_SESSION_TOKEN: ${{ env.AWS_SESSION_TOKEN }} | |
AWS_SECRET_ACCESS_KEY: ${{ env.AWS_SECRET_ACCESS_KEY }} | |
AWS_REGION: ${{ env.AWS_REGION }} | |
run: | | |
echo -e "\e[1;34mLaunching devcontainer and running git bisect...\e[0m" | |
GPU_ARGS="" | |
if [[ "${{ inputs.runner }}" == *"-gpu-"* ]]; then | |
if [[ ! "${LAUNCH_ARGS}" =~ "--gpus" ]]; then | |
echo "GPU runner detected; enabling '--gpus all'" | |
GPU_ARGS="--gpus all" | |
else | |
echo "GPU runner detected, but '--gpus' already present in LAUNCH_ARGS" | |
fi | |
fi | |
# Grab the url for this step summary | |
run="$GITHUB_RUN_ID" | |
attempt="${GITHUB_RUN_ATTEMPT:-1}" | |
server="${GITHUB_SERVER_URL:-https://github.com}" | |
repo="$GITHUB_REPOSITORY" | |
job_name="${{ needs.generate-job-name.outputs.job-name }}" | |
job_id=$( | |
gh api --paginate \ | |
repos/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}/jobs \ | |
--jq ".jobs[] | select(.name | endswith(\"${job_name}\")) | .id" | |
) || : | |
GHA_LOG_URL="$server/$repo/actions/runs/$run/job/$job_id" | |
STEP_SUMMARY_URL="$server/$repo/actions/runs/$run/attempts/$attempt#summary-$job_id" | |
echo -e "\e[1;34mGHA Log URL: $GHA_LOG_URL\e[0m" | |
echo -e "\e[1;34mBisection Results: $STEP_SUMMARY_URL\e[0m" | |
mkdir -p /tmp/shared | |
rc=0 | |
set -x | |
.devcontainer/launch.sh -d ${LAUNCH_ARGS} ${GPU_ARGS} \ | |
--env "AWS_ROLE_ARN=" \ | |
--env "AWS_REGION=${AWS_REGION}" \ | |
--env "SCCACHE_REGION=${AWS_REGION}" \ | |
--env "AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}" \ | |
--env "AWS_SESSION_TOKEN=${AWS_SESSION_TOKEN}" \ | |
--env "AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}" \ | |
--env "LAUNCH_ARGS=${LAUNCH_ARGS} ${GPU_ARGS}" \ | |
--env "STEP_SUMMARY_URL=${STEP_SUMMARY_URL}" \ | |
--env "GHA_LOG_URL=${GHA_LOG_URL}" \ | |
--env "GITHUB_REPOSITORY=${GITHUB_REPOSITORY}" \ | |
--volume "/tmp/shared:/tmp/shared" \ | |
-- ./ci/util/git_bisect.sh \ | |
--summary-file "/tmp/shared/summary.md" \ | |
--good-ref "${{ inputs.good_ref }}" \ | |
--bad-ref "${{ inputs.bad_ref }}" \ | |
--preset "${{ inputs.preset }}" \ | |
--cmake-options "${{ inputs.cmake_options }}" \ | |
--build-targets "${{ inputs.build_targets }}" \ | |
--ctest-targets "${{ inputs.ctest_targets }}" \ | |
--lit-precompile-tests "${{ inputs.lit_precompile_tests }}" \ | |
--lit-tests "${{ inputs.lit_tests }}" \ | |
|| rc=$? | |
set +x | |
# Append the summary (if any) to the GitHub step summary | |
if [[ -d /tmp/shared ]]; then | |
find /tmp/shared -type f -exec cat {} \; >> "$GITHUB_STEP_SUMMARY" || : | |
fi | |
echo -e "\e[1;34mGHA Log URL: $GHA_LOG_URL\e[0m" | |
echo -e "\e[1;34mBisection Results: $STEP_SUMMARY_URL\e[0m" | |
exit $rc |