|
1 | 1 | --- |
2 | 2 | name: python-packaging-license-checker |
3 | | -description: Assess license compatibility for Python package redistribution using SPDX.org license database. Evaluates whether a given license allows building and distributing wheels, with real-time license information lookup. |
4 | | -allowed-tools: WebFetch |
| 3 | +description: Check whether a Python package license is compatible with redistribution |
| 4 | + in Red Hat products, using the Fedora License Data as the authoritative policy source. |
| 5 | + Produces a structured six-field verdict with escalation guidance for non-trivial cases. |
| 6 | +allowed-tools: Bash Skill WebFetch |
5 | 7 | --- |
6 | 8 |
|
7 | 9 | # Python Package License Compatibility Checker |
8 | 10 |
|
9 | | -This skill helps you evaluate whether a Python package license is compatible with redistribution, particularly for building and distributing wheels in enterprise environments. It uses the authoritative SPDX License List for accurate, up-to-date license information. |
| 11 | +Evaluate whether a Python package license permits redistribution in Red Hat products. |
10 | 12 |
|
11 | | -## Assessment Instructions |
12 | | - |
13 | | -When a user provides a license name and asks about compatibility for redistribution, building wheels, or licensing restrictions, follow this methodology: |
14 | | - |
15 | | -### Step-by-Step Process |
16 | | - |
17 | | -1. **Fetch Current SPDX Data**: |
18 | | - ``` |
19 | | - Use WebFetch to query: https://raw.githubusercontent.com/spdx/license-list-data/main/json/licenses.json |
20 | | - ``` |
21 | | - |
22 | | -2. **License Matching**: |
23 | | - - Try exact SPDX ID match first |
24 | | - - Try case-insensitive SPDX ID match |
25 | | - - Try full name matching |
26 | | - - Try partial/fuzzy matching for common variations |
27 | | - |
28 | | -3. **Risk Classification** (check strong copyleft FIRST to prevent misclassification): |
29 | | - ``` |
30 | | - IF (strong_copyleft_pattern): |
31 | | - Risk = High, Status = Restricted/Incompatible |
32 | | - # GPL, AGPL are ALWAYS restricted regardless of OSI/FSF status |
33 | | - ELIF (NOT isOsiApproved): |
34 | | - Risk = High, Status = Restricted/Incompatible |
35 | | - ELIF (isOsiApproved AND weak_copyleft_pattern): |
36 | | - Risk = Medium, Status = Compatible with Requirements |
37 | | - ELIF (isOsiApproved AND isFsfLibre AND permissive_pattern): |
38 | | - Risk = Low, Status = Compatible |
39 | | - ELSE: |
40 | | - Risk = High, Status = Unknown - requires manual review |
41 | | - ``` |
| 13 | +**Policy basis**: Red Hat redistribution policy follows the Fedora Linux license |
| 14 | +policy. Use the Fedora License Data as the authoritative source -- NOT OSI-approval |
| 15 | +status. |
42 | 16 |
|
43 | | -4. **Generate Assessment**: |
44 | | - - Include all SPDX metadata |
45 | | - - Provide clear compatibility guidance |
46 | | - - List specific requirements |
47 | | - - Add Red Hat context where relevant |
| 17 | +## Inputs |
48 | 18 |
|
49 | | -## License Assessment Framework |
| 19 | +- **package_name**: Python package name |
| 20 | +- **source_available**: whether buildable source exists (if already known) |
| 21 | +- **upstream_org**: name and primary country of the maintaining organisation (if already known) |
50 | 22 |
|
51 | | -### Input Processing |
52 | | -Accept various formats and normalize them: |
53 | | -- **SPDX Identifiers**: "MIT", "Apache-2.0", "GPL-3.0-only" |
54 | | -- **Full Names**: "MIT License", "Apache License 2.0", "GNU General Public License v3.0" |
55 | | -- **Common Aliases**: "Apache 2", "BSD 3-Clause", "GPLv3" |
56 | | -- **Case Variations**: Handle case-insensitive matching |
| 23 | +## Step 1: Locate and clone the source repository |
57 | 24 |
|
58 | | -### SPDX Data Analysis |
59 | | -When processing SPDX license data, examine these key fields: |
60 | | -- `licenseId`: Official SPDX identifier |
61 | | -- `name`: Full license name |
62 | | -- `isOsiApproved`: OSI approval status (boolean) |
63 | | -- `isFsfLibre`: FSF Free Software status (boolean) |
64 | | -- `isDeprecatedLicenseId`: Whether license is deprecated (boolean) |
65 | | -- `reference`: URL to full license details |
66 | | -- `seeAlso`: Array of additional reference URLs |
| 25 | +Use the python-packaging-source-finder skill to find the upstream repo URL: |
67 | 26 |
|
68 | | -### License Pattern Definitions |
| 27 | +``` |
| 28 | +Skill: python-packaging-source-finder |
| 29 | +``` |
69 | 30 |
|
70 | | -Use these explicit pattern lists for classification. Match against the SPDX `licenseId` field (case-insensitive). |
| 31 | +Then use the git-shallow-clone skill to clone the repository locally: |
71 | 32 |
|
72 | | -#### Permissive Patterns (permissive_pattern) |
73 | | -Licenses where the `licenseId` contains or matches any of: |
74 | | -- `MIT`, `Apache-`, `BSD-`, `ISC`, `Unlicense`, `0BSD`, `PSF-`, `Python-`, `Zlib`, `BSL-1.0`, `CC0-`, `WTFPL`, `MulanPSL-` |
| 33 | +``` |
| 34 | +Skill: git-shallow-clone |
| 35 | +``` |
75 | 36 |
|
76 | | -#### Weak Copyleft Patterns (weak_copyleft_pattern) |
77 | | -Licenses where the `licenseId` contains or matches any of: |
78 | | -- `LGPL-`, `MPL-`, `EPL-`, `CDDL-`, `CPL-`, `CeCILL-2.1`, `EUPL-` |
| 37 | +This gives a local path to the cloned repo for deterministic license inspection. |
79 | 38 |
|
80 | | -#### Strong Copyleft Patterns (strong_copyleft_pattern) |
81 | | -Licenses where the `licenseId` contains or matches any of: |
82 | | -- `GPL-` (but NOT `LGPL-`), `AGPL-`, `SSPL-`, `OSL-`, `CeCILL-` (but NOT `CeCILL-2.1`), `EUPL-` (when used with strong copyleft intent) |
| 39 | +## Step 2: Read the license from source |
83 | 40 |
|
84 | | -**CRITICAL**: `GPL-2.0`, `GPL-3.0`, `GPL-2.0-only`, `GPL-2.0-or-later`, `GPL-3.0-only`, `GPL-3.0-or-later` are ALL strong copyleft. They are NOT permissive. They MUST be classified as Restricted/Incompatible for commercial wheel redistribution. |
| 41 | +Search the cloned repo root for license files in this order: |
| 42 | +LICENSE, LICENSE.txt, LICENSE.md, COPYING, COPYING.txt, LICENCE, LICENCE.txt |
85 | 43 |
|
86 | | -### Compatibility Assessment Logic |
| 44 | +```bash |
| 45 | +find <clone_path> -maxdepth 1 -iname "license*" -o -iname "copying*" -o -iname "licence*" | head -5 |
| 46 | +``` |
87 | 47 |
|
88 | | -Use SPDX flags and the pattern definitions above to determine compatibility: |
| 48 | +Read the first 30 lines of the found file and map to an SPDX identifier. |
89 | 49 |
|
90 | | -#### ✅ Highly Compatible (Low Risk) |
91 | | -- OSI Approved AND FSF Libre AND matches permissive_pattern |
92 | | -- Examples: MIT, Apache-2.0, BSD-3-Clause, ISC, PSF-2.0 |
93 | | -- No copyleft requirements of any kind |
| 50 | +Also check `pyproject.toml` or `setup.cfg` for a `license` field as a secondary |
| 51 | +signal. If the PyPI metadata license differs from the source file, set |
| 52 | +Source verified: MISMATCH (PyPI: [X], repo: [Y]) and use the source file as |
| 53 | +authoritative. |
94 | 54 |
|
95 | | -#### ⚠️ Compatible with Requirements (Medium Risk) |
96 | | -- OSI Approved AND matches weak_copyleft_pattern |
97 | | -- Examples: LGPL-2.1-only, LGPL-3.0-or-later, MPL-2.0 |
98 | | -- File-level or library-level copyleft only |
| 55 | +**Normalise to SPDX ID** using exact then fuzzy matching: |
| 56 | +- "Apache 2" -> Apache-2.0, "GPL v2" -> GPL-2.0-only, "BSD" -> BSD-3-Clause, |
| 57 | + "GPLv3" -> GPL-3.0-or-later, "MIT License" -> MIT, "LGPLv2.1" -> LGPL-2.1-only |
99 | 58 |
|
100 | | -#### ❌ Restricted/High Risk |
101 | | -- Matches strong_copyleft_pattern (GPL, AGPL) — regardless of OSI or FSF status |
102 | | -- Non-OSI approved licenses |
103 | | -- Proprietary or unclear terms |
104 | | -- GPL licenses require ALL derivative works to be released under the same GPL license, making them incompatible with proprietary or commercial redistribution of binary wheels |
| 59 | +**Compound expressions**: If the license contains `AND` or `OR`: |
| 60 | +- `AND`: split and evaluate each component. Apply the license governing the |
| 61 | + importable library API (not a bundled CLI). Document the split in Notes. |
| 62 | +- `OR`: evaluate each component; use the most permissive allowed one. If none are |
| 63 | + allowed, the overall verdict is the best individual verdict. |
105 | 64 |
|
106 | | -### Output Format |
| 65 | +If no license file found and no SPDX mapping possible: Verdict: NEEDS-HUMAN-REVIEW. |
107 | 66 |
|
108 | | -Provide a structured assessment with: |
109 | | - |
110 | | -1. **SPDX Information**: |
111 | | - - Official SPDX ID |
112 | | - - Full license name |
113 | | - - OSI Approved: Yes/No |
114 | | - - FSF Libre: Yes/No |
115 | | - - Deprecated: Yes/No (if applicable) |
116 | | - |
117 | | -2. **Compatibility Assessment**: |
118 | | - - Status: Compatible/Restricted/Incompatible |
119 | | - - Redistribution: Allowed/Restricted/Prohibited |
120 | | - - Commercial Use: Allowed/Restricted/Prohibited |
121 | | - |
122 | | -3. **Requirements**: Key compliance obligations |
123 | | -4. **Risk Level**: Low/Medium/High for enterprise use |
124 | | -5. **Red Hat Context**: Special considerations if applicable |
| 67 | +Clean up the clone when done: |
125 | 68 |
|
| 69 | +```bash |
| 70 | +rm -rf <clone_parent_dir> |
| 71 | +``` |
126 | 72 |
|
127 | | -## Red Hat Vendor Agreements |
128 | | - |
129 | | -Red Hat has specific licensing agreements with the following hardware vendors: |
| 73 | +## Step 3: Look up license in Fedora License Data |
130 | 74 |
|
131 | | -- **NVIDIA**: Agreement covers CUDA libraries, runtimes, and related NVIDIA proprietary components |
132 | | -- **Intel Gaudi**: Agreement covers Gaudi AI accelerator software and libraries |
133 | | -- **IBM Spyre**: Agreement covers IBM Spyre AI hardware and associated software components |
| 75 | +Fetch the live data: |
134 | 76 |
|
135 | | -When evaluating packages with dependencies on these vendor-specific components, note that Red Hat has explicit redistribution rights under these agreements. |
| 77 | +``` |
| 78 | +WebFetch: https://gitlab.com/fedora/legal/fedora-license-data/-/jobs/artifacts/main/raw/fedora-licenses.json?job=json |
| 79 | +``` |
136 | 80 |
|
137 | | -## Error Handling |
| 81 | +Search for an entry matching the SPDX ID (case-insensitive on `spdx_abbrev`). |
| 82 | +Each entry has `status`: "allowed" or "not-allowed". |
| 83 | + |
| 84 | +- `allowed` -> proceed to Step 4 (verdict refinement) |
| 85 | +- `not-allowed` -> Verdict: BLOCKED |
| 86 | +- not found -> Verdict: NEEDS-HUMAN-REVIEW |
| 87 | + |
| 88 | +**Fallback table** (use only when the JSON fetch fails): |
| 89 | + |
| 90 | +| Status | SPDX identifiers (representative) | |
| 91 | +|---|---| |
| 92 | +| allowed | MIT, Apache-2.0, BSD-2-Clause, BSD-3-Clause, ISC, PSF-2.0, CC0-1.0 | |
| 93 | +| allowed | GPL-2.0-only, GPL-2.0-or-later, GPL-3.0-only, GPL-3.0-or-later | |
| 94 | +| allowed | LGPL-2.0-only, LGPL-2.1-only, LGPL-2.1-or-later, LGPL-3.0-or-later | |
| 95 | +| allowed | AGPL-3.0-only, AGPL-3.0-or-later, MPL-2.0, EUPL-1.2, EPL-2.0 | |
| 96 | +| not-allowed | SSPL-1.0, BUSL-1.1, Commons-Clause | |
| 97 | +| ESCALATE | Any custom/proprietary license not in SPDX | |
138 | 98 |
|
139 | | -### SPDX Data Fetch Failures |
140 | | -If the SPDX license list cannot be retrieved, exit early and warn the user. |
| 99 | +When using fallback, append to Notes: "Fedora License Data unreachable; fallback |
| 100 | +table used. Verify at https://docs.fedoraproject.org/en-US/legal/allowed-licenses/" |
141 | 101 |
|
142 | | -### License Not Found in SPDX |
143 | | -When a license identifier is not found in the SPDX license list: |
144 | | -1. Check for common typos or variations |
145 | | -2. Suggest SPDX-compliant alternatives |
146 | | -3. Recommend contacting package maintainer |
147 | | -4. Provide conservative risk assessment |
| 102 | +## Step 4: Verdict refinement (apply in order) |
148 | 103 |
|
149 | | -### Deprecated Licenses |
150 | | -For deprecated SPDX licenses: |
151 | | -1. Note the deprecation status |
152 | | -2. Suggest migrating to current equivalent |
153 | | -3. Provide assessment based on deprecated license terms |
154 | | -4. Recommend updating package licensing |
| 104 | +**Rule A -- Proprietary or custom license** (not in Fedora data): |
155 | 105 |
|
156 | | -For complex licensing scenarios involving multiple packages or custom license terms, recommend consultation with legal counsel. |
| 106 | +First check the Vendor Agreements table below. If covered by an agreement: |
| 107 | +Verdict: ALLOWED, Notes: "Covered by Red Hat [Vendor] redistribution agreement." |
157 | 108 |
|
158 | | -## Integration Notes |
| 109 | +Otherwise: |
| 110 | +- Red Hat / IBM-owned upstream (e.g. Neural Magic): |
| 111 | + Verdict: ESCALATE-TO-LEGAL. |
| 112 | + Notes: "Red Hat-owned proprietary license. Must be relicensed before inclusion. |
| 113 | + Open a ServiceNow opensource-legal ticket." |
| 114 | +- Third-party proprietary: |
| 115 | + Verdict: ESCALATE-TO-LEGAL. |
| 116 | + Notes: "Third-party proprietary license. May be includable as a EULA-listed |
| 117 | + component. Open a legal-review task." |
| 118 | + |
| 119 | +**Rule B -- AGPL-3.0** (status=allowed, SPDX is AGPL-3.0-only or -or-later): |
| 120 | +Verdict: ALLOWED-WITH-CAVEAT. |
| 121 | +Notes: "AGPL-3.0 is allowed for redistribution but is frequently part of a |
| 122 | +dual-licensing strategy, increasing risk. PM sign-off required." |
| 123 | + |
| 124 | +**Rule C -- All other allowed licenses**: |
| 125 | +Verdict: ALLOWED. Notes: omit unless compliance note needed (e.g. LGPL dynamic |
| 126 | +linking does not propagate copyleft). |
| 127 | + |
| 128 | +**Rule D -- Not-allowed**: |
| 129 | +Verdict: BLOCKED. |
| 130 | +Notes: "License is on the Fedora not-allowed list." |
| 131 | + |
| 132 | +## Step 5: Compliance checks |
| 133 | + |
| 134 | +**Build compliance** (independent of license verdict): |
| 135 | +If `source_available` is false or no buildable source exists (no setup.py, |
| 136 | +pyproject.toml, or equivalent): |
| 137 | +- Build compliance: BUILD-COMPLIANCE-FLAG |
| 138 | +- Append to Notes: "No buildable source. Options: (1) request upstream sdist, |
| 139 | + (2) make dependency optional, |
| 140 | + (3) file PSX exception with 6-month deadline per PSS.SBR.02.02." |
| 141 | +- Otherwise: Build compliance: OK |
| 142 | + |
| 143 | +**Export compliance** (independent of license verdict): |
| 144 | +If upstream org's primary country is on the US OFAC sanctions list, EU Consolidated |
| 145 | +Sanctions List, or flagged by Red Hat geopolitical policy: |
| 146 | +- Export compliance: EXPORT-COMPLIANCE-FLAG |
| 147 | +- Append to Notes: "Upstream org may require export compliance review. Contact |
| 148 | + exportcompliance@redhat.com. Geopolitical sensitivity does not automatically block |
| 149 | + inclusion (precedent: Yandex/Russia cleared for catboost)." |
| 150 | +- Otherwise: Export compliance: OK |
| 151 | + |
| 152 | +## Output Format |
| 153 | + |
| 154 | +Produce ONLY this six-field block. No preamble, no extra text. |
| 155 | + |
| 156 | +``` |
| 157 | +_License:_ [SPDX expression, or "non-SPDX: [raw string]" if unmappable] |
| 158 | +_Source verified:_ YES | NO | MISMATCH (PyPI: [X], repo: [Y]) |
| 159 | +_Verdict:_ ALLOWED | ALLOWED-WITH-CAVEAT | NEEDS-HUMAN-REVIEW | ESCALATE-TO-LEGAL | BLOCKED |
| 160 | +_Build compliance:_ OK | BUILD-COMPLIANCE-FLAG |
| 161 | +_Export compliance:_ OK | EXPORT-COMPLIANCE-FLAG |
| 162 | +_Notes:_ [Actionable guidance. Omit if verdict is ALLOWED and all flags are OK.] |
| 163 | +``` |
| 164 | + |
| 165 | +## Vendor Agreements |
| 166 | + |
| 167 | +| Vendor | Covered components | |
| 168 | +|---|---| |
| 169 | +| NVIDIA | CUDA libraries, runtimes, NCCL, and related NVIDIA components | |
| 170 | +| Intel Gaudi | Gaudi AI accelerator software and libraries | |
| 171 | +| IBM Spyre | IBM Spyre AI hardware and associated software components | |
| 172 | + |
| 173 | +## Quick Reference |
| 174 | + |
| 175 | +| Scenario | Verdict | Build | Export | |
| 176 | +|---|---|---|---| |
| 177 | +| MIT, Apache-2.0, BSD-*, ISC, PSF-2.0 | ALLOWED | OK | OK | |
| 178 | +| GPL-*, LGPL-* | ALLOWED | OK | OK | |
| 179 | +| AGPL-3.0-* | ALLOWED-WITH-CAVEAT | OK | OK | |
| 180 | +| SSPL-1.0, BUSL-1.1, Commons-Clause | BLOCKED | -- | -- | |
| 181 | +| RH-owned proprietary | ESCALATE-TO-LEGAL | -- | -- | |
| 182 | +| Third-party proprietary (no vendor agreement) | ESCALATE-TO-LEGAL | -- | -- | |
| 183 | +| PyPI/repo license mismatch | per repo license (MISMATCH noted) | OK | OK | |
| 184 | +| No buildable source | per license | FLAG | OK | |
| 185 | +| OFAC/sanctions-flagged upstream | per license | OK | FLAG | |
| 186 | + |
| 187 | +## Error Handling |
159 | 188 |
|
160 | | -This skill works best when combined with: |
161 | | -- **python-packaging-license-finder** - Use to find license names before compatibility assessment |
| 189 | +- **Fedora data unreachable**: Use fallback table; append note. |
| 190 | +- **Source repo not found**: If python-packaging-source-finder returns null/low |
| 191 | + confidence, set Source verified: NO, verdict: NEEDS-HUMAN-REVIEW. |
| 192 | +- **Source repo private/inaccessible**: Set Source verified: NO. Verdict: |
| 193 | + NEEDS-HUMAN-REVIEW. |
| 194 | +- **Multiple conflicting LICENSE files**: Source verified: MISMATCH, verdict: |
| 195 | + NEEDS-HUMAN-REVIEW. Note the conflicting files. |
0 commit comments