|
| 1 | +name: "Workflow/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" |
| 12 | + required: false |
| 13 | + default: linux-amd64-cpu16 |
| 14 | + type: string |
| 15 | + good_ref: |
| 16 | + description: "Good ref/sha/tag/branch. Defaults to latest release tag." |
| 17 | + required: false |
| 18 | + type: string |
| 19 | + bad_ref: |
| 20 | + description: "Bad ref/sha/tag/branch. Defaults to main." |
| 21 | + required: false |
| 22 | + type: string |
| 23 | + launch_args: |
| 24 | + description: "Arguments for .devcontainer/launch.sh -d" |
| 25 | + required: false |
| 26 | + default: "" |
| 27 | + type: string |
| 28 | + preset: |
| 29 | + description: "CMake preset" |
| 30 | + required: true |
| 31 | + type: string |
| 32 | + build_targets: |
| 33 | + description: "Space separated ninja build targets" |
| 34 | + required: false |
| 35 | + default: "" |
| 36 | + type: string |
| 37 | + ctest_targets: |
| 38 | + description: "Space separated CTest targets" |
| 39 | + required: false |
| 40 | + default: "" |
| 41 | + type: string |
| 42 | + workflow_call: |
| 43 | + inputs: |
| 44 | + runner: |
| 45 | + description: "Runner label" |
| 46 | + required: false |
| 47 | + default: linux-amd64-cpu16 |
| 48 | + type: string |
| 49 | + good_ref: |
| 50 | + description: "Good ref/sha/tag/branch. Defaults to latest release tag." |
| 51 | + required: false |
| 52 | + type: string |
| 53 | + bad_ref: |
| 54 | + description: "Bad ref/sha/tag/branch. Defaults to HEAD." |
| 55 | + required: false |
| 56 | + type: string |
| 57 | + launch_args: |
| 58 | + description: "Arguments for .devcontainer/launch.sh -d" |
| 59 | + required: false |
| 60 | + default: "" |
| 61 | + type: string |
| 62 | + preset: |
| 63 | + description: "CMake preset" |
| 64 | + required: true |
| 65 | + type: string |
| 66 | + build_targets: |
| 67 | + description: "Space separated ninja build targets" |
| 68 | + required: false |
| 69 | + default: "" |
| 70 | + type: string |
| 71 | + ctest_targets: |
| 72 | + description: "Space separated CTest targets" |
| 73 | + required: false |
| 74 | + default: "" |
| 75 | + type: string |
| 76 | + |
| 77 | +jobs: |
| 78 | + bisect: |
| 79 | + runs-on: ${{ inputs.runner }} |
| 80 | + permissions: |
| 81 | + id-token: write |
| 82 | + contents: read |
| 83 | + steps: |
| 84 | + - name: Checkout repository |
| 85 | + uses: actions/checkout@v4 |
| 86 | + with: |
| 87 | + persist-credentials: false |
| 88 | + fetch-depth: 0 # FULL history |
| 89 | + fetch-tags: true # Ensure tags are present |
| 90 | + |
| 91 | + - name: Get AWS credentials for sccache bucket |
| 92 | + uses: aws-actions/configure-aws-credentials@v4 |
| 93 | + with: |
| 94 | + role-to-assume: arn:aws:iam::279114543810:role/gha-oidc-NVIDIA |
| 95 | + aws-region: us-east-2 |
| 96 | + role-duration-seconds: 43200 # 12 hours |
| 97 | + |
| 98 | + - name: Prepare AWS config for devcontainer |
| 99 | + run: | |
| 100 | + # The devcontainer will mount this path to the home directory |
| 101 | + aws_dir="${{ github.workspace }}/.aws" |
| 102 | + mkdir -p "${aws_dir}" |
| 103 | + cat > "${aws_dir}/config" <<EOF |
| 104 | + [default] |
| 105 | + bucket=rapids-sccache-devs |
| 106 | + region=us-east-2 |
| 107 | + EOF |
| 108 | + cat > "${aws_dir}/credentials" <<EOF |
| 109 | + [default] |
| 110 | + aws_access_key_id=${AWS_ACCESS_KEY_ID} |
| 111 | + aws_session_token=${AWS_SESSION_TOKEN} |
| 112 | + aws_secret_access_key=${AWS_SECRET_ACCESS_KEY} |
| 113 | + EOF |
| 114 | + chmod 0600 "${aws_dir}/credentials" |
| 115 | + chmod 0664 "${aws_dir}/config" |
| 116 | +
|
| 117 | + - name: Resolve good and bad refs |
| 118 | + id: refs |
| 119 | + run: | |
| 120 | + good_ref="${{ inputs.good_ref }}" |
| 121 | + bad_ref="${{ inputs.bad_ref }}" |
| 122 | + if [[ -z "$good_ref" ]]; then |
| 123 | + good_ref=$(git tag --list 'v*' | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | sort -V | tail -n 1) |
| 124 | + echo "Good ref defaulted to last release: $good_ref" |
| 125 | + fi |
| 126 | + if [[ -z "$good_ref" ]]; then |
| 127 | + echo "::error::Unable to determine good ref" >&2 |
| 128 | + exit 1 |
| 129 | + fi |
| 130 | + if [[ -z "$bad_ref" ]]; then |
| 131 | + bad_ref="HEAD" |
| 132 | + echo "Bad ref defaulted to HEAD: $bad_ref" |
| 133 | + fi |
| 134 | + good_sha=$(git rev-parse "$good_ref") |
| 135 | + bad_sha=$(git rev-parse "$bad_ref") |
| 136 | + echo "good_sha=$good_sha" >> "$GITHUB_OUTPUT" |
| 137 | + echo "bad_sha=$bad_sha" >> "$GITHUB_OUTPUT" |
| 138 | + echo "Good ref $good_ref resolved to $good_sha" |
| 139 | + echo "Bad ref $bad_ref resolved to $bad_sha" |
| 140 | +
|
| 141 | + - name: Run git bisect |
| 142 | + env: |
| 143 | + GOOD_SHA: ${{ steps.refs.outputs.good_sha }} |
| 144 | + BAD_SHA: ${{ steps.refs.outputs.bad_sha }} |
| 145 | + PRESET: ${{ inputs.preset }} |
| 146 | + BUILD_TARGETS: ${{ inputs.build_targets }} |
| 147 | + CTEST_TARGETS: ${{ inputs.ctest_targets }} |
| 148 | + LAUNCH_ARGS: ${{ inputs.launch_args }} |
| 149 | + GITHUB_REPOSITORY: ${{ github.repository }} |
| 150 | + # AWS credentials from the configure-aws-credentials action |
| 151 | + AWS_ACCESS_KEY_ID: ${{ env.AWS_ACCESS_KEY_ID }} |
| 152 | + AWS_SESSION_TOKEN: ${{ env.AWS_SESSION_TOKEN }} |
| 153 | + AWS_SECRET_ACCESS_KEY: ${{ env.AWS_SECRET_ACCESS_KEY }} |
| 154 | + AWS_REGION: ${{ env.AWS_REGION }} |
| 155 | + run: | |
| 156 | + cat <<'BASH' > bisect.sh |
| 157 | + #!/usr/bin/env bash |
| 158 | +
|
| 159 | + set -euo pipefail |
| 160 | +
|
| 161 | + cat <<'RUN' > run-once.sh |
| 162 | + #!/usr/bin/env bash |
| 163 | +
|
| 164 | + set -euo pipefail |
| 165 | +
|
| 166 | + echo "::group::⚙️ Testing $(git log --oneline | head -n1)" |
| 167 | +
|
| 168 | + # Configure and parse the build directory from CMake output |
| 169 | + BUILD_DIR="" |
| 170 | + if ! cmake --preset "${PRESET}" 2>&1 | tee cmake-config.log; then |
| 171 | + echo "::endgroup::" |
| 172 | + echo "🔴📝 CMake configure failed for preset ${PRESET}" |
| 173 | + exit 1 |
| 174 | + fi |
| 175 | + BUILD_DIR=$(awk -F': ' '/-- Build files have been written to:/ {print $2}' cmake-config.log | tail -n1) |
| 176 | + if [[ -z "${BUILD_DIR}" ]]; then |
| 177 | + echo "::endgroup::" |
| 178 | + echo "🔴‼️ Unable to determine build directory" |
| 179 | + exit 1 |
| 180 | + fi |
| 181 | + if [[ -n "${BUILD_TARGETS}" ]]; then |
| 182 | + if ! ninja -C "${BUILD_DIR}" ${BUILD_TARGETS}; then |
| 183 | + echo "::endgroup::" |
| 184 | + echo "🔴🛠️ Ninja build failed for targets: ${BUILD_TARGETS}" |
| 185 | + exit 1 |
| 186 | + fi |
| 187 | + fi |
| 188 | + if [[ -n "${CTEST_TARGETS}" ]]; then |
| 189 | + for t in ${CTEST_TARGETS}; do |
| 190 | + if ! ctest --test-dir "${BUILD_DIR}" -R "$t" -V --output-on-failure; then |
| 191 | + echo "::endgroup::" |
| 192 | + echo "🔴🔎 CTest failed for target $t" |
| 193 | + exit 1 |
| 194 | + fi |
| 195 | + done |
| 196 | + fi |
| 197 | +
|
| 198 | + echo "::endgroup::" |
| 199 | + echo "🟢✅ Commit ok." |
| 200 | + exit 0 |
| 201 | + RUN |
| 202 | + chmod +x run-once.sh |
| 203 | +
|
| 204 | + echo "Starting bisect with:" |
| 205 | + echo " BAD_SHA: $BAD_SHA" |
| 206 | + echo " GOOD_SHA: $GOOD_SHA" |
| 207 | +
|
| 208 | + echo "::group::⚙️ Starting git bisect" |
| 209 | + (set -x; git bisect start "$BAD_SHA" "$GOOD_SHA") |
| 210 | + echo "::endgroup::" |
| 211 | +
|
| 212 | + if git bisect run ./run-once.sh; then |
| 213 | + bad_commit=$(git rev-parse HEAD) |
| 214 | + commit_info=$(git log --pretty=format:'%h %s'| head -n1) |
| 215 | + pr_ref=$(printf '%s\n' "$commit_info" | grep -oE '#[0-9]+' | head -n1 || true) |
| 216 | + echo "::group::Bisect Result Info" |
| 217 | + { |
| 218 | + echo "### Bisect Result" |
| 219 | +
|
| 220 | + echo "- Culprit Commit: $commit_info" |
| 221 | + if [[ -n "$pr_ref" ]]; then |
| 222 | + pr_num=${pr_ref#\#} |
| 223 | + echo "- Culprit PR: https://github.com/${GITHUB_REPOSITORY}/pull/$pr_num" |
| 224 | + fi |
| 225 | + echo "- Commit SHA: $bad_commit" |
| 226 | + echo "- Commit URL: https://github.com/${GITHUB_REPOSITORY}/commit/${bad_commit}" |
| 227 | + echo |
| 228 | + echo "<details><summary>Bisect Log</summary>" |
| 229 | + echo "<pre>" |
| 230 | + git bisect log || : |
| 231 | + echo "</pre>" |
| 232 | + echo "</details>" |
| 233 | + echo |
| 234 | + } | tee -a /tmp/shared/summary.md |
| 235 | + echo "::endgroup::" |
| 236 | + else |
| 237 | + echo "::group::Bisect Result Info" |
| 238 | + { |
| 239 | + echo "### Bisect Failed" |
| 240 | + echo "git bisect did not resolve to a single commit." |
| 241 | + echo "<pre>" |
| 242 | + git bisect log || : |
| 243 | + echo "</pre>" |
| 244 | + } | tee -a /tmp/shared/summary.md |
| 245 | + echo "::endgroup::" |
| 246 | + exit 1 |
| 247 | + fi |
| 248 | +
|
| 249 | + BASH |
| 250 | + chmod +x bisect.sh |
| 251 | +
|
| 252 | + mkdir /tmp/shared/ |
| 253 | +
|
| 254 | + rc=0 |
| 255 | + .devcontainer/launch.sh -d ${LAUNCH_ARGS} \ |
| 256 | + --env "AWS_ROLE_ARN=" \ |
| 257 | + --env "AWS_REGION=${AWS_REGION}" \ |
| 258 | + --env "SCCACHE_REGION=${AWS_REGION}" \ |
| 259 | + --env "AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}" \ |
| 260 | + --env "AWS_SESSION_TOKEN=${AWS_SESSION_TOKEN}" \ |
| 261 | + --env "AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}" \ |
| 262 | + --env "GITHUB_SHA=$GITHUB_SHA" \ |
| 263 | + --env "GITHUB_WORKSPACE=$GITHUB_WORKSPACE" \ |
| 264 | + --env "GOOD_SHA=$GOOD_SHA" \ |
| 265 | + --env "BAD_SHA=$BAD_SHA" \ |
| 266 | + --env "PRESET=$PRESET" \ |
| 267 | + --env "BUILD_TARGETS=$BUILD_TARGETS" \ |
| 268 | + --env "CTEST_TARGETS=$CTEST_TARGETS" \ |
| 269 | + --env "GITHUB_REPOSITORY=$GITHUB_REPOSITORY" \ |
| 270 | + --volume "/tmp/shared:/tmp/shared" \ |
| 271 | + -- ./bisect.sh || rc=$? |
| 272 | +
|
| 273 | + find /tmp/shared -type f -exec cat {} \; >> "$GITHUB_STEP_SUMMARY" |
0 commit comments