Skip to content

ci: check dependency licenses#825

Merged
nabinchha merged 5 commits into
mainfrom
codex/822-dependency-license-ci
Jul 16, 2026
Merged

ci: check dependency licenses#825
nabinchha merged 5 commits into
mainfrom
codex/822-dependency-license-ci

Conversation

@nabinchha

@nabinchha nabinchha commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

📋 Summary

Add a reproducible CI gate that validates every supported Python locked runtime dependency slice against an explicit Apache-2.0 compatibility policy. Unknown or unapproved licenses, changed exact exception reports, stale exceptions, and unreviewed SPDX exception terms fail closed.

🔗 Related Issue

Closes #822

🔄 Changes

  • Add a repository-owned license policy with the approved Apache-2.0, BSD, ISC, MIT, and Zlib allowlist plus exact version/license package exceptions.
  • Add a pinned pip-licenses scanner with SPDX-like AND, OR, grouping, and fail-closed WITH handling.
  • Require a complete canonical MIT license body before using bundled text to normalize UNKNOWN metadata.
  • Make the local Make target construct an isolated locked production environment with an explicit Python 3.10 TOML fallback group.
  • Run the CI check across Python 3.10–3.14 so every supported lock slice is covered.
  • Add regression coverage for expression handling, exact exception reports, canonical MIT text, drift, and stale exceptions.

🧪 Testing

  • make check-all
  • uv lock --check
  • Focused dependency-license tests (12 passed)
  • Isolated Python 3.10 focused suite (12 passed)
  • Isolated locked runtime license scans on Python 3.10–3.14 (90 packages on 3.10; 88 on 3.11–3.14)
  • Workflow YAML validation

✅ Checklist

  • Follows commit message conventions
  • Commits are signed off (DCO)
  • Architecture docs updated (N/A — CI and policy tooling only)

Description updated with AI

@nabinchha
nabinchha requested a review from a team as a code owner July 15, 2026 18:12
@greptile-apps

greptile-apps Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds a reproducible CI gate that validates every locked Python runtime dependency against an explicit Apache-2.0 compatibility policy across Python 3.10–3.14. All previous review findings have been addressed: tomli moved to the dedicated license-check dependency group, the empty-semicolon early-return bug fixed, and the email-validator concern is moot because that package is absent from the current lock file.

  • New scripts/check_dependency_licenses.py: Implements a SPDX-like expression parser with semicolon splitting, alias normalization, WITH-exception fail-closed handling, canonical MIT text recognition, and stale-exception detection.
  • New dependency-license-policy.toml: Declares the allowed SPDX identifiers and exact per-package exceptions for 8 packages whose licenses fall outside the allowlist (certifi, chardet, numpy, pathspec, pillow, regex, tqdm, typing_extensions).
  • New CI job dependency-licenses: Runs make check-dependency-licenses in a matrix across all five supported Python versions with fail-fast: false so every slice is independently reported.

Confidence Score: 5/5

This PR adds only new CI tooling and policy files; it introduces no changes to production code paths and cannot regress runtime behaviour.

All three previously raised concerns are resolved: tomli is correctly gated in its own dependency group, the empty-semicolon early-return is fixed, and email-validator is absent from the lock file so no stale exception can fire. The expression parser, MIT-text recognizer, and stale-exception detector all behave correctly and are covered by 12 targeted tests.

No files require special attention.

Important Files Changed

Filename Overview
scripts/check_dependency_licenses.py Core scanner logic: SPDX expression parser, MIT text recognizer, semicolon-separated alias normalization, and policy evaluation. Logic is correct and well-exercised by the test suite.
dependency-license-policy.toml Policy allowlist and 8 exact-version/license exceptions; no email-validator entry and email-validator is absent from uv.lock, so no stale-exception concern.
tests_e2e/test_dependency_license_check.py 12 unit tests cover permissive/composite licenses, AND/OR semantics, WITH-exception fail-closed, empty semicolons, bundled MIT text, and stale exceptions; coverage is thorough.
.github/workflows/ci.yml Adds dependency-licenses matrix job (Python 3.10-3.14) pinned at commit SHAs with fail-fast: false; correctly passes LICENSE_PYTHON_VERSION from the matrix.
pyproject.toml Adds license-check optional group with tomli conditional on Python < 3.11; correctly structured so --no-dev --group license-check includes tomli only when needed.
Makefile New check-dependency-licenses target uses --isolated --no-dev --group license-check --locked to build an accurate production slice before invoking the script.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[make check-dependency-licenses] --> B[uv run --isolated --no-dev --group license-check --locked]
    B --> C[check_dependency_licenses.py]
    C --> D[uv tool run pip-licenses --python sys.executable]
    D --> E[JSON report: name, version, license, license_text]
    E --> F[evaluate_report]
    F --> G{package in exceptions?}
    G -- yes --> H{exact version+license match?}
    H -- yes --> I[reviewed]
    H -- no --> J[exception mismatch violation]
    G -- no --> K[normalized_license_alternatives]
    K --> L{license == UNKNOWN?}
    L -- yes --> M{looks_like_mit_license text?}
    M -- yes --> N[frozenset MIT]
    M -- no --> O[unknown violation]
    L -- no --> P[alias lookup / SPDX parser]
    P --> Q{any alternative subset of allowed_licenses?}
    Q -- yes --> R[passes]
    Q -- no --> S[disallowed violation]
    F --> T{stale exceptions?}
    T -- yes --> U[stale exception violation]
    T -- no --> V[all clear]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[make check-dependency-licenses] --> B[uv run --isolated --no-dev --group license-check --locked]
    B --> C[check_dependency_licenses.py]
    C --> D[uv tool run pip-licenses --python sys.executable]
    D --> E[JSON report: name, version, license, license_text]
    E --> F[evaluate_report]
    F --> G{package in exceptions?}
    G -- yes --> H{exact version+license match?}
    H -- yes --> I[reviewed]
    H -- no --> J[exception mismatch violation]
    G -- no --> K[normalized_license_alternatives]
    K --> L{license == UNKNOWN?}
    L -- yes --> M{looks_like_mit_license text?}
    M -- yes --> N[frozenset MIT]
    M -- no --> O[unknown violation]
    L -- no --> P[alias lookup / SPDX parser]
    P --> Q{any alternative subset of allowed_licenses?}
    Q -- yes --> R[passes]
    Q -- no --> S[disallowed violation]
    F --> T{stale exceptions?}
    T -- yes --> U[stale exception violation]
    T -- no --> V[all clear]
Loading

Reviews (4): Last reviewed commit: "fix: declare license checker fallback" | Re-trigger Greptile

Comment thread scripts/check_dependency_licenses.py Outdated
Comment on lines +90 to +93
tokens = [token.strip() for token in EXPRESSION_OPERATOR.split(stripped) if token.strip()]
if not tokens:
return frozenset()
identifiers.update(tokens)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Premature early return on empty semicolon segment discards already-collected identifiers. When stripped is empty (e.g. from a trailing semicolon such as "Apache Software License; " or consecutive semicolons), tokens is [] and the function immediately returns frozenset(), throwing away any identifiers accumulated in earlier loop iterations. The package is then flagged as "unknown or unrecognized" even though at least one valid segment was already parsed. The correct fix is to skip empty segments with continue rather than exiting the loop early.

Suggested change
tokens = [token.strip() for token in EXPRESSION_OPERATOR.split(stripped) if token.strip()]
if not tokens:
return frozenset()
identifiers.update(tokens)
tokens = [token.strip() for token in EXPRESSION_OPERATOR.split(stripped) if token.strip()]
if not tokens:
continue
identifiers.update(tokens)
Prompt To Fix With AI
This is a comment left during a code review.
Path: scripts/check_dependency_licenses.py
Line: 90-93

Comment:
Premature early return on empty semicolon segment discards already-collected identifiers. When `stripped` is empty (e.g. from a trailing semicolon such as `"Apache Software License; "` or consecutive semicolons), `tokens` is `[]` and the function immediately returns `frozenset()`, throwing away any identifiers accumulated in earlier loop iterations. The package is then flagged as "unknown or unrecognized" even though at least one valid segment was already parsed. The correct fix is to skip empty segments with `continue` rather than exiting the loop early.

```suggestion
        tokens = [token.strip() for token in EXPRESSION_OPERATOR.split(stripped) if token.strip()]
        if not tokens:
            continue
        identifiers.update(tokens)
```

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 42748e4. Empty semicolon-separated segments are now skipped without discarding valid identifiers, and test_evaluate_report_ignores_empty_semicolon_segments covers trailing and consecutive delimiters. The focused suite and full CI pass.

Comment thread dependency-license-policy.toml Outdated
Comment on lines +27 to +29
[exceptions.email-validator]
license = "The Unlicense (Unlicense)"
reason = "Existing unmodified validation library installed as a separate runtime dependency."

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Stale exception will break CI if PR #824 merges first. The email-validator exception is for a package that PR #824 removes. The stale-exception check added in this very PR will then flag it as "email-validator: stale exception; package is not present in the scanned environment" and fail the dependency-licenses job. The PR description already calls this out, but the exception must be removed from this file before (or at the same time as) merge whenever #824 lands first.

Prompt To Fix With AI
This is a comment left during a code review.
Path: dependency-license-policy.toml
Line: 27-29

Comment:
**Stale exception will break CI if PR #824 merges first.** The `email-validator` exception is for a package that PR #824 removes. The stale-exception check added in this very PR will then flag it as `"email-validator: stale exception; package is not present in the scanned environment"` and fail the `dependency-licenses` job. The PR description already calls this out, but the exception must be removed from this file before (or at the same time as) merge whenever #824 lands first.

How can I resolve this? If you propose a fix, please make it concise.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. PR #824 remains open, so the current runtime closure still contains email-validator and this exception is currently required. The resolution is to merge #825 before #824; if the order changes, this exception will be removed from #825 before merge. The dependency is also called out in the PR description.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR #824 has now merged. I rebased this branch onto the updated main and removed the email-validator exception in b048ee4. The isolated locked scans pass on Python 3.10–3.14 without that stale exception.

@github-actions

Copy link
Copy Markdown
Contributor

Code Review: PR #825 — ci: check dependency licenses

Summary

This PR adds a reproducible CI check that validates the locked runtime dependency
closure against an explicit Apache-2.0 compatibility policy. It introduces:

  • dependency-license-policy.toml — an allowlist of permissive SPDX licenses plus
    narrowly-scoped per-package exceptions.
  • scripts/check_dependency_licenses.py — a pinned pip-licenses scanner that
    normalizes license metadata, detects bundled MIT text, and flags unknown /
    disallowed / drifted / stale-exception cases.
  • A check-dependency-licenses Make target and a dedicated dependency-licenses
    CI job that syncs the --no-dev --locked closure before checking.
  • Focused unit tests for evaluate_report covering approval, rejection, bundled
    MIT detection, exception drift, and stale exceptions.

The design is clean and well-factored: pure evaluate_report logic separated from
I/O (collect_report), a --report flag enabling offline testing, and frozen
dataclasses throughout. The tests are genuine (they assert exact violation strings),
not hollow. The main risks are in the license-expression normalization, which does
not distinguish disjunctive (OR) from conjunctive (AND) license expressions.

Findings

1. (High) OR license expressions are treated as AND, producing false violations

scripts/check_dependency_licenses.py:224normalized_license_ids splits on
AND, OR, and WITH identically via EXPRESSION_OPERATOR, unions every token
into one set, then evaluate_report (line 268) requires the entire set to be a
subset of allowed_licenses.

For a dual-licensed package that reports "MIT OR GPL-3.0-only" — where a
downstream consumer is entitled to choose the permissive MIT arm — this yields
{"MIT", "GPL-3.0-only"}, and since GPL-3.0-only is not allowed, the package is
reported as a violation. That is incorrect: an OR expression is satisfied if any
one arm is allowed, whereas AND requires all arms.

The existing test test_evaluate_report_accepts_permissive_and_composite_licenses
uses "Apache-2.0 OR BSD-3-Clause" where both arms happen to be allowed, so it
passes and masks the bug. A real dependency like packaging historically reported
"Apache-2.0 OR BSD-3-Clause" (both fine), but any permissive OR copyleft
package would be spuriously failed, blocking CI on a legitimately compatible
dependency. Consider parsing OR groups separately (allowed if any alternative is a
subset) versus AND groups (allowed only if all terms are allowed).

2. (Medium) WITH exception operator splits the exception into a bogus token

scripts/check_dependency_licenses.py:165,224 — the same EXPRESSION_OPERATOR
splits "Apache-2.0 WITH LLVM-exception" into {"Apache-2.0", "LLVM-exception"}.
LLVM-exception is a license exception modifier, not a standalone license, and
will never be in allowed_licenses, so any package using a WITH clause (common
for permissively-licensed compiled deps) is flagged as disallowed. The WITH
right-hand side should be dropped or handled as a modifier on the preceding license
rather than treated as an independent identifier.

3. (Medium) tomli fallback import is undeclared; script/tests break on Python < 3.11

scripts/check_dependency_licenses.py:149-152 — on Python < 3.11 the module does
import tomli as tomllib, but tomli is not declared as a dependency anywhere in
the project (no match in any pyproject.toml). tests_e2e/test_dependency_license_check.py
exec_modules this script at import time, so on a 3.10 interpreter the entire test
module fails to import with ModuleNotFoundError: tomli — even though the tests
under review never call load_policy. If the e2e matrix includes Python 3.10 (the
repo supports 3.10–3.14), this is a hard failure. The dedicated CI job pins 3.11 so
the job itself is safe, but the tests are not run under that pin. Either declare a
tomli; python_version < "3.11" dev dependency or gate the test import on the Python
version.

4. (Low) Bundled-MIT heuristic misses the common "The MIT License (MIT)" header

scripts/check_dependency_licenses.py:198-200looks_like_mit_license requires
the normalized text to startswith("mit license "). A large fraction of real MIT
license files begin with "The MIT License (MIT)" (the SPDX/GitHub canonical
header), which fails the startswith check and is therefore reported as
unknown or unrecognized license when metadata is UNKNOWN. This weakens the
fallback for exactly the packages it is meant to rescue. Matching on the presence of
"permission is hereby granted, free of charge" (already required) with a looser
title check would be more robust.

5. (Low, informational) Stale-exception check couples this PR to #824 ordering

scripts/check_dependency_licenses.py:272-273 and
dependency-license-policy.toml:102-104 — the email-validator exception will
become "stale" (and fail CI) the moment #824 removes that dependency, as the PR body
itself notes. This is correct behavior by design, but it makes merge order
load-bearing. Worth confirming the coordination note is acted on before merge; no
code change required.

Structural Impact (graphify, 2.7s)

Risk: LOW (localized change)

  • 2 Python files, 0 AST entities, 0/82 clusters

The change is additive and isolated: new script, new policy file, new test, plus
CI/Makefile wiring. No existing modules import the new code, so there is no blast
radius on the interface → engine → config layering or any public API. The
structural risk is low; the substantive risk is entirely in the correctness of the
license-normalization logic (findings 1–2).

Verdict

Request changes (advisory — not posted). The tooling is well-structured and
the tests are meaningful, but findings 1 and 2 are real correctness bugs: legitimate
permissive dependencies expressed with OR or WITH will be flagged as policy
violations and block CI. Because the whole point of this check is to gate merges on
license compatibility, false positives here are as disruptive as false negatives.
Finding 3 will break the test suite outright if the e2e matrix runs on Python 3.10.
Findings 4–5 are lower priority. Recommend addressing the expression-normalization
(OR/WITH) handling and the tomli dependency before merge.

@johnnygreco

Copy link
Copy Markdown
Contributor

Thanks for putting this together, @nabinchha — the package-scoped policy and regression coverage are a solid foundation.

Summary

This adds a repository-owned dependency-license policy, a pinned pip-licenses scanner, a local Make target, and a dedicated CI job. The implementation matches the stated intent for the Python 3.11 CI slice, but supported-version and normalization gaps mean it does not yet enforce the complete locked runtime closure or reject every unreviewed license form.

Findings

Critical — Let's fix these before merge

.github/workflows/ci.yml:265, dependency-license-policy.toml:31 — CI covers only one locked runtime closure

  • What: The job is fixed to Python 3.11 even though uv.lock selects different runtime versions below 3.11. The isolated 3.11 run passes with 90 packages, but uv run --isolated --python 3.10 --all-packages --no-dev --locked python scripts/check_dependency_licenses.py scans 92 packages and fails because numpy==2.2.6 reports BSD License while the sole numpy exception expects the Python 3.11+ composite string.
  • Why: A green job does not establish that the current dependency set passes on every supported locked slice, and a dependency or metadata change confined to the Python 3.10 resolution can bypass this gate. That conflicts with the PR's “complete locked runtime dependency closure” acceptance criterion.
  • Suggestion: Run the license job for each distinct locked runtime slice (at minimum Python 3.10 and 3.11 for the versions currently selected in uv.lock) and let a package exception record every explicitly reviewed exact report, ideally scoped by version or environment marker. Keep an integration check for each slice so future lock changes cannot silently reduce coverage.

scripts/check_dependency_licenses.py:118WITH exceptions are silently discarded

  • What: _parse_primary() consumes the token after WITH but returns only the base-license identifiers. As a result, Apache-2.0 WITH Totally-Unknown-terms normalizes to Apache-2.0 and produces no violation; the test at tests_e2e/test_dependency_license_check.py:81 currently codifies the same base-only behavior.
  • Why: A new dependency can introduce an unknown or unreviewed SPDX exception and still pass whenever its base license is allowed, which defeats the policy's fail-closed behavior for unknown license terms.
  • Suggestion: Preserve the WITH combination during evaluation and require the exception (or the exact combined expression) to be explicitly allowed. Reject unknown exception identifiers by default and add a regression showing that an unreviewed WITH term fails; if LLVM-exception is intended to pass, authorize it explicitly.

Warnings — Worth addressing

scripts/check_dependency_licenses.py:64 — The MIT fallback accepts incomplete or modified license text

  • What: looks_like_mit_license() checks only a heading and the phrase permission is hereby granted, free of charge. A truncated body passes, as the current tests demonstrate, and a body with additional restrictions appended after that phrase also passes.
  • Why: A package with UNKNOWN metadata can be classified as MIT even when the bundled text is incomplete, contains a second license, or adds incompatible terms, creating another false-approval path in a compliance gate.
  • Suggestion: Match the complete canonical MIT grant and disclaimer while allowing the copyright line to vary, and reject additional license terms; alternatively, keep UNKNOWN fail-closed and use a reviewed package-specific exception or canonical-text hash. Add negative coverage for a truncated body and a standard-looking preamble followed by an extra restriction.

Makefile:458 — The local target scans an arbitrary active environment

  • What: uv run --no-sync does not construct the locked production environment. In a fresh worktree the target scanned 0 packages and reported every exception stale; against the repository's normal dev environment it scanned 159 packages and failed on dev-only dependencies instead of checking CI's 90-package runtime set.
  • Why: The advertised local preflight is environment-dependent, so contributors cannot reliably reproduce CI and may get false failures or scan the wrong package set.
  • Suggestion: Have the Make target create the locked production environment itself, for example with uv run --isolated --python $(LICENSE_PYTHON_VERSION) --all-packages --no-dev --locked ...; this form reproduced the 90-package Python 3.11 result without mutating the dev environment. Then make CI call that same target for the required Python-version matrix rather than preparing the environment separately.

What Looks Good

  • The global allowlist is cleanly separated from exact, reasoned package exceptions, and stale exceptions are treated as policy drift rather than silently retained.
  • The expression parser handles AND, OR, grouping, aliases, and semicolon-delimited metadata in a readable way; the follow-up commit also resolves the existing empty-segment feedback with targeted regression coverage.
  • The intended Python 3.11 production slice passes end to end, and the focused suite is healthy: 10 tests pass, with ruff check and ruff format --check clean on both changed Python files.

Verdict

Needs changes. Before merge, cover the Python 3.10 lock slice, preserve and validate WITH terms, harden the MIT text fallback, and make the local target construct an isolated locked runtime environment.


This review was generated by an AI assistant.

Add a locked runtime dependency license scan with an explicit permissive allowlist and package-specific reviewed exceptions. Fail on unknown licenses, exception drift, and stale exceptions.

Closes #822

Signed-off-by: Nabin Mulepati <nmulepati@nvidia.com>
Preserve SPDX OR, AND, and WITH semantics during policy evaluation. Cover malformed and delimited metadata, recognize the alternate MIT heading, and declare the Python 3.10 TOML fallback.

Signed-off-by: Nabin Mulepati <nmulepati@nvidia.com>
Scan isolated runtime environments across every supported Python version. Require exact version and license exception reports, preserve SPDX WITH terms, and accept only complete canonical MIT text.

Signed-off-by: Nabin Mulepati <nmulepati@nvidia.com>
Comment thread pyproject.toml
"pytest-env>=1.2.0,<2",
"pytest-httpx>=0.36.0,<1",
"ruff>=0.14.10,<1",
"tomli>=2.0.1,<3; python_version < '3.11'",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 tomli placed in dev group causes ModuleNotFoundError on Python 3.10 CI run

The check-dependency-licenses make target runs uv run --all-packages --no-dev --locked. Because --no-dev excludes the dev dependency group, tomli is not installed in that environment. On Python 3.10 the script reaches import tomli as tomllib (line 18 of scripts/check_dependency_licenses.py) and immediately crashes, making the dependency-licenses CI job always fail for the 3.10 matrix slot.

A fix is to move tomli out of the dev group — either into a dedicated non-dev group (e.g. scripts) and add --group scripts to the make target, or into the root project's dependencies with the same python_version < '3.11' marker.

Prompt To Fix With AI
This is a comment left during a code review.
Path: pyproject.toml
Line: 45

Comment:
`tomli` placed in `dev` group causes `ModuleNotFoundError` on Python 3.10 CI run

The `check-dependency-licenses` make target runs `uv run --all-packages --no-dev --locked`. Because `--no-dev` excludes the `dev` dependency group, `tomli` is not installed in that environment. On Python 3.10 the script reaches `import tomli as tomllib` (line 18 of `scripts/check_dependency_licenses.py`) and immediately crashes, making the `dependency-licenses` CI job always fail for the `3.10` matrix slot.

A fix is to move `tomli` out of the `dev` group — either into a dedicated non-dev group (e.g. `scripts`) and add `--group scripts` to the make target, or into the root project's `dependencies` with the same `python_version < '3.11'` marker.

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 322f72a. tomli is now declared in a dedicated license-check dependency group, and the isolated Make target explicitly requests that group while retaining --no-dev. The Python 3.10 locked license scan passes (90 packages checked), along with 12 focused tests and make check-all.

Remove the reviewed exception after PR #824 dropped email-validator from the locked runtime dependency graph.

Signed-off-by: Nabin Mulepati <nmulepati@nvidia.com>
@nabinchha
nabinchha force-pushed the codex/822-dependency-license-ci branch from d9a1cb8 to b048ee4 Compare July 16, 2026 16:57
Move the Python 3.10 tomli fallback into a dedicated dependency group and request it explicitly from the isolated license-check target.

Signed-off-by: Nabin Mulepati <nmulepati@nvidia.com>
@nabinchha

Copy link
Copy Markdown
Contributor Author

@johnnygreco Thanks for the detailed review. All four findings are addressed in 9f237f3a, and the follow-up dependency note is addressed in 322f72ae:

  • Locked runtime coverage: the CI job now runs Python 3.10–3.14. Package exceptions record exact reviewed version/license report pairs, including NumPy's distinct 3.10 and 3.11+ metadata.
  • Fail-closed WITH: the parser preserves the combined base-license/exception identifier. Unknown exception terms fail unless that exact combination is explicitly allowed; positive and negative regressions cover this.
  • MIT fallback: fallback detection now requires the complete canonical grant, notice requirement, and disclaimer, permits variable copyright lines, and rejects truncation or appended restrictions.
  • Reproducible local target: the Make target constructs an isolated --all-packages --no-dev --locked environment for the selected Python version, and CI invokes that same target.
  • Python 3.10 TOML fallback: tomli is now in a dedicated license-check group requested explicitly by the target rather than relying on the dev group or a transitive runtime edge.

PR #824 has also merged. This branch is rebased onto it, and the stale email-validator exception has been removed.

Verification: 12 focused tests pass, make check-all passes, uv lock --check passes, and isolated locked license scans pass on Python 3.10–3.14 (90 packages on 3.10 and 88 on 3.11–3.14 after #824 removed email-validator and dnspython).

@johnnygreco johnnygreco left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nabinchha Thanks for addressing this so thoroughly. I independently verified the updated head: all four findings are resolved, the 12 focused tests and full formatting/lint checks pass, the lock is current, and isolated locked license scans pass on Python 3.10–3.14. No remaining concerns from my review. Approved.

@nabinchha
nabinchha merged commit 8fa8e7b into main Jul 16, 2026
70 checks passed
@nabinchha
nabinchha deleted the codex/822-dependency-license-ci branch July 16, 2026 19:10
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.

ci: add dependency license compatibility check

2 participants