Repository navigation
[libcu++] cuda_error: carry the exact status, its domain, and the source location #495
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
| # SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. | |
| # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| name: Approve SASS Diff | |
| on: | |
| issue_comment: | |
| types: [created] | |
| permissions: | |
| # Write perms needed to post reactions and comments | |
| pull-requests: write | |
| issues: write | |
| actions: write | |
| jobs: | |
| approve: | |
| # Expressions cannot match regexes; the first step validates the complete command. | |
| if: >- | |
| github.repository == 'NVIDIA/cccl' && | |
| github.event.issue.pull_request && | |
| github.event.issue.state == 'open' && | |
| github.event.comment.user.type == 'User' && | |
| contains(github.event.comment.body, '/sass diff ok') | |
| runs-on: ubuntu-slim | |
| # Mutations disable retries because a server error can follow a successful write. | |
| steps: | |
| - name: Validate approval command | |
| id: request | |
| uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 | |
| with: | |
| retries: 5 | |
| script: | | |
| const { comment, issue } = context.payload; | |
| core.info(`Validate comment ${comment.id} from @${comment.user.login} on PR #${issue.number}.`); | |
| // Match both: | |
| // | |
| // /sass diff ok <full SHA> | |
| // | |
| // and | |
| // | |
| // /sass diff ok [<short SHA>](<link to full commit SHA>) | |
| const body = comment.body.trim(); | |
| const plainMatch = /^\/sass diff ok\s+([0-9a-f]{40})$/.exec(body); | |
| const linkMatch = /^\/sass diff ok\s+\[([0-9a-f]{7,40})\]\((https:\/\/github\.com\/[^\s)]+\/([0-9a-f]{40}))\)$/.exec(body); | |
| let sha; | |
| if (plainMatch) { | |
| sha = plainMatch[1]; | |
| core.info(`Command contains a plain commit SHA: ${sha}.`); | |
| } else if (linkMatch) { | |
| sha = linkMatch[3]; | |
| core.info(`Command contains a commit link for SHA ${sha}.`); | |
| // Check both the visible label and destination before accepting a commit link. | |
| const repoUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}`; | |
| if ( | |
| !sha.startsWith(linkMatch[1]) || | |
| ![`${repoUrl}/pull/${issue.number}/commits/${sha}`, `${repoUrl}/commit/${sha}`].includes(linkMatch[2]) | |
| ) { | |
| core.info("Ignore command: the commit label or link destination does not match the expected commit."); | |
| await github.rest.issues.createComment({ | |
| request: { retries: 0 }, | |
| ...context.repo, | |
| issue_number: issue.number, | |
| body: | |
| `Error: @${comment.user.login}, the commit label or link destination is invalid. ` + | |
| "Use `/sass diff ok <SHA>` with the full 40-character SHA of the current PR head.", | |
| }); | |
| core.setFailed("The commit label or link destination is invalid."); | |
| return; | |
| } | |
| } else { | |
| core.info("Ignore comment: the command format is invalid."); | |
| // Give command syntax feedback before checking approval eligibility. | |
| if (body === "/sass diff ok") { | |
| await github.rest.issues.createComment({ | |
| request: { retries: 0 }, | |
| ...context.repo, | |
| issue_number: issue.number, | |
| body: | |
| `Error: @${comment.user.login}, use \`/sass diff ok <SHA>\`. ` + | |
| "Replace `<SHA>` with the full 40-character SHA of the current PR head.", | |
| }); | |
| } | |
| core.setFailed("The approval command format is invalid."); | |
| return; | |
| } | |
| core.info(`Read the current head of PR #${issue.number}.`); | |
| const { data: pr } = await github.rest.pulls.get({ | |
| ...context.repo, | |
| pull_number: issue.number, | |
| }); | |
| if (pr.head.sha !== sha) { | |
| core.info(`Ignore command: requested SHA ${sha} does not match PR head ${pr.head.sha}.`); | |
| return; | |
| } | |
| // Acknowledge that we have received the comment | |
| await github.rest.reactions.createForIssueComment({ | |
| request: { retries: 0 }, | |
| ...context.repo, | |
| comment_id: context.payload.comment.id, | |
| content: "eyes", | |
| }); | |
| if (comment.user.id === pr.user.id) { | |
| const body = | |
| `Error: @${comment.user.login}, you cannot approve SASS changes ` + | |
| "on your own pull request :raised_eyebrow:. " + | |
| "Please ask another CCCL developer to approve the SASS diff."; | |
| await github.rest.issues.createComment({ | |
| request: { retries: 0 }, | |
| ...context.repo, | |
| issue_number: issue.number, | |
| body, | |
| }); | |
| core.setFailed("The PR author cannot approve their own SASS changes."); | |
| return; | |
| } | |
| core.info(`Command is valid for SHA ${sha}.`); | |
| core.setOutput("sha", sha); | |
| # Repository permissions let GITHUB_TOKEN authorize approval without organization membership access. | |
| - name: Verify repository access | |
| if: steps.request.outputs.sha != '' | |
| id: authorization | |
| uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 | |
| with: | |
| retries: 5 | |
| script: | | |
| const username = context.payload.comment.user.login; | |
| core.info(`Check repository access for @${username}.`); | |
| let permission = "none"; | |
| try { | |
| const { data } = await github.rest.repos.getCollaboratorPermissionLevel({ | |
| ...context.repo, | |
| username, | |
| }); | |
| permission = data.permission; | |
| } catch (error) { | |
| // Non-collaborators must receive the same denial as users with read-only access. | |
| if (error.status !== 404) throw error; | |
| } | |
| const authorized = ["write", "maintain", "admin"].includes(permission); | |
| core.info(`Repository permission for @${username}: ${permission}; authorized=${authorized}.`); | |
| core.setOutput("authorized", authorized); | |
| if (!authorized) { | |
| const body = | |
| `Error: @${username}, ` + | |
| "you need repository write access or greater to approve SASS changes. " + | |
| "Please ask someone with that access to review the SASS diffs."; | |
| await github.rest.issues.createComment({ | |
| request: { retries: 0 }, | |
| ...context.repo, | |
| issue_number: context.payload.issue.number, | |
| body, | |
| }); | |
| core.setFailed("SASS approval requires repository write access."); | |
| } | |
| - name: Find the SASS approval Job | |
| id: job | |
| if: steps.authorization.outputs.authorized == 'true' | |
| uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 | |
| env: | |
| SHA: ${{ steps.request.outputs.sha }} | |
| with: | |
| retries: 5 | |
| script: | | |
| const { comment, issue } = context.payload; | |
| core.info(`Find CI for PR #${issue.number}, SHA ${process.env.SHA}, command ${comment.id}.`); | |
| const runs = await github.paginate(github.rest.actions.listWorkflowRuns, { | |
| ...context.repo, | |
| workflow_id: "ci-workflow-pull-request.yml", | |
| branch: `pull-request/${issue.number}`, | |
| head_sha: process.env.SHA, | |
| event: "push", | |
| per_page: 100, | |
| }); | |
| core.info(`Found ${runs.length} matching CI runs.`); | |
| const run = runs.sort((a, b) => b.id - a.id)[0]; | |
| if (!run) { | |
| core.setFailed("No matching CI run exists."); | |
| return; | |
| } | |
| if (run.status === "completed" && run.conclusion === "success") { | |
| core.info(`CI run ${run.id} already succeeds.`); | |
| core.setOutput("ci_passed", true); | |
| return; | |
| } | |
| core.info(`List jobs for workflow run ${run.id}: status=${run.status}, conclusion=${run.conclusion}.`); | |
| const jobs = await github.paginate(github.rest.actions.listJobsForWorkflowRun, { | |
| ...context.repo, | |
| run_id: run.id, | |
| filter: "latest", | |
| // Pages of 100 jobs can cause GitHub to return HTTP 502. | |
| per_page: 50, | |
| }); | |
| // This name must match the approval job in ci-workflow-pull-request.yml. | |
| const job = jobs.find((job) => job.name === "Require SASS approval"); | |
| core.info(`Found ${jobs.length} jobs. SASS approval job: ${job?.id ?? "none"}.`); | |
| if (job) core.info(`Job ${job.id}: status=${job.status}, conclusion=${job.conclusion}.`); | |
| if (!job || job.status !== "completed") { | |
| core.info("The SASS approval job is not ready. Ask the reviewer to comment again."); | |
| await github.rest.issues.createComment({ | |
| request: { retries: 0 }, | |
| ...context.repo, | |
| issue_number: issue.number, | |
| body: | |
| `Error: @${comment.user.login}, the SASS approval job is not ready. ` + | |
| "Wait for the \"Require SASS approval\" job to finish, then post " + | |
| `\`/sass diff ok ${process.env.SHA}\` again if it fails.`, | |
| }); | |
| core.setFailed("The SASS approval job is not ready."); | |
| return; | |
| } | |
| if (job.conclusion !== "failure") { | |
| core.info("The SASS approval job does not need a rerun."); | |
| return; | |
| } | |
| core.setOutput("job_id", job.id); | |
| - name: Find the report and record approval | |
| id: approval | |
| if: steps.job.outputs.job_id != '' | |
| uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 | |
| env: | |
| SHA: ${{ steps.request.outputs.sha }} | |
| with: | |
| retries: 5 | |
| script: | | |
| const { comment, issue } = context.payload; | |
| // Partial reruns change check IDs even when the report job does not run again. | |
| const marker = `<!-- sass-diff-GITHUB_SHA:${process.env.SHA} -->`; | |
| core.info(`Find report for SHA ${process.env.SHA} before command ${comment.id}.`); | |
| const query = ` | |
| query($owner: String!, $repo: String!, $number: Int!, $before: String) { | |
| repository(owner: $owner, name: $repo) { | |
| pullRequest(number: $number) { | |
| comments(last: 100, before: $before) { | |
| nodes { | |
| fullDatabaseId | |
| body | |
| isMinimized | |
| author { login __typename } | |
| } | |
| pageInfo { hasPreviousPage startCursor } | |
| } | |
| } | |
| } | |
| } | |
| `; | |
| let before = null; | |
| let report; | |
| while (!report) { | |
| const { repository } = await github.graphql(query, { | |
| ...context.repo, | |
| number: issue.number, | |
| before, | |
| }); | |
| const comments = repository.pullRequest.comments; | |
| core.info(`Read ${comments.nodes.length} comments; earlier comments=${comments.pageInfo.hasPreviousPage}.`); | |
| report = comments.nodes.reverse().find((entry) => | |
| entry.author?.__typename === "Bot" && | |
| ["github-actions", "github-actions[bot]"].includes(entry.author.login) && | |
| BigInt(entry.fullDatabaseId) < BigInt(comment.id) && | |
| entry.body.split(/\r?\n/).includes(marker) | |
| ); | |
| if (report || !comments.pageInfo.hasPreviousPage) break; | |
| before = comments.pageInfo.startCursor; | |
| } | |
| if (!report || report.isMinimized) { | |
| await github.rest.issues.createComment({ | |
| request: { retries: 0 }, | |
| ...context.repo, | |
| issue_number: issue.number, | |
| body: | |
| `Error: @${comment.user.login}, no visible matching SASS report precedes your approval command. ` + | |
| "Review the current SASS report, make sure it is visible, then post " + | |
| `\`/sass diff ok ${process.env.SHA}\` again.`, | |
| }); | |
| core.setFailed("No visible matching report precedes the approval command."); | |
| return; | |
| } | |
| core.info(`Matching SASS report: comment ${report.fullDatabaseId}, SHA ${process.env.SHA}.`); | |
| const fs = require("fs"); | |
| fs.writeFileSync(`${process.env.RUNNER_TEMP}/sass-approval`, ""); | |
| core.setOutput("accepted", true); | |
| core.info(`Recorded approval by @${comment.user.login} for report ${report.fullDatabaseId}.`); | |
| # Upload before the rerun so the job can read the validated approval immediately. | |
| - name: Upload approval | |
| if: steps.approval.outputs.accepted == 'true' | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: sass-approval-pr-${{ github.event.issue.number }}-${{ steps.request.outputs.sha }} | |
| path: ${{ runner.temp }}/sass-approval | |
| if-no-files-found: error | |
| retention-days: 5 | |
| overwrite: true | |
| - name: Rerun the SASS approval job | |
| if: steps.approval.outputs.accepted == 'true' | |
| uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 | |
| env: | |
| JOB_ID: ${{ steps.job.outputs.job_id }} | |
| with: | |
| retries: 5 | |
| script: | | |
| const { comment } = context.payload; | |
| const job_id = Number(process.env.JOB_ID); | |
| // GitHub also reruns dependent jobs, including the CI branch protection job. | |
| core.info(`Request a rerun of SASS approval job ${job_id} and its dependent jobs.`); | |
| try { | |
| await github.rest.actions.reRunJobForWorkflowRun({ | |
| request: { retries: 0 }, | |
| ...context.repo, | |
| job_id, | |
| }); | |
| } catch (error) { | |
| if (error.status === 403) { | |
| // The artifact records approval even when GitHub rejects the rerun. | |
| core.info("CI is currently in progress, so rerun request returned 403. User will manually have to rerun CI once it finishes."); | |
| await github.rest.issues.createComment({ | |
| request: { retries: 0 }, | |
| ...context.repo, | |
| issue_number: context.payload.issue.number, | |
| body: | |
| `@${comment.user.login}, your SASS approval is recorded, but GitHub rejects CI rerun requests while it is running. ` + | |
| "After the current CI run finishes, rerun its failed jobs to apply the approval.", | |
| }); | |
| return; | |
| } | |
| throw error; | |
| } | |
| core.info(`Requested SASS approval and CI reruns for comment ${comment.id}.`); | |
| - name: Acknowledge successful command | |
| if: success() && (steps.approval.outputs.accepted == 'true' || steps.job.outputs.ci_passed == 'true') | |
| uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 | |
| with: | |
| retries: 5 | |
| script: | | |
| const { comment } = context.payload; | |
| await github.rest.reactions.createForIssueComment({ | |
| request: { retries: 0 }, | |
| ...context.repo, | |
| comment_id: comment.id, | |
| content: "+1", | |
| }); | |
| core.info(`Acknowledged command ${comment.id}.`); |