Skip to content

fix: validate HF wheel licenses before upload - #2386

Open
charlesbluca wants to merge 2 commits into
NVIDIA:mainfrom
charlesbluca:fix-hf-nightly-validator
Open

fix: validate HF wheel licenses before upload#2386
charlesbluca wants to merge 2 commits into
NVIDIA:mainfrom
charlesbluca:fix-hf-nightly-validator

Conversation

@charlesbluca

Copy link
Copy Markdown
Collaborator

Description

Hugging Face package builds now validate Apache-2.0 wheel metadata before any PyPI upload. The shared builder inspects every wheel's single .dist-info/METADATA, prefers the PEP 639 License-Expression header, falls back to the legacy License header, and requires the configured Apache classifier. Failures include the wheel filename and both observed license fields.

The previous OCR-only workflow check ran after upload and read only the legacy License header. Hatchling emits License-Expression: Apache-2.0, so a valid OCR wheel could be reported as invalid only after publication. Moving validation into the shared builder protects OCR, page-elements, and table-structure before _twine_upload; OCR-specific version, dependency, package-content, extension-ABI, and platform-tag checks remain in the workflow.

Current setuptools rejects combining a PEP 639 license expression with the required Apache Trove classifier. The metadata patch therefore keeps modern SPDX expressions for hatchling packages and uses the equivalent legacy text form for setuptools packages. Both forms are enforced by the shared validator and continue to bundle the Apache license file.

OCRv2 2.0.2 was already fully published and must not be rerun.

Validation

  • uv run --with pytest python -m pytest ci/tests/test_huggingface_release_workflow.py -q — 34 passed.
  • uvx pre-commit run --files ci/scripts/nightly_build_publish.py ci/tests/test_huggingface_release_workflow.py .github/workflows/huggingface-nightly.yml — all hooks passed.
  • git diff --check — passed.
  • No-upload Python 3.12 production builds from the current Hugging Face repositories:
    • nemotron-page-elements-v3 3.0.2.dev20260721152500, py3-none-any: License='Apache-2.0', no License-Expression, Apache classifier present; validator and twine check passed.
    • nemotron-table-structure-v1 1.0.1.dev20260721152500, py3-none-any: License='Apache-2.0', no License-Expression, Apache classifier present; validator and twine check passed.
    • nemotron-ocr 2.0.2.dev20260721152500, CPython 3.12 manylinux_2_34_x86_64, built in nvidia/cuda:13.0.0-devel-ubuntu24.04 with the workflow build, runtime-pin, auditwheel, and required-member flags: License-Expression='Apache-2.0', no legacy License, Apache classifier present; validator and twine check passed. Twine reported only the package's existing missing long-description warnings.
  • Downloaded published nemotron-ocr 2.0.2 CPython 3.12 manylinux_2_34_aarch64: the same production validator passed; independent inspection found License-Expression='Apache-2.0', no legacy License, and the Apache classifier. twine check passed with the same existing long-description warnings.

Checklist

  • I am familiar with the Contributing Guidelines.
  • New or existing tests cover these changes.
  • The documentation is up to date with these changes.

@copy-pr-bot

copy-pr-bot Bot commented Jul 21, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

Signed-off-by: Charles Blackmon-Luca <20627856+charlesbluca@users.noreply.github.com>
@charlesbluca
charlesbluca force-pushed the fix-hf-nightly-validator branch from 2d1aabd to 7df9f7a Compare July 21, 2026 15:46
@charlesbluca
charlesbluca marked this pull request as ready for review July 21, 2026 15:47
@charlesbluca
charlesbluca requested review from a team as code owners July 21, 2026 15:47
@charlesbluca
charlesbluca requested a review from edknv July 21, 2026 15:47
@greptile-apps

greptile-apps Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR moves HF wheel license validation into the shared builder (nightly_build_publish.py) so it executes before _twine_upload, preventing packages from being published with incorrect or missing license metadata. The OCR-specific inline check that ran after upload and only read the legacy License header is removed from the workflow.

  • New _validate_wheel_license_metadata: Inspects every built .whl for a valid License-Expression (PEP 639, preferred) or legacy License header and the required Apache Trove classifier, collecting all failures before raising.
  • Setuptools-aware _pyproject_license_line: Detects the build backend from pyproject.toml and emits license = {text = "Apache-2.0"} for setuptools (which rejects mixing SPDX expressions with Trove classifiers) and license = "Apache-2.0" for hatchling (PEP 639 form).
  • Test coverage: 34 unit tests cover both modern and legacy license paths, classifier rejection, multi-wheel partial failures, and source-order verification that validation precedes upload.

Confidence Score: 5/5

Safe to merge — validation now runs before upload for all three HF packages, the build-backend detection is correct, and tests cover the critical precedence logic.

The change is narrowly scoped to CI tooling: it adds pre-upload license validation and makes the pyproject license form backend-aware. Both code paths (setuptools/hatchling) are tested end-to-end, the ordering contract is enforced by a source-position test, and the workflow's inline OCR check is cleanly replaced. No production library code is touched.

Files Needing Attention: No files require special attention.

Important Files Changed

Filename Overview
ci/scripts/nightly_build_publish.py Added _validate_wheel_license_metadata (reads License-Expression/License/Classifier from each wheel's METADATA before upload) and updated _pyproject_license_line/_patch_pyproject_license to emit the correct license form based on build backend. Logic is correct and well-guarded.
.github/workflows/huggingface-nightly.yml Removed the post-upload OCR-only license check (which read only the legacy License header and ran after publication). Remaining inline validation covers OCR-specific version, runtime dependencies, wheel membership, and platform tags.
ci/tests/test_huggingface_release_workflow.py Added 8 new tests covering setuptools legacy detection, modern vs. legacy license precedence, missing/incorrect license rejection, classifier enforcement, multi-wheel failure collection, and pre-upload ordering. Coverage is thorough for the new validator.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[nightly_build_publish.py main] --> B[_patch_pyproject_license]
    B --> C{build-backend starts\nwith setuptools.?}
    C -- Yes --> D["license = {text = 'Apache-2.0'}"]
    C -- No --> E["license = 'Apache-2.0'"]
    D --> F[_build wheel + sdist]
    E --> F
    F --> G[_auditwheel_repair_dist_dir\nif --auditwheel-repair]
    G --> H[_validate_required_wheel_members]
    H --> I{args.license_text?}
    I -- Yes --> J[_validate_wheel_license_metadata]
    J --> K{Each wheel:\nLicense-Expression or License\n== expected?}
    K -- All pass --> L{args.upload?}
    K -- Any fail --> M[RuntimeError\nbefore upload]
    L -- Yes --> N[_twine_upload]
    L -- No --> O[Done, no upload]
    I -- No --> L
Loading

Reviews (2): Last reviewed commit: "Merge branch 'main' into fix-hf-nightly-..." | Re-trigger Greptile

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant