Skip to content

Initial public release #10

Initial public release

Initial public release #10

Workflow file for this run

# Copyright (c) 2026 Santander Group
# SPDX-License-Identifier: Apache-2.0
#
# License compliance: dependency-license allowlist + SPDX header verification.
name: License check
on:
push:
branches: [main, development]
pull_request:
permissions:
contents: read
jobs:
dep-licenses:
name: Dependency license allowlist
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Set up Python
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
with:
python-version: "3.12"
cache: pip
- name: Install project + pip-licenses
run: |
python -m pip install --upgrade pip
# Install the CPU-only torch build (pulled transitively via pgmpy) so
# we don't pull NVIDIA CUDA wheels (proprietary licences) that are
# irrelevant to this library.
pip install torch --index-url https://download.pytorch.org/whl/cpu
pip install -e "."
pip install pip-licenses==5.0.0
- name: Verify all dependency licenses are in allowlist
run: |
pip-licenses --format=json > /tmp/licenses.json
python - <<'PY'
import json
import re
import sys
# Canonical allowlist of OSI-approved permissive licences.
ALLOWED = {
"Apache Software License", "Apache 2.0", "Apache-2.0",
"MIT License", "MIT",
"BSD License", "BSD-3-Clause", "BSD-2-Clause", "BSD",
"Mozilla Public License 2.0 (MPL 2.0)", "MPL-2.0",
"Python Software Foundation License", "PSF-2.0",
"The Unlicense (Unlicense)", "Unlicense",
"ISC License (ISCL)", "ISC",
}
# Manual overrides for packages whose metadata reports UNKNOWN but
# whose licence is publicly verified. Add a justification per entry.
KNOWN_GOOD = {
"numpy": "BSD-3-Clause (verified at https://github.com/numpy/numpy/blob/main/LICENSE.txt)",
"scipy": "BSD-3-Clause (verified at https://github.com/scipy/scipy/blob/main/LICENSE.txt)",
"scikit-learn": "BSD-3-Clause (verified at https://github.com/scikit-learn/scikit-learn/blob/main/COPYING)",
"joblib": "BSD-3-Clause (verified at https://github.com/joblib/joblib/blob/main/LICENSE.txt)",
"pgmpy": "MIT (verified at https://github.com/pgmpy/pgmpy/blob/dev/LICENSE)",
"pandas": "BSD-3-Clause (verified at https://github.com/pandas-dev/pandas/blob/main/LICENSE)",
"pyarrow": "Apache-2.0 (verified at https://github.com/apache/arrow/blob/main/LICENSE.txt)",
# torch and its transitive deps (pulled via pgmpy).
"torch": "BSD-3-Clause (verified at https://github.com/pytorch/pytorch/blob/main/LICENSE)",
"tqdm": "MPL-2.0 AND MIT (verified at https://github.com/tqdm/tqdm/blob/master/LICENCE)",
"sympy": "BSD-3-Clause (verified at https://github.com/sympy/sympy/blob/master/LICENSE)",
"mpmath": "BSD-3-Clause (verified at https://github.com/mpmath/mpmath/blob/master/LICENSE)",
"filelock": "Unlicense (verified at https://github.com/tox-dev/filelock/blob/main/LICENSE)",
"networkx": "BSD-3-Clause (verified at https://github.com/networkx/networkx/blob/main/LICENSE.txt)",
"fsspec": "BSD-3-Clause (verified at https://github.com/fsspec/filesystem_spec/blob/master/LICENSE)",
"MarkupSafe": "BSD-3-Clause (verified at https://github.com/pallets/markupsafe/blob/main/LICENSE.txt)",
"typing_extensions": "PSF-2.0 (verified at https://github.com/python/typing_extensions/blob/main/LICENSE)",
"Jinja2": "BSD-3-Clause (verified at https://github.com/pallets/jinja/blob/main/LICENSE.txt)",
"narwhals": "MIT (verified at https://github.com/narwhals-dev/narwhals/blob/main/LICENSE.md)",
"packaging": "Apache-2.0 OR BSD-2-Clause (verified at https://github.com/pypa/packaging/blob/main/LICENSE)",
}
def tokens(license_str: str):
return [t.strip() for t in re.split(r"\s*(?:;|,| AND | OR )\s*", license_str) if t.strip()]
data = json.load(open("/tmp/licenses.json"))
bad = []
for p in data:
name = p["Name"]
lic = p["License"]
if name in KNOWN_GOOD:
print(f"OVERRIDE: {name} {p['Version']} -> {lic!r} accepted ({KNOWN_GOOD[name]})")
continue
parts = tokens(lic)
if not parts or not all(t in ALLOWED for t in parts):
bad.append((name, p["Version"], lic))
if bad:
print("\n--- DISALLOWED dependencies ---")
for name, version, lic in bad:
print(f" {name} {version} -> {lic!r}")
print("\nIf the dependency licence is genuinely OSI-permissive but mis-reported,")
print("add the package to KNOWN_GOOD in .github/workflows/license-check.yml")
print("with a justification linking to the upstream LICENSE file.")
sys.exit(1)
print(f"\nOK: {len(data)} dependencies, all licences in allowlist.")
PY
spdx-headers:
name: SPDX headers
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Verify every Python file declares SPDX-License-Identifier
run: |
missing=0
while IFS= read -r f; do
if ! head -n 5 "$f" | grep -q "SPDX-License-Identifier:"; then
echo "MISSING SPDX header: $f"
missing=$((missing + 1))
fi
done < <(find src tests -type f -name "*.py")
if [ "$missing" -gt 0 ]; then
echo "Found $missing files without SPDX header."
exit 1
fi
echo "OK: all Python files have SPDX headers."