Files
computer-playbook/utils/licensing/roles.py
T
kevinveenbirkenbachandClaude Opus 5 ff734fe728 feat(licensing): declare the deployed software's licence per role
The project licence governs this repository's code and says nothing about the application a role installs, which is where a restriction on commercial use usually sits: a deployment cleared to use Infinito.Nexus commercially is still barred from NLLB-200's CC BY-NC weights.

Each invokable role names its licence in meta/<role>/license.yml with the url it was read from. The rights of a named licence live once in meta/licenses.yml, fifteen fields following the taxonomy choosealicense publishes. AND folds to the strictest combination, OR keeps the most favourable whole licence, and the seven contexts are derived from the rights rather than declared, so a heading cannot contradict the matrix under it.

The generated page renders one matrix per context, a lint refuses a role that declares nothing, and a Playwright spec asserts the page against the deployed docs. 21 of 22 deprecated SPDX ids were resolved against the upstream text; EPSON's remains unread because its AUR page blocks automated access.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-20 23:35:00 +02:00

68 lines
2.0 KiB
Python

"""A role's declared licence, resolved against the central table."""
from __future__ import annotations
from typing import TYPE_CHECKING, NamedTuple
from utils.annotations.suppress import is_suppressed_in_head
from utils.cache.files import read_text
from utils.cache.yaml import load_yaml
from utils.licensing.rights import context_of, resolve
from utils.roles.mapping import ROLE_FILE_META_LICENSE
if TYPE_CHECKING:
from pathlib import Path
from utils.licensing.contexts import Context
RULE = "license-declared"
OWN_LICENSE = "LicenseRef-Infinito-Nexus-Community"
def deploys_nothing(role_dir: Path) -> bool:
"""Whether the role declares that it installs no third-party software.
Args:
role_dir: the role's directory.
"""
path = role_dir / ROLE_FILE_META_LICENSE
if not path.is_file():
return False
return is_suppressed_in_head(read_text(str(path)).splitlines(), RULE)
class Declared(NamedTuple):
"""What a role says about the licence of the software it deploys.
Args:
spdx: the SPDX expression, empty when the role declares none.
url: where the licence was read.
rights: the resolved rights row, None when no table entry matches.
context: the class the generated page groups the role under.
"""
spdx: str
url: str
rights: dict[str, bool] | None
context: Context
def declared(role_dir: Path) -> Declared | None:
"""Return a role's licence, or None when it declares no licence file.
Args:
role_dir: the role's directory.
"""
path = role_dir / ROLE_FILE_META_LICENSE
if not path.is_file():
return None
parsed = load_yaml(str(path))
if not isinstance(parsed, dict):
return None
spdx = str(parsed.get("spdx", "")).strip()
inline = parsed.get("rights")
rights = (
resolve(spdx, inline if isinstance(inline, dict) else None) if spdx else None
)
return Declared(spdx, str(parsed.get("url", "")), rights, context_of(rights))