vimPluginsUpdater: generate plugin licenses
Fetch GitHub SPDX license metadata during plugin updates and emit meta.license for generated vim plugin derivations. Fall back to lib.licenses.unfree when no SPDX license is detected, and round-trip existing license metadata through get-plugins.nix so partial updates do not drop it.
This commit is contained in:
@@ -17,6 +17,7 @@ let
|
||||
parse = _name: value: {
|
||||
inherit (value) pname version;
|
||||
homePage = value.meta.homepage;
|
||||
license = value.meta.license.spdxId or null;
|
||||
checksum =
|
||||
if hasChecksum value then
|
||||
{
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#
|
||||
|
||||
import inspect
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import textwrap
|
||||
@@ -40,6 +41,15 @@ HEADER = "# GENERATED by ./pkgs/applications/editors/vim/plugins/utils/update.py
|
||||
|
||||
NIXPKGS_NVIMTREESITTER_FOLDER = "pkgs/applications/editors/vim/plugins/nvim-treesitter"
|
||||
|
||||
SPDX_LICENSE_NORMALIZATION = {
|
||||
"AGPL-3.0": "AGPL-3.0-only",
|
||||
"GPL-2.0": "GPL-2.0-only",
|
||||
"GPL-3.0": "GPL-3.0-only",
|
||||
"LGPL-2.0": "LGPL-2.0-only",
|
||||
"LGPL-2.1": "LGPL-2.1-only",
|
||||
"LGPL-3.0": "LGPL-3.0-only",
|
||||
}
|
||||
|
||||
|
||||
class VimEditor(nixpkgs_plugin_update.Editor):
|
||||
nvim_treesitter_updated = False
|
||||
@@ -112,11 +122,22 @@ class VimEditor(nixpkgs_plugin_update.Editor):
|
||||
|
||||
content = f" {plugin.normalized_name} = "
|
||||
src_nix = repo.as_nix(plugin)
|
||||
license_spdx_id = plugin.license
|
||||
if license_spdx_id is not None:
|
||||
license_spdx_id = SPDX_LICENSE_NORMALIZATION.get(
|
||||
license_spdx_id, license_spdx_id
|
||||
)
|
||||
license_nix = (
|
||||
f" meta.license = lib.meta.getLicenseFromSpdxId {json.dumps(license_spdx_id)};\n"
|
||||
if license_spdx_id
|
||||
else " meta.license = lib.licenses.unfree;\n"
|
||||
)
|
||||
content += """{buildFn} {{
|
||||
pname = "{plugin.name}";
|
||||
version = "{plugin.version}";
|
||||
src = {src_nix};
|
||||
meta.homepage = "{repo.uri}";
|
||||
{license_nix}\
|
||||
meta.hydraPlatforms = [ ];
|
||||
}};
|
||||
|
||||
@@ -125,6 +146,7 @@ class VimEditor(nixpkgs_plugin_update.Editor):
|
||||
plugin=plugin,
|
||||
src_nix=src_nix,
|
||||
repo=repo,
|
||||
license_nix=license_nix,
|
||||
)
|
||||
log.debug(content)
|
||||
return content
|
||||
|
||||
+53
@@ -244,6 +244,9 @@ class Repo:
|
||||
hash = "{plugin.to_sri_hash()}";
|
||||
}}"""
|
||||
|
||||
def get_license_spdx_id(self, fallback_license: str | None = None) -> str | None:
|
||||
return fallback_license
|
||||
|
||||
|
||||
class RepoGitHub(Repo):
|
||||
def __init__(self, owner: str, repo: str, branch: str) -> None:
|
||||
@@ -460,6 +463,41 @@ class RepoGitHub(Repo):
|
||||
loaded = json.loads(data)
|
||||
return loaded["hash"]
|
||||
|
||||
def get_license_spdx_id(self, fallback_license: str | None = None) -> str | None:
|
||||
license_url = f"https://api.github.com/repos/{self.owner}/{self.repo}/license"
|
||||
log.debug("Fetching license metadata from %s", license_url)
|
||||
|
||||
def log_fetch_failure(reason: str) -> str | None:
|
||||
log.warning(
|
||||
"Failed to fetch license metadata for %s/%s: %s; reusing %s",
|
||||
self.owner,
|
||||
self.repo,
|
||||
reason,
|
||||
fallback_license or "no cached license",
|
||||
)
|
||||
return fallback_license
|
||||
|
||||
try:
|
||||
req = make_request(license_url, self.token)
|
||||
with urllib.request.urlopen(req, timeout=10) as response:
|
||||
data = json.load(response)
|
||||
except urllib.error.HTTPError as e:
|
||||
if e.code == 404:
|
||||
return None
|
||||
return log_fetch_failure(f"HTTP {e.code}")
|
||||
except urllib.error.URLError as e:
|
||||
return log_fetch_failure(str(e))
|
||||
|
||||
license_info = data.get("license")
|
||||
if not isinstance(license_info, dict):
|
||||
return None
|
||||
|
||||
spdx_id = license_info.get("spdx_id")
|
||||
if not spdx_id or spdx_id == "NOASSERTION":
|
||||
return None
|
||||
|
||||
return spdx_id
|
||||
|
||||
def as_nix(self, plugin: "Plugin") -> str:
|
||||
if plugin.has_submodules:
|
||||
submodule_attr = "\n fetchSubmodules = true;"
|
||||
@@ -528,6 +566,7 @@ class Plugin:
|
||||
date: datetime | None = None
|
||||
last_tag: str | None = None
|
||||
tag: str | None = None
|
||||
license: str | None = None
|
||||
|
||||
@property
|
||||
def normalized_name(self) -> str:
|
||||
@@ -783,6 +822,7 @@ class Editor:
|
||||
date,
|
||||
last_tag=last_tag,
|
||||
tag=source_tag,
|
||||
license=attr.get("license"),
|
||||
)
|
||||
|
||||
plugins.append((pdesc, p))
|
||||
@@ -1112,6 +1152,11 @@ def prefetch_plugin(
|
||||
)
|
||||
if cached_plugin is not None:
|
||||
log.debug(f"Cache hit for {p.name}!")
|
||||
license_spdx_id = (
|
||||
cached_plugin.license
|
||||
or (current_plugin.license if current_plugin else None)
|
||||
or p.repo.get_license_spdx_id()
|
||||
)
|
||||
return (
|
||||
replace(
|
||||
cached_plugin,
|
||||
@@ -1121,6 +1166,7 @@ def prefetch_plugin(
|
||||
date=date,
|
||||
last_tag=latest_tag,
|
||||
tag=source_tag,
|
||||
license=license_spdx_id,
|
||||
),
|
||||
p.repo.redirect,
|
||||
)
|
||||
@@ -1132,6 +1178,11 @@ def prefetch_plugin(
|
||||
if source_tag
|
||||
else p.repo.prefetch(commit)
|
||||
)
|
||||
license_spdx_id = (
|
||||
current_plugin.license
|
||||
if current_plugin and current_plugin.license
|
||||
else p.repo.get_license_spdx_id()
|
||||
)
|
||||
|
||||
return (
|
||||
Plugin(
|
||||
@@ -1143,6 +1194,7 @@ def prefetch_plugin(
|
||||
date=date,
|
||||
last_tag=latest_tag,
|
||||
tag=source_tag,
|
||||
license=license_spdx_id,
|
||||
),
|
||||
p.repo.redirect,
|
||||
)
|
||||
@@ -1249,6 +1301,7 @@ class Cache:
|
||||
attr.get("version", ""),
|
||||
last_tag=attr.get("last_tag"),
|
||||
tag=attr.get("tag"),
|
||||
license=attr.get("license"),
|
||||
)
|
||||
downloads[cache_key] = p
|
||||
return downloads
|
||||
|
||||
Reference in New Issue
Block a user