fix: validate HF wheel licenses before upload - #2386
Conversation
Signed-off-by: Charles Blackmon-Luca <20627856+charlesbluca@users.noreply.github.com>
2d1aabd to
7df9f7a
Compare
Greptile SummaryThis PR moves HF wheel license validation into the shared builder (
|
| 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
Reviews (2): Last reviewed commit: "Merge branch 'main' into fix-hf-nightly-..." | Re-trigger Greptile
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 639License-Expressionheader, falls back to the legacyLicenseheader, 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
Licenseheader. Hatchling emitsLicense-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.nemotron-page-elements-v33.0.2.dev20260721152500,py3-none-any:License='Apache-2.0', noLicense-Expression, Apache classifier present; validator andtwine checkpassed.nemotron-table-structure-v11.0.1.dev20260721152500,py3-none-any:License='Apache-2.0', noLicense-Expression, Apache classifier present; validator andtwine checkpassed.nemotron-ocr2.0.2.dev20260721152500, CPython 3.12manylinux_2_34_x86_64, built innvidia/cuda:13.0.0-devel-ubuntu24.04with the workflow build, runtime-pin, auditwheel, and required-member flags:License-Expression='Apache-2.0', no legacyLicense, Apache classifier present; validator andtwine checkpassed. Twine reported only the package's existing missing long-description warnings.nemotron-ocr2.0.2CPython 3.12manylinux_2_34_aarch64: the same production validator passed; independent inspection foundLicense-Expression='Apache-2.0', no legacyLicense, and the Apache classifier.twine checkpassed with the same existing long-description warnings.Checklist