Skip to content

Commit d5a18d9

Browse files
committed
ci: make CI and hook configuration self-contained
Decentralized alternative: every setting lives in this repository with no references to shared org repositories. - .pre-commit-config.yaml: local add-license hook backed by the in-repo tools/add_copyright.py (hardened: LICENSE files read-only with a staleness year gate, .github templates excluded, double-header check covers both copyright forms); flake8 args quoted correctly; no external hook repositories beyond the standard public ones. - conventional-pr workflow: full validation/labeling logic inlined - PR title gate, per-type human-readable labels with enforced colors, cherry-pick detection, maintainer skip-title-check override, fork-PR support via guarded pull_request_target. - pre-commit workflow: robust modified-files runner (null-delimited, deletion-safe, cached). - PR templates kept in-repo: internal drops the manual Commit Type checklist (automated by the workflow); external keeps it per review feedback, gains the chore type and an absolute contributing link. TRI-1100
1 parent d1e6320 commit d5a18d9

11 files changed

Lines changed: 761 additions & 196 deletions

File tree

.github/PULL_REQUEST_TEMPLATE/pull_request_template_external_contrib.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<!-- Describe your pull request here. Please read the text below the line, and make sure you follow the checklist.-->
33

44
#### Checklist
5-
- [ ] I have read the [Contribution guidelines](#../../CONTRIBUTING.md) and signed the [Contributor License
5+
- [ ] I have read the [Contribution guidelines](https://github.com/triton-inference-server/server/blob/main/CONTRIBUTING.md) and signed the [Contributor License
66
Agreement](https://github.com/NVIDIA/triton-inference-server/blob/master/Triton-CCLA-v1.pdf)
77
- [ ] PR title reflects the change and is of format `<commit_type>: <Title>`
88
- [ ] Changes are described in the pull request.
@@ -20,6 +20,7 @@ Agreement](https://github.com/NVIDIA/triton-inference-server/blob/master/Triton-
2020
Check the [conventional commit type](https://github.com/angular/angular/blob/22b96b9/CONTRIBUTING.md#type)
2121
box here and add the label to the github PR.
2222
- [ ] build
23+
- [ ] chore
2324
- [ ] ci
2425
- [ ] docs
2526
- [ ] feat
Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
# Copyright 2023-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
#
3+
# Redistribution and use in source and binary forms, with or without
4+
# modification, are permitted provided that the following conditions
5+
# are met:
6+
# * Redistributions of source code must retain the above copyright
7+
# notice, this list of conditions and the following disclaimer.
8+
# * Redistributions in binary form must reproduce the above copyright
9+
# notice, this list of conditions and the following disclaimer in the
10+
# documentation and/or other materials provided with the distribution.
11+
# * Neither the name of NVIDIA CORPORATION nor the names of its
12+
# contributors may be used to endorse or promote products derived
13+
# from this software without specific prior written permission.
14+
#
15+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
16+
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18+
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
19+
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20+
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21+
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22+
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23+
# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26+
27+
# Self-contained conventional-commit enforcement for this repository:
28+
# validates the PR title (<type>: <Title>), applies one human-readable label
29+
# per type found in the title and conforming commit subjects (org color
30+
# scheme), detects cherry-picks, and honors the maintainer-applied
31+
# skip-title-check override label.
32+
33+
name: conventional-pr
34+
35+
on:
36+
pull_request:
37+
types: [opened, edited, synchronize, reopened, labeled, unlabeled]
38+
pull_request_target:
39+
types: [opened, edited, synchronize, reopened, labeled, unlabeled]
40+
41+
jobs:
42+
validate-and-label:
43+
# Same-repo PRs run on the safe pull_request event; fork PRs run on
44+
# pull_request_target so the labeling job gets a writable token (safe:
45+
# this job never checks out PR code).
46+
if: >-
47+
(github.event_name == 'pull_request' &&
48+
github.event.pull_request.head.repo.full_name == github.repository) ||
49+
(github.event_name == 'pull_request_target' &&
50+
github.event.pull_request.head.repo.full_name != github.repository)
51+
runs-on: ubuntu-latest
52+
permissions:
53+
pull-requests: write
54+
issues: write
55+
contents: read
56+
steps:
57+
- name: Validate PR title, apply type labels, detect cherry-picks
58+
uses: actions/github-script@v7
59+
with:
60+
script: |
61+
// Conventional-commit type -> human-readable label, org-wide color,
62+
// and description. Titles are validated against the type tokens;
63+
// the friendlier label is what gets applied.
64+
const TYPES = {
65+
build: { label: 'build', color: 'c5def5',
66+
description: 'Build system or external dependencies (build: PRs)' },
67+
chore: { label: 'chore', color: 'fef2c0',
68+
description: 'Maintenance work, no production code change (chore: PRs)' },
69+
ci: { label: 'CI/CD', color: 'bfd4f2',
70+
description: 'Continuous integration and workflow changes (ci: PRs)' },
71+
docs: { label: 'documentation', color: '0075ca',
72+
description: 'Improvements or additions to documentation (docs: PRs)' },
73+
feat: { label: 'feature', color: 'a2eeef',
74+
description: 'New feature or capability (feat: PRs)' },
75+
fix: { label: 'fix', color: 'd73a4a',
76+
description: 'Bug fix (fix: PRs)' },
77+
perf: { label: 'performance', color: 'fbca04',
78+
description: 'Performance improvement (perf: PRs)' },
79+
refactor: { label: 'refactor', color: '5319e7',
80+
description: 'Code change that neither fixes a bug nor adds a feature (refactor: PRs)' },
81+
revert: { label: 'revert', color: 'b60205',
82+
description: 'Reverts a previous change (revert: PRs)' },
83+
style: { label: 'code style', color: 'f9d0c4',
84+
description: 'Formatting and style-only changes (style: PRs)' },
85+
test: { label: 'testing', color: '1d76db',
86+
description: 'Adding or correcting tests (test: PRs)' },
87+
};
88+
const CHERRY = { label: 'cherry-pick', color: '006b75',
89+
description: 'Cherry-picked from another branch' };
90+
const types = Object.keys(TYPES);
91+
const typeLabels = Object.values(TYPES).map((t) => t.label);
92+
const { owner, repo } = context.repo;
93+
const number = context.payload.pull_request.number;
94+
95+
// Create the label if missing, otherwise enforce the org color and
96+
// description.
97+
async function ensureLabel({ label: name, color, description }) {
98+
try {
99+
await github.rest.issues.createLabel(
100+
{ owner, repo, name, color, description });
101+
} catch (e) {
102+
if (e.status !== 422) throw e; // 422 = already exists
103+
const { data } = await github.rest.issues.getLabel({ owner, repo, name });
104+
if (data.color.toLowerCase() !== color ||
105+
(data.description || '') !== description) {
106+
await github.rest.issues.updateLabel(
107+
{ owner, repo, name, color, description });
108+
}
109+
}
110+
}
111+
112+
// Maintainer override: a human-applied skip-title-check label lets
113+
// a non-conventional title pass (with a prominent warning). Ensured
114+
// here so it is discoverable in every repo's label picker.
115+
const OVERRIDE = { label: 'skip-title-check', color: 'e4e669',
116+
description: 'Maintainer override: conventional-title check bypassed' };
117+
await ensureLabel(OVERRIDE);
118+
const { data: current } = await github.rest.issues.listLabelsOnIssue(
119+
{ owner, repo, issue_number: number });
120+
const overridden = current.some((l) => l.name === OVERRIDE.label);
121+
122+
// 1. Validate the PR title (the squash-merge commit title).
123+
const title = context.payload.pull_request.title;
124+
const match = title.match(/^(\w+)(\([^)]*\))?!?: .+/);
125+
const titleValid = match !== null && types.includes(match[1]);
126+
if (!titleValid && !overridden) {
127+
core.setFailed(
128+
`PR title "${title}" does not follow the Conventional Commits ` +
129+
`format "<type>: <Title>" with type one of: ${types.join(', ')}. ` +
130+
'See https://www.conventionalcommits.org/ - or a maintainer can ' +
131+
`apply the "${OVERRIDE.label}" label to override.`);
132+
return;
133+
}
134+
if (!titleValid) {
135+
core.warning(
136+
`PR title "${title}" is not a conventional commit; passing only ` +
137+
`because the "${OVERRIDE.label}" label was applied by a maintainer. ` +
138+
'Consider fixing the title before squash-merging.');
139+
}
140+
141+
// 2. Derive the full type set: the title plus every conforming
142+
// commit subject in the PR (merge commits are ignored).
143+
const commits = await github.paginate(github.rest.pulls.listCommits,
144+
{ owner, repo, pull_number: number, per_page: 100 });
145+
const derived = new Set(titleValid ? [match[1]] : []);
146+
for (const c of commits) {
147+
const subject = c.commit.message.split('\n', 1)[0];
148+
if (/^Merge /.test(subject)) continue;
149+
const m = subject.match(/^(\w+)(\([^)]*\))?!?: .+/);
150+
if (m && types.includes(m[1])) derived.add(m[1]);
151+
else core.warning(`Commit ${c.sha.slice(0, 9)} subject is not ` +
152+
`a conventional commit: "${subject}"`);
153+
}
154+
155+
// Fail-safe: no derivable type, no manually assigned type label,
156+
// and no maintainer override.
157+
if (derived.size === 0 && !overridden &&
158+
!current.some((l) => typeLabels.includes(l.name))) {
159+
core.setFailed(
160+
'No conventional commit type could be determined from the PR ' +
161+
'title or its commits, and no type label is assigned.');
162+
return;
163+
}
164+
165+
// 3. Apply one label per derived type; drop stale auto-managed
166+
// labels (incl. legacy raw-token names from earlier versions).
167+
const wanted = [...derived].map((t) => TYPES[t].label);
168+
for (const t of derived) await ensureLabel(TYPES[t]);
169+
for (const l of current) {
170+
if ((typeLabels.includes(l.name) || types.includes(l.name)) &&
171+
!wanted.includes(l.name)) {
172+
await github.rest.issues.removeLabel(
173+
{ owner, repo, issue_number: number, name: l.name });
174+
}
175+
}
176+
if (wanted.length > 0) {
177+
await github.rest.issues.addLabels(
178+
{ owner, repo, issue_number: number, labels: wanted });
179+
core.info(`Labeled: ${wanted.join(', ')}.`);
180+
}
181+
182+
// 4. Cherry-pick detection: the standard `git cherry-pick -x`
183+
// trailer in any commit, an explicit hint in the title/branch, or
184+
// a trailing "(#N)" squash-merge reference to a DIFFERENT PR (the
185+
// signature of cherry-picking an already squash-merged commit).
186+
// Additive only - a manually applied label is never removed.
187+
const trailer = /cherry[- ]?picked from commit [0-9a-f]{7,40}/i;
188+
const hint = /cherry[- ]?pick/i;
189+
const prRef = /\(#(\d+)\)$/;
190+
const refersToOtherPr = (subject) => {
191+
const m = subject.trim().match(prRef);
192+
return m !== null && Number(m[1]) !== number;
193+
};
194+
const isCherryPick =
195+
commits.some((c) => trailer.test(c.commit.message)) ||
196+
hint.test(title) ||
197+
hint.test(context.payload.pull_request.head.ref) ||
198+
refersToOtherPr(title) ||
199+
commits.some((c) => refersToOtherPr(c.commit.message.split('\n', 1)[0]));
200+
if (isCherryPick) {
201+
await ensureLabel(CHERRY);
202+
await github.rest.issues.addLabels(
203+
{ owner, repo, issue_number: number, labels: [CHERRY.label] });
204+
core.info('Cherry-pick detected; labeled as "cherry-pick".');
205+
}

.github/workflows/pre-commit.yml

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright 2023-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
1+
# Copyright 2023-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
22
#
33
# Redistribution and use in source and binary forms, with or without
44
# modification, are permitted provided that the following conditions
@@ -32,14 +32,23 @@ on:
3232
jobs:
3333
pre-commit:
3434
runs-on: ubuntu-latest
35+
permissions:
36+
contents: read
3537
steps:
3638
- uses: actions/checkout@v5.0.0
3739
with:
3840
fetch-depth: 2
39-
- name: Get modified files
40-
id: modified-files
41-
run: echo "modified_files=$(git diff --name-only -r HEAD^1 HEAD | xargs)" >> $GITHUB_OUTPUT
4241
- uses: actions/setup-python@v6.0.0
43-
- uses: pre-commit/action@v3.0.1
42+
- uses: actions/cache@v4
4443
with:
45-
extra_args: --files ${{ steps.modified-files.outputs.modified_files }}
44+
path: ~/.cache/pre-commit
45+
key: pre-commit-${{ runner.os }}-${{ hashFiles('.pre-commit-config.yaml') }}
46+
- name: Run pre-commit on the files modified by the PR
47+
# Null-delimited so paths with spaces survive; --no-run-if-empty
48+
# handles deletion-only PRs; deleted paths are filtered out before
49+
# being handed to pre-commit.
50+
run: |
51+
python -m pip install --quiet pre-commit
52+
git diff --name-only -z --diff-filter=d HEAD^1 HEAD \
53+
| xargs -0 --no-run-if-empty \
54+
pre-commit run --show-diff-on-failure --color=always --files

.pre-commit-config.yaml

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,28 @@
1-
# SPDX-FileCopyrightText: Copyright (c) 2023-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2-
# SPDX-License-Identifier: BSD-3-Clause
1+
# Copyright 2023-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
#
3+
# Redistribution and use in source and binary forms, with or without
4+
# modification, are permitted provided that the following conditions
5+
# are met:
6+
# * Redistributions of source code must retain the above copyright
7+
# notice, this list of conditions and the following disclaimer.
8+
# * Redistributions in binary form must reproduce the above copyright
9+
# notice, this list of conditions and the following disclaimer in the
10+
# documentation and/or other materials provided with the distribution.
11+
# * Neither the name of NVIDIA CORPORATION nor the names of its
12+
# contributors may be used to endorse or promote products derived
13+
# from this software without specific prior written permission.
14+
#
15+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
16+
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18+
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
19+
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20+
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21+
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22+
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23+
# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
326

427
repos:
528
- repo: https://github.com/PyCQA/isort
@@ -16,7 +39,7 @@ repos:
1639
rev: 7.3.0
1740
hooks:
1841
- id: flake8
19-
args: [--max-line-length=88, --select=C,E,F,W,B,B950, --extend-ignore = E203,E501]
42+
args: ["--max-line-length=88", "--select=C,E,F,W,B,B950", "--extend-ignore=E203,E501,W503"]
2043
types_or: [python, cython]
2144
- repo: https://github.com/pre-commit/mirrors-clang-format
2245
rev: v16.0.5
@@ -50,24 +73,15 @@ repos:
5073
- id: requirements-txt-fixer
5174
- id: trailing-whitespace
5275

53-
- repo: https://github.com/triton-inference-server/developer_tools
54-
rev: v0.1.0
55-
hooks:
56-
- id: add-license
5776
- repo: local
5877
hooks:
59-
# Incremental SPDX-header adoption: migrate each source file to the two-line
60-
# SPDX header the first time it is touched. pre-commit runs hooks only on the
61-
# files staged in a commit, so scoping by file type (below) -- not by directory
62-
# -- rolls SPDX out gradually as files change. The hook maintains an existing
63-
# SPDX header, replaces a legacy long-form NVIDIA BSD header in place, or
64-
# inserts one. It runs after add-license above, which only maintains the
65-
# copyright year on the string both headers share.
66-
- id: add-spdx-license
67-
name: Add SPDX License Header
68-
entry: python tools/add_spdx_header.py
78+
- id: add-license
79+
name: Add License
80+
entry: python tools/add_copyright.py
6981
language: python
70-
files: \.(py|pyi|sh|bash|yaml|yml|cc|cpp|cxx|h|hpp|cu|cuh)$
7182
stages: [pre-commit]
7283
verbose: true
7384
require_serial: true
85+
# GitHub issue/PR templates must start with YAML frontmatter; keep
86+
# license headers out of them. Workflows ARE processed.
87+
exclude: ^\.github/(ISSUE_TEMPLATE/|PULL_REQUEST_TEMPLATE|pull_request_template)

build.py

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,29 @@
11
#!/usr/bin/env python3
2-
# SPDX-FileCopyrightText: Copyright (c) 2020-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3-
# SPDX-License-Identifier: BSD-3-Clause
2+
# Copyright 2020-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3+
#
4+
# Redistribution and use in source and binary forms, with or without
5+
# modification, are permitted provided that the following conditions
6+
# are met:
7+
# * Redistributions of source code must retain the above copyright
8+
# notice, this list of conditions and the following disclaimer.
9+
# * Redistributions in binary form must reproduce the above copyright
10+
# notice, this list of conditions and the following disclaimer in the
11+
# documentation and/or other materials provided with the distribution.
12+
# * Neither the name of NVIDIA CORPORATION nor the names of its
13+
# contributors may be used to endorse or promote products derived
14+
# from this software without specific prior written permission.
15+
#
16+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
17+
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19+
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
20+
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21+
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22+
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23+
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
24+
# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
427

528
import argparse
629
import importlib.util
@@ -298,7 +321,7 @@ def gitclone(self, repo, tag, subdir, org):
298321
self.cmd("fi")
299322
self.cwd(subdir)
300323
self.cmd(f"git fetch origin {tag}:tritonbuildref", check_exitcode=True)
301-
self.cmd(f"git checkout tritonbuildref", check_exitcode=True)
324+
self.cmd("git checkout tritonbuildref", check_exitcode=True)
302325
else:
303326
self.cmd(
304327
f" git clone --recursive --single-branch --depth=1 -b {tag} {org}/{repo}.git {subdir}; git --git-dir {subdir}/.git log --oneline -1",
@@ -1765,9 +1788,9 @@ def create_docker_build_script(script_name, container_install_dir, container_ci_
17651788
if secrets:
17661789
finalargs += [
17671790
f"--secret id=req,src={requirements}",
1768-
f"--secret id=VLLM_INDEX_URL",
1769-
f"--secret id=PYTORCH_TRITON_URL",
1770-
f"--secret id=NVPL_SLIM_URL",
1791+
"--secret id=VLLM_INDEX_URL",
1792+
"--secret id=PYTORCH_TRITON_URL",
1793+
"--secret id=NVPL_SLIM_URL",
17711794
f"--build-arg BUILD_PUBLIC_VLLM={build_public_vllm}",
17721795
]
17731796
finalargs += [

0 commit comments

Comments
 (0)