Reviewing packaging-ci changes
Scope
Trigger when a PR changes any of:
.github/workflows/**,.github/actions/**,.github/prompts/claude-pr-review.md,CODEOWNERSdocker/dockerfiles/**,docker/publish/**,docker/config/**,docker/entrypoint/**requirements/*.txt.release/pypi/*.setup.pyMakefileinference/core/version.py,inference_models/pyproject.toml,inference_models/uv.lock(version + pin sync)
OUT of scope (defer): library/business logic under inference/, inference_models/inference_models/ source, inference_sdk/, inference_cli/; Workflows blocks/kinds; test content (this skill covers only which CI job runs tests and on what runner/deps).
Review checklist
BLOCK
- Release PR (
inference/core/version.py__version__changed) → confirm ALL ofinference_models/pyproject.toml(version),inference_models/uv.lock, and the 4inference-models~=requirement pins move together; RC suffix consistent everywhere (#2341). The reverse does NOT hold: aninference_modelsversion bump alone does not require aninference/core/version.pybump (the package versions independently). inference-modelspin bumped in one requirements file but not all 4 (requirements.{cpu,gpu,vino,jetson}.txt), or the# keep in synccomment deleted (#2341, #2144).inference_models/pyproject.tomldeps changed with no regeneratedinference_models/uv.lockin the same PR (#2301).- New/edited workflow missing top-level
permissions:(least privilege) (#1242, #1484). - Third-party GitHub Action referenced by a mutable tag/branch instead of a full commit SHA (supply-chain: a compromised upstream tag runs in CI with repo permissions).
run:step interpolates untrusted${{ github.event.* }}(PR title/body, issue/comment text) directly into a shell command (script injection) — pass viaenv:and reference"$VAR"instead.github.event.inputs.<bool>used bare in anif/pushinstead of== 'true'— string"false"is truthy (#522).- Dockerfile
-r requirements/*.txt/COPYpath that is not actually COPY-ed in, or wrong (#1595, #1166, #810). - New runtime dep added to
requirements/*.txtonly, not to every Dockerfile target (+ OS/system libs) that needs it (#1209, #975). - New/moved test or install job whose runner lacks the required system libs (GPU
cuda.h,libgles2/libegl1/libvips-dev) (#2147, #2294).
FLAG
- New image target missing any of:
.github/workflows/docker.<name>.yml, Dockerfile underdocker/dockerfiles/, publish script (if published),permissions:block; or a removed target that left orphans (#2149). - New PyPI wheel / requirements group not wired into
.release/pypi/*.setup.pyand/orMakefile:create_wheelsand the matchingtest_package_install_*.yml. - Bare
>=with no upper bound on a fast-moving lib (ask for justification) (#2071, #1424). - Suspected transitive-dep break with no defensive, commented pin (#839, #1879, #989).
- Base-image / runner / pinned-action-version change with no stated reason (#1491, #2385).
- New build/test job without
timeout-minutes(#596, #635). - New PR-triggered workflow without a
concurrency:group. - Secret referenced in a workflow that could be exfiltrated on a fork/
pull_request_targettrigger, or a new secret without an owner.
NIT
- Quoting /
token_format/ cosmetic YAML churn (acceptable, but must not hide a behavior change) (#2326).
Not blocking
- Numpy is pinned
numpy>=2.0.0,<2.4.0repo-wide (_requirements.txt+ Jetson Dockerfiles build torch/opencv from source for numpy 2.x). Do NOT ask for a numpy-1.x pin — that constraint is gone. Only flag if a change reintroduces an unbounded numpy or a conflicting per-target pin. - Adding an upper bound to a dependency proactively is a normal maintainer move, not a regression — don't demand a linked failure for every bound.
- Cosmetic reordering / comment rewording that preserves behavior.
- A version bump PR that touches many files is expected; the release template legitimately spans version.py + pyproject + uv.lock + 4 requirements files.
- Do NOT demand an
inference/core/version.pybump on non-release PRs — inference releases are versioned separately; the lock-step checks above apply only when a version actually changes in the diff.
Standards
- Version is single-sourced. Image tags and wheel versions derive from
inference/core/version.py(__version__), read in CI viaDISABLE_VERSION_CHECK=true python ./inference/core/version.py.inference-modelsis versioned separately ininference_models/pyproject.toml(version). inference-modelspin stays in lockstep across 4 files.requirements.{cpu,gpu,vino,jetson}.txteach pininference-models~=X.Y.Zand carry the# keep in sync between ...comment. Verify all four move together and matchinference_models/pyproject.toml(#2341).- Requirements feed wheels too.
.release/pypi/inference*.setup.pyandMakefile:create_wheelsread the samerequirements/*.txt; a bad pin breaks both Docker and PyPI paths. - Least-privilege
permissions:. Every workflow declares top-levelpermissions: contents: read, escalating per-job only where needed (id-token: writefor GCP OIDC indocker.*.yml;issues: write+pull-requests: writeinclaude-pr-review.yml) (#1242, #1484). - CI supply-chain & injection hygiene. This review bot itself runs on PRs. Third-party actions must be SHA-pinned;
run:steps must not interpolate untrusted${{ github.event.* }}(pass throughenv:); new workflows must not expose secrets to fork/pull_request_targetruns. - Dependency pins are bounded; additions are surgical. Maintainers add upper bounds proactively and pin transitive deps that broke silently even when not directly listed (
tokenizers#839,timm#1879,aiortc#989). Numpy isnumpy>=2.0.0,<2.4.0. - Requirement-file paths in Dockerfiles must be correct. Cross-check every
-r requirements.*.txt/COPYagainst the files actually COPY-ed in — a recurring break source (#1595). - GHA
workflow_dispatchboolean inputs are strings. Compare with== 'true', e.g.push: ${{ github.event_name == 'release' || (github.event.inputs.force_push == 'true') }}(#522). - Pinned runners/base images are intentional. Python 3.9 Dockerfiles pin
bookworm(Debian trixie broke them, #1491); Jetson images pinsetuptools,pycuda, and (Jetson 6/7)transformers>=4.57.3,<5.9.0(#2385, #2339). Base-image/tag changes need a stated reason. - CI jobs run on runners that have the target's system libs.
inference-gpuinstall tests needcuda.h/GPU libs → GPU machine (#2147); experimental e2e needslibgles2/libegl1/libvips-devapt-installed (#2294). - Timeouts on long jobs. Docker builds set
timeout-minutes(120 indocker.cpu.yml) (#596, #635). - Tag/registry semantics live in
.github/actions/determine-tags/action.yml:latestis only added for a real release that is the newest and is not an RC (gate&& [[ "$NORMALIZED_RELEASE" != *rc* ]], #1983); the normalize step strips a leadingvand matches multi-digit patches via regex^v?[0-9]+\.[0-9]+\.[0-9]+$(#1470).
Required companions
Block the PR if a change lacks its companion:
- Release PR ("Release X.Y.Z") → bump
inference/core/version.py__version__ANDinference_models/pyproject.tomlversionANDinference_models/uv.lockAND all 4inference-models~=pins, then amendinference_models/docs/changelog.mdby replacing## Unreleasedwith the released version heading and adding a fresh## Unreleasedsection. RC→final drops thercsuffix in every place (#2341). inference-modelsbump anywhere → the other 3 requirements files +pyproject.toml+uv.lock. Never one-of-four (#2341).inference_models/pyproject.tomldep add/bump → regeneratedinference_models/uv.lockin the same PR (#2301) + aninference_models/docs/changelog.mdentry under## Unreleasedfor user-facing changes.- New Docker image target → its
.github/workflows/docker.<name>.yml+ Dockerfile + (if published) publish script +permissions:. Removing a target removes all of these (#2149). - New PyPI wheel / requirements group → wire into
.release/pypi/*.setup.pyand/orMakefile:create_wheelsand the relevanttest_package_install_*.yml. - New runtime dep → correct
requirements/*.txtAND every Dockerfile target that uses it (system libs viaapt/dnf, e.g.libvips#1209,rustc#975). - New GHA workflow → top-level
permissions: contents: read,timeout-minutes, and aconcurrency:group for PR-triggered jobs.
Key files & entry points
inference/core/version.py— canonical server version (__version__).inference_models/pyproject.toml,inference_models/uv.lock,inference_models/docs/changelog.md— inference-models version + changelog.requirements/requirements.{cpu,gpu,vino,jetson}.txt— the 4 in-syncinference-modelspin files;requirements/_requirements.txt— shared core pins (numpy lives here)..github/actions/determine-tags/action.yml— tag/normalization logic..github/workflows/docker.*.yml(canonical:docker.cpu.yml),publish.pypi.yml— image build/push + wheel publishing;claude-pr-review.yml— review-agent workflow.docker/dockerfiles/Dockerfile.onnx.jetson.{5.1.1,6.0.0,6.2.0,7.1.0}— Jetson recipes (the fragile ones)..release/pypi/inference*.setup.py,Makefile(create_wheels,create_inference_cli_whl) — wheel assembly.CODEOWNERS,.github/prompts/claude-pr-review.md— ownership + review-agent config (codeowner review required, #455, #2228, #2135).
Reference PRs
- #2341 — version + 4-file pin sync (the release template).
- #2326 — build once, push Docker Hub + GCP; canonical docker workflow shape.
- #1242 / #1484 — least-privilege
permissions:. - #1983 — don't push
latestfor RC releases. - #1470 — version-tag regex / leading-
vstrip. - #522 — GHA boolean input
== 'true'coercion bug. - #839 / #1879 — defensive pins for transitive-dep breaks.
- #2301 — Lambda build fix: certifi/Pygments pin +
uv.locksync. - #2385 — pin
transformers<5.9in Jetson 6/7 Dockerfiles. - #2147 — run gpu-install test on runner with required system libs.
- #2149 — CI/docker/release cleanup after inference-models switch (target removal).
- #1595 — wrong
requirements/*.txtCOPY path broke the build.
Related topic skills
Load the matching topic skill when the PR also shows these cross-cutting concerns:
review-topic-backward-compat-and-versioningreview-topic-concurrency-and-resource-safetyreview-topic-test-hygiene