feat(ci): onboard GB200 AWS UAT reservation #460
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
| # Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. | |
| # | |
| # Licensed under the Apache License, Version 2.0 (the "License"); | |
| # you may not use this file except in compliance with the License. | |
| # You may obtain a copy of the License at | |
| # | |
| # http://www.apache.org/licenses/LICENSE-2.0 | |
| # | |
| # Unless required by applicable law or agreed to in writing, software | |
| # distributed under the License is distributed on an "AS IS" BASIS, | |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| # See the License for the specific language governing permissions and | |
| # limitations under the License. | |
| # Lets contributors self-serve issue assignment by commenting on an issue: | |
| # /assign -> claims the issue (commenter), only if unassigned | |
| # /assign @user ... -> assigns the mentioned user(s), only if unassigned | |
| # /unassign -> releases the issue, only if assigned to the commenter | |
| # | |
| # Single-owner model: /assign is refused when the issue already has an | |
| # assignee (comment /unassign to release it first). /unassign only ever | |
| # removes the commenter and is refused when they are not currently | |
| # assigned, so nobody can drop another person's claim. | |
| # | |
| # issue_comment always runs in the base-repo context with the base-repo | |
| # token, so there is no fork/pwn-request exposure here: the job only needs | |
| # `issues: write` and never checks out untrusted code. GitHub's | |
| # addAssignees silently ignores users who are not assignable (no triage/ | |
| # write access and no prior repo activity); we detect and report those. | |
| name: Self-Assign Issues | |
| on: | |
| issue_comment: | |
| types: [created] | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.event.issue.number }} | |
| cancel-in-progress: false | |
| jobs: | |
| assign: | |
| name: Handle /assign and /unassign | |
| # Issues only (not PRs); comment starts with /assign or /unassign. | |
| if: >- | |
| !github.event.issue.pull_request && | |
| (startsWith(github.event.comment.body, '/assign') || | |
| startsWith(github.event.comment.body, '/unassign')) | |
| runs-on: ubuntu-latest | |
| permissions: | |
| issues: write | |
| timeout-minutes: 5 | |
| steps: | |
| - name: Apply assignment change | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| with: | |
| script: | | |
| const body = context.payload.comment.body.trim(); | |
| const commenter = context.payload.comment.user.login; | |
| // /unassign must be checked first: startsWith('/assign') would | |
| // not match it, but keep the order explicit for clarity. | |
| const unassign = body.startsWith('/unassign'); | |
| // Assignees as of the triggering event. Good enough for gating; | |
| // concurrent /assign comments are a benign, rare race. | |
| const current = (context.payload.issue.assignees || []).map(a => a.login); | |
| const common = { | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.issue.number, | |
| }; | |
| if (unassign) { | |
| // Self-only: a commenter may only release their own claim. | |
| if (!current.includes(commenter)) { | |
| core.info(`${commenter} is not assigned; nothing to unassign`); | |
| await github.rest.issues.createComment({ | |
| ...common, | |
| body: `@${commenter} you are not currently assigned to this issue, so there is nothing to release.`, | |
| }); | |
| return; | |
| } | |
| await github.rest.issues.removeAssignees({ | |
| ...common, | |
| assignees: [commenter], | |
| }); | |
| core.info(`Unassigned ${commenter}`); | |
| return; | |
| } | |
| // Assign only on an unassigned issue (single-owner model). | |
| if (current.length) { | |
| core.info(`Issue already assigned to ${current.join(', ')}; refusing`); | |
| await github.rest.issues.createComment({ | |
| ...common, | |
| body: [ | |
| `This issue is already assigned to ${current.map(u => '@' + u).join(', ')}.`, | |
| 'The assignee can comment `/unassign` to release it first.', | |
| ].join('\n'), | |
| }); | |
| return; | |
| } | |
| // "@user" mentions anywhere in the comment; fall back to the | |
| // commenter when none are given (self claim). | |
| const mentioned = [...body.matchAll(/@([a-zA-Z0-9](?:[a-zA-Z0-9-]{0,37}[a-zA-Z0-9])?)/g)].map(m => m[1]); | |
| const targets = mentioned.length ? [...new Set(mentioned)] : [commenter]; | |
| const { data: issue } = await github.rest.issues.addAssignees({ | |
| ...common, | |
| assignees: targets, | |
| }); | |
| // GitHub drops non-assignable users without erroring — surface them. | |
| const got = new Set(issue.assignees.map(a => a.login)); | |
| const skipped = targets.filter(u => !got.has(u)); | |
| if (skipped.length) { | |
| core.warning(`Could not assign: ${skipped.join(', ')}`); | |
| await github.rest.issues.createComment({ | |
| ...common, | |
| body: [ | |
| `Could not assign ${skipped.map(u => '@' + u).join(', ')}.`, | |
| '', | |
| 'GitHub only allows assigning users with triage/write access or', | |
| 'prior activity in this repository.', | |
| ].join('\n'), | |
| }); | |
| } |