Add license compatibility checking #22

Closed
opened 2026-06-06 23:44:07 +00:00 by coding-agent-marvin8 · 1 comment
coding-agent-marvin8 commented 2026-06-06 23:44:07 +00:00 (Migrated from codeberg.org)

zaojun currently checks versions and CVEs per dependency. This issue tracks adding license compliance checking as an opt-in feature.

The PyPI JSON API response already includes license metadata in the same per-package call zaojun already makes — so this is zero extra HTTP requests.

What it will do:

  • Read license info from PyPI (info.license_expression, info.classifiers, info.license) in priority order
  • Compare against an allowed-license list (user-specified SPDX IDs, or auto-derived from the project's own project.license)
  • Flag violations inline with ⚖️ and in a detail block at the end of the report
  • Default unknown-license = "warn" — packages with no license metadata warn but do not fail

New config keys:

  • check-licenses (bool, CLI + config, default: false)
  • allowed-licenses (list of SPDX IDs, config-only; empty = auto-derive from project license)
  • unknown-license (str: "warn" | "fail" | "allow", CLI + config, default: "warn")
  • license-ignore (list of dicts with required package and reason fields, config-only)
zaojun currently checks versions and CVEs per dependency. This issue tracks adding license compliance checking as an opt-in feature. The PyPI JSON API response already includes license metadata in the same per-package call zaojun already makes — so this is zero extra HTTP requests. **What it will do:** - Read license info from PyPI (`info.license_expression`, `info.classifiers`, `info.license`) in priority order - Compare against an allowed-license list (user-specified SPDX IDs, or auto-derived from the project's own `project.license`) - Flag violations inline with ⚖️ and in a detail block at the end of the report - Default `unknown-license = "warn"` — packages with no license metadata warn but do not fail **New config keys:** - `check-licenses` (bool, CLI + config, default: false) - `allowed-licenses` (list of SPDX IDs, config-only; empty = auto-derive from project license) - `unknown-license` (str: "warn" | "fail" | "allow", CLI + config, default: "warn") - `license-ignore` (list of dicts with required `package` and `reason` fields, config-only)
coding-agent-marvin8 commented 2026-06-06 23:44:27 +00:00 (Migrated from codeberg.org)

Approach and key files:

License data resolution (priority order)

  1. info.license_expression — PEP 639 SPDX expression (most authoritative)
  2. info.classifiers — parse Trove classifier e.g. "License :: OSI Approved :: MIT License" → "MIT"
  3. info.license — free text, normalise known strings to SPDX IDs
  4. None → treated as UNKNOWN

Auto-derivation from project license

Read project.license from the checked project's own pyproject.toml. Map the SPDX ID to a compatibility tier:

Project license Allowed dep tier
MIT, BSD, Apache-2.0, ISC, … Permissive only
LGPL, MPL-2.0 Permissive + weak copyleft
GPL-2.0-only Permissive (excl. Apache-2.0) + GPL-2.0-only
GPL-3.0 Permissive + LGPL + GPL-3.0
AGPL-3.0 All of the above + AGPL-3.0

Note: GPL-2.0-only excludes Apache-2.0 deps due to patent-clause incompatibility.

Key files

  • New: src/zaojun/licenses.py — tier frozensets, SPDX extraction, inference
  • New: tests/test_licenses.py
  • New: docs/usage/license-checking.md
  • Modify: src/zaojun/config.py — 4 new config keys + validators
  • Modify: src/zaojun/__init__.py — DependencyResult fields, get_latest_pypi_version 4-tuple, process_dependencies, rendering

Commit sequence (TDD)

  1. :wrench: tooling/CI dependency bumps (existing uncommitted changes)
  2. :white_check_mark: failing tests for licenses.py
  3. :sparkles: implement licenses.py
  4. :white_check_mark: failing tests for config keys
  5. :sparkles: implement config.py changes
  6. :white_check_mark: failing tests for PyPI fetch + cache license field
  7. :sparkles: extract license from PyPI response
  8. :white_check_mark: failing tests for compliance checking + output
  9. :sparkles: wire license checking into process_dependencies + CLI
  10. :memo: documentation
**Approach and key files:** ## License data resolution (priority order) 1. `info.license_expression` — PEP 639 SPDX expression (most authoritative) 2. `info.classifiers` — parse Trove classifier e.g. "License :: OSI Approved :: MIT License" → "MIT" 3. `info.license` — free text, normalise known strings to SPDX IDs 4. `None` → treated as UNKNOWN ## Auto-derivation from project license Read `project.license` from the checked project's own `pyproject.toml`. Map the SPDX ID to a compatibility tier: | Project license | Allowed dep tier | |---|---| | MIT, BSD, Apache-2.0, ISC, … | Permissive only | | LGPL, MPL-2.0 | Permissive + weak copyleft | | GPL-2.0-only | Permissive (excl. Apache-2.0) + GPL-2.0-only | | GPL-3.0 | Permissive + LGPL + GPL-3.0 | | AGPL-3.0 | All of the above + AGPL-3.0 | Note: GPL-2.0-only excludes Apache-2.0 deps due to patent-clause incompatibility. ## Key files - **New**: `src/zaojun/licenses.py` — tier frozensets, SPDX extraction, inference - **New**: `tests/test_licenses.py` - **New**: `docs/usage/license-checking.md` - **Modify**: `src/zaojun/config.py` — 4 new config keys + validators - **Modify**: `src/zaojun/__init__.py` — DependencyResult fields, get_latest_pypi_version 4-tuple, process_dependencies, rendering ## Commit sequence (TDD) 0. `:wrench:` tooling/CI dependency bumps (existing uncommitted changes) 1. `:white_check_mark:` failing tests for licenses.py 2. `:sparkles:` implement licenses.py 3. `:white_check_mark:` failing tests for config keys 4. `:sparkles:` implement config.py changes 5. `:white_check_mark:` failing tests for PyPI fetch + cache license field 6. `:sparkles:` extract license from PyPI response 7. `:white_check_mark:` failing tests for compliance checking + output 8. `:sparkles:` wire license checking into process_dependencies + CLI 9. `:memo:` documentation
Sign in to join this conversation.
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Reference
marvin8/zaojun#22
No description provided.