From a49a712c31052e367aee5c52ead00f56b765ef87 Mon Sep 17 00:00:00 2001 From: Aryan Gorwade Date: Tue, 3 Nov 2026 15:53:50 -0800 Subject: [PATCH] Introduce triage system Signed-off-by: Aryan Gorwade --- .github/ISSUE_TEMPLATE/bug_report.md | 16 +++- .github/ISSUE_TEMPLATE/config.yml | 7 ++ .github/ISSUE_TEMPLATE/feature_request.md | 10 ++- .github/ISSUE_TEMPLATE/question.md | 8 -- .github/PULL_REQUEST_TEMPLATE.md | 6 +- .github/workflows/assign.yaml | 98 +++++++++++++++++++++++ .github/workflows/issue-state-labels.yaml | 62 ++++++++++++++ .github/workflows/issue-triage.yaml | 45 +++++++++++ .github/workflows/stale.yaml | 17 +++- AGENTS.md | 9 ++- CONTRIBUTING.md | 55 +++++++++++-- GOVERNANCE.md | 7 +- README.md | 3 +- 13 files changed, 311 insertions(+), 32 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/question.md create mode 100644 .github/workflows/assign.yaml create mode 100644 .github/workflows/issue-state-labels.yaml create mode 100644 .github/workflows/issue-triage.yaml diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index afb9fd66a7..1e21c43510 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -6,16 +6,22 @@ labels: ["bug", "needs-triage"] --- -_**Important Note: NVIDIA AI Enterprise customers can get support from NVIDIA Enterprise support. Please open a case [here](https://enterprise-support.nvidia.com/s/create-case)**._ + + +**Motivation** + + +**Check here if you would like to work on this issue** +- [ ] **Describe the bug** -A clear and concise description of what the bug is. + **To Reproduce** -Detailed steps to reproduce the issue. + **Expected behavior** -A clear and concise description of what you expected to happen. + **Environment (please provide the following information):** - GPU Operator Version: [e.g. v25.3.0] @@ -36,6 +42,7 @@ A clear and concise description of what you expected to happen. - [ ] containerd logs `journalctl -u containerd > containerd.log` + diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 3ba13e0cec..35eb7977cf 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1 +1,8 @@ blank_issues_enabled: false +contact_links: + - name: Question / General Discussion + url: https://github.com/NVIDIA/gpu-operator/discussions + about: Ask a question or raise a discussion related to GPU Operator + - name: Community Slack (#gpu-operator) + url: https://inviter.co/dsx-ai-factory + about: Chat with the GPU Operator team and community diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index a0c4b594b2..a3908c0e96 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -2,7 +2,13 @@ name: Feature request about: Submit a feature request title: "[Feature]: " -labels: ["feature", "needs-triage", "lifecycle/frozen"] +labels: ["feature", "needs-triage"] --- +**Motivation** + - +**Check here if you would like to work on this issue** +- [ ] + +**Describe the feature** + diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md deleted file mode 100644 index 1ae7b0820f..0000000000 --- a/.github/ISSUE_TEMPLATE/question.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: Question / General Discussion -about: Ask a question or raise a discussion -title: "[Question]: " -labels: ["question"] ---- - - diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 0e7c3e22ab..6689eac68d 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,6 +1,10 @@ +## Issue + +Fixes # + ## Description - + ## Checklist diff --git a/.github/workflows/assign.yaml b/.github/workflows/assign.yaml new file mode 100644 index 0000000000..c50e428381 --- /dev/null +++ b/.github/workflows/assign.yaml @@ -0,0 +1,98 @@ +# Copyright NVIDIA CORPORATION +# +# 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. + +name: Assign + +on: + issue_comment: + types: [created] + +permissions: {} + +jobs: + assign: + name: Assign issue from /assign comment + if: ${{ !github.event.issue.pull_request && startsWith(github.event.comment.body, '/assign') }} + runs-on: ubuntu-latest + timeout-minutes: 5 + permissions: + issues: write + steps: + - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9 + with: + script: | + const { owner, repo } = context.repo; + const { comment, issue } = context.payload; + const react = (content) => + github.rest.reactions.createForIssueComment({ owner, repo, comment_id: comment.id, content }); + + const firstLine = comment.body.split(/\r?\n/, 1)[0].trim(); + if (!/^\/assign(\s|$)/.test(firstLine)) { + return; + } + + // Matches GitHub's own rule: assigning requires the triage role or higher. + const { data: perm } = await github.rest.repos.getCollaboratorPermissionLevel({ + owner, + repo, + username: comment.user.login, + }); + const canAssign = ['admin', 'maintain', 'write', 'triage'].includes(perm.role_name) + || ['admin', 'write'].includes(perm.permission); + if (!canAssign) { + return react('-1'); + } + + const match = firstLine.match(/^\/assign(?:\s+@?([A-Za-z0-9-]+))?$/); + if (!match || !issue.labels.some((label) => label.name === 'accepted')) { + return react('confused'); + } + + const assignee = match[1] ?? comment.user.login; + const { data: updated } = await github.rest.issues.addAssignees({ + owner, + repo, + issue_number: issue.number, + assignees: [assignee], + }); + // GitHub silently skips users it cannot assign, such as people who have not commented on the issue. + if (!updated.assignees.some((a) => a.login.toLowerCase() === assignee.toLowerCase())) { + return react('confused'); + } + + // The PR gatekeeper accepts any assignee, so a new claimant must replace the previous ones. + // Maintainers stay assigned because the gatekeeper does not apply to them. + const previousClaimants = []; + for (const { login } of updated.assignees) { + if (login.toLowerCase() === assignee.toLowerCase()) { + continue; + } + const { data: level } = await github.rest.repos.getCollaboratorPermissionLevel({ + owner, + repo, + username: login, + }); + if (!['admin', 'maintain', 'write'].includes(level.permission)) { + previousClaimants.push(login); + } + } + if (previousClaimants.length > 0) { + await github.rest.issues.removeAssignees({ + owner, + repo, + issue_number: issue.number, + assignees: previousClaimants, + }); + } + return react('+1'); diff --git a/.github/workflows/issue-state-labels.yaml b/.github/workflows/issue-state-labels.yaml new file mode 100644 index 0000000000..b034465500 --- /dev/null +++ b/.github/workflows/issue-state-labels.yaml @@ -0,0 +1,62 @@ +# Copyright NVIDIA CORPORATION +# +# 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. + +name: Issue State Labels + +on: + issues: + types: [labeled] + +permissions: {} + +concurrency: + group: issue-state-${{ github.event.issue.number }} + +jobs: + single-state: + name: Keep one state label per issue + if: contains(fromJSON('["needs-triage","needs-more-info","accepted"]'), github.event.label.name) + runs-on: ubuntu-latest + timeout-minutes: 5 + permissions: + issues: write + steps: + - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9 + with: + script: | + const stateLabels = ['needs-triage', 'needs-more-info', 'accepted']; + const { owner, repo } = context.repo; + const issue_number = context.payload.issue.number; + const added = context.payload.label.name; + if (!stateLabels.includes(added)) { + return; + } + + const { data: labels } = await github.rest.issues.listLabelsOnIssue({ owner, repo, issue_number }); + const current = labels.map((label) => label.name); + // Adding two state labels at once starts two runs; a later run must not undo an earlier one. + if (!current.includes(added)) { + return; + } + + for (const name of current.filter((label) => stateLabels.includes(label) && label !== added)) { + try { + await github.rest.issues.removeLabel({ owner, repo, issue_number, name }); + core.info(`Removed ${name}: ${added} replaces it.`); + } catch (error) { + if (error.status !== 404) { + throw error; + } + } + } diff --git a/.github/workflows/issue-triage.yaml b/.github/workflows/issue-triage.yaml new file mode 100644 index 0000000000..93b683ff00 --- /dev/null +++ b/.github/workflows/issue-triage.yaml @@ -0,0 +1,45 @@ +# Copyright NVIDIA CORPORATION +# +# 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. + +name: Issue Triage + +on: + issues: + types: [opened] + +permissions: {} + +jobs: + label: + name: Label new issues for triage + runs-on: ubuntu-latest + timeout-minutes: 5 + permissions: + issues: write + steps: + # Issue templates apply needs-triage, but issues created through the API skip templates. + - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9 + with: + script: | + const stateLabels = ['needs-triage', 'needs-more-info', 'accepted']; + const labels = context.payload.issue.labels.map((label) => label.name); + if (labels.some((label) => stateLabels.includes(label))) { + return; + } + await github.rest.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.issue.number, + labels: ['needs-triage'], + }); diff --git a/.github/workflows/stale.yaml b/.github/workflows/stale.yaml index d732fb40a2..daeb5c6ffb 100644 --- a/.github/workflows/stale.yaml +++ b/.github/workflows/stale.yaml @@ -20,9 +20,24 @@ jobs: with: stale-issue-message: 'This issue is stale because it has been open 90 days with no activity. This issue will be closed in 30 days unless new comments are made or the stale label is removed. To skip these checks, apply the "lifecycle/frozen" label.' stale-issue-label: 'lifecycle/stale' - exempt-issue-labels: 'lifecycle/frozen,feature,enhancement' + exempt-issue-labels: 'lifecycle/frozen,feature,enhancement,accepted' days-before-stale: 90 close-issue-message: 'This issue was automatically closed due to inactivity.' days-before-issue-close: 30 remove-stale-when-updated: true operations-per-run: 1000 + - uses: actions/stale@4391f3da665fdf50b6810c1a66712fb9ba21aa93 # v11 + with: + only-issue-labels: 'needs-more-info' + # Triage policy start date. + start-date: '2026-10-27T00:00:00Z' + stale-issue-message: 'This issue has been waiting for more information for 23 days with no activity. It will be closed in 7 days unless the requested information is provided.' + stale-issue-label: 'lifecycle/stale' + exempt-issue-labels: 'lifecycle/frozen' + days-before-issue-stale: 23 + close-issue-message: 'This issue was automatically closed because the requested information was not provided within 30 days.' + days-before-issue-close: 7 + days-before-pr-stale: -1 + days-before-pr-close: -1 + remove-stale-when-updated: true + operations-per-run: 1000 diff --git a/AGENTS.md b/AGENTS.md index e7339876dc..4993c90afd 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -88,14 +88,15 @@ the CRDs in the helm chart and OLM bundle are in-sync. ## Contribution process (see `CONTRIBUTING.md`) -- For any significant change (architectural change, new feature, breaking change, non-trivial - bug fix), an issue should exist describing the problem/proposal before implementation begins — - check for or ask about a linked issue rather than assuming a PR alone is sufficient. +- For any change, an issue should exist describing the problem/proposal before implementation begins. Additionally, the contributor should be approved to work on said issue — ensure the PR is linked to an issue the contributor has been assigned to. +- Keep PR titles short and imperative. +- Issue or PR descriptions drafted with AI must be thoroughly reviewed and verified by the user before they are submitted. +- Replies to maintainers in review comments or issue discussions may not be AI generated. - All commits must be signed off (DCO): `git commit -s`, producing a trailing `Signed-off-by: Name ` line. Do not fabricate a sign-off identity — use the configured git user's identity. - Do not open, push to, or comment on GitHub issues/PRs without explicit user confirmation. -- Keep PR titles short and imperative; the body should explain motivation ("why"), not just restate the diff. +- Keep all changes as minimal as possible. ## Things to avoid diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c920feba5d..6cb1142669 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -77,18 +77,61 @@ can be viewed [here](https://docs.nvidia.com/datacenter/cloud-native/gpu-operato ## Contributions NVIDIA is willing to work with partners for adding platform support for the GPU Operator. The GPU Operator is open-source and permissively licensed under the Apache 2.0 license with only minimal requirements for source code [contributions](#signing). -To file feature requests, bugs, or questions, submit an issue at https://github.com/NVIDIA/gpu-operator/issues +To file feature requests or bugs, submit an issue at https://github.com/NVIDIA/gpu-operator/issues. For questions, start a discussion at https://github.com/NVIDIA/gpu-operator/discussions or ask in the `#gpu-operator` channel in our community Slack at https://inviter.co/dsx-ai-factory. -To contribute to the project, file a Pull Request at https://github.com/NVIDIA/gpu-operator/pulls. Contributions do not require explicit contributor license agreements (CLA), but we expect contributors to [sign](#signing) their work. - -Before beginning implementation or opening a Pull Request for a significant change, first open an issue describing the problem or proposal. -A significant change includes, but is not limited to, architectural changes, new features, breaking changes to API or behavior, and non-trivial bug fixes. +All external contributions start with an issue. External contributors must have an accepted issue that is assigned to them before they open a pull request. See [Contribution workflow](#contribution-workflow). Contributions do not require explicit contributor license agreements (CLA), but we expect contributors to [sign](#signing) their work. Reviewers are automatically assigned to Pull Requests from the [OWNERS](CODEOWNERS) file. -The approval / decision-making process is documented in [GOVERNANCE.md](GOVERNANCE.md) +PR approval and decision-making for major changes are documented in [GOVERNANCE.md](GOVERNANCE.md). All contributions must adhere to the [Code of Conduct](CODE_OF_CONDUCT.md). +### AI Usage +AI-assisted contributions are welcome, however, **you must understand your code**. If you can't explain what your changes do and how they interact with the greater system without the aid of AI tools, your contribution may not be accepted. AI should assist your effort, not replace it or shift the burden of understanding, testing, and reviewing onto maintainers. + +The human contributor is accountable for all code, regardless of how much was AI-generated. Each contribution must name the human behind the change via a [DCO sign-off](#signing). + +Maintainers may close issues and PRs that are unproductive or low-quality, without explanation, regardless of whether AI tools were used or not. If a contributor repeatedly opens unproductive issues or PRs, they may be blocked from contributing to the project because it is disruptive and disrespectful of the maintainers' time. + +Below are some unacceptable usages of AI that will likely result in your contribution being closed: + +- Submitting AI-generated issues or PRs without thorough review and verification +- Submitting AI-generated code that you cannot explain or answer questions about during review +- Using AI to generate replies to review comments or issue discussions +- Submitting AI-generated code that contains unnecessary refactoring or unrelated changes not scoped to the problem the PR is solving + +### Contribution workflow + +**To propose a new issue:** start at step 1.\ +**To work on an existing issue:** skip to step 3 and [claim an `accepted` issue](https://github.com/NVIDIA/gpu-operator/issues?q=is%3Aopen+label%3Aaccepted+no%3Aassignee) with no assignees. + +1. **Open an issue.** Use the bug report template to report a defect, or the feature request template to propose new behavior or a [major change](GOVERNANCE.md#core-decisions). Indicate if you want to work on the issue. Each new issue gets the `needs-triage` label. +2. **Wait for triage.** A maintainer reviews the issue against the [triage criteria](#triage-criteria). The maintainer then labels the issue `accepted`, `needs-more-info`, or closes it. +3. **Claim the issue.** When an issue is `accepted`, comment on it expressing interest and summarizing your proposed solution. Issue authors have priority if they indicate they would like to work on it. Other claims are first-come, first-served. A maintainer discusses your plan with you and assigns the issue to you when the plan is ready. +4. **Open a pull request.** File a pull request at https://github.com/NVIDIA/gpu-operator/pulls and link the issue in the description (for example, `Fixes #1234`). Maintainers close pull requests that do not link an issue assigned to the author, and pull requests that are duplicates of existing ones. + +### Issue labels + +Each open issue has one state label and one category label. For example, `needs-triage` and `bug` together identify a bug report that no maintainer has reviewed yet. + +| Label | Type | Meaning | +| ----- | ---- | ------- | +| `needs-triage` | State | No maintainer has reviewed the issue yet. | +| `needs-more-info` | State | Maintainers are waiting for more information from the author. If the issue has no activity for 30 days, it is closed. | +| `accepted` | State | The issue is approved for work. Contributors can claim it. | +| `bug` | Category | A defect in existing behavior. | +| `feature` | Category | A request for new behavior. | + +### Triage criteria + +Maintainers use these questions to triage an issue: + +- Is the problem clear and reproducible? +- Is the issue a duplicate, or does other work already address it? +- Does the issue fit the supported use cases and the project priorities? +- Is the proposed approach appropriate? +- Does the issue align with the product roadmap? + ## Development Environment Setup ### Prerequisites diff --git a/GOVERNANCE.md b/GOVERNANCE.md index 1ee1775bfc..ba0ff73ea8 100644 --- a/GOVERNANCE.md +++ b/GOVERNANCE.md @@ -48,9 +48,8 @@ report issues, and engage in discussions. ### Routine Decisions -Routine decisions — bug fixes, minor features, documentation, dependency updates — proceed -via a PR. At least one maintainer approval is required for technical acceptance; a maintainer -then merges after verifying project requirements are met. +Routine decisions — bug fixes, minor features, documentation, dependency updates — proceed via a PR. External contributions first follow the contribution workflow referenced in [CONTRIBUTING.md](CONTRIBUTING.md), which requires maintainer approval on an issue before implementation begins. +A maintainer then approves and merges PRs after verifying project requirements are met. ### Core Decisions @@ -68,7 +67,7 @@ considered, and the impact on existing contributors and users. Core decisions are made by the maintainers. For contributors who want to propose and contribute a major change, the process is as follows: -- A proposal for a major change will be submitted as an issue. +- A proposal for a major change will be submitted as a [feature request](.github/ISSUE_TEMPLATE/feature_request.md). - The maintainers will discuss the proposal and seek feedback from the community. - Once the discussion is concluded, maintainers will make the final decision. diff --git a/README.md b/README.md index 97a0a81b2e..10d887f398 100644 --- a/README.md +++ b/README.md @@ -60,8 +60,7 @@ For information on platform support and getting started, visit the official docu ## Contributions [Read the document on contributions](CONTRIBUTING.md). -You can contribute by opening a [pull request](https://help.github.com/en/articles/about-pull-requests). Contributions must adhere to the [Code of Conduct](CODE_OF_CONDUCT.md). ## Support and Getting Help -Please open [an issue on the GitHub project](https://github.com/NVIDIA/gpu-operator/issues/new) for any questions. Your feedback is appreciated. +Please start [a discussion on the GitHub project](https://github.com/NVIDIA/gpu-operator/discussions) for any questions. You can also join our [community Slack](https://inviter.co/dsx-ai-factory). Your feedback is appreciated.