SPDX licence-list cache and validation.
Hex requires package: [licenses: ...] entries to be SPDX identifiers.
Buildroot's <NAME>_LICENSE strings are mostly SPDX-aligned but use a
few non-SPDX forms (GPL-2.0+ rather than GPL-2.0-or-later, etc.) and
occasionally lag behind the upstream list. The generator validates each
BR-supplied identifier against the canonical SPDX list and offers
Jaro-distance suggestions when one doesn't match.
Cache
The list (~100 KB) is fetched on first use to
$NERVES_ARTIFACTS_DIR/nbpr/spdx_licenses.json (same base as the BR
tarball cache; XDG_DATA_HOME-aware, falling back to ~/.local/share).
Subsequent calls read the cached file. Refresh with refresh!/0 or
rm the file.
No hardcoded BR-to-SPDX translation table — when BR says GPL-2.0+,
the suggestions surface GPL-2.0-or-later and friends, and the user
picks. This keeps maintenance to zero at the cost of one extra
decision per non-canonical licence.
Summary
Functions
Returns the absolute path the SPDX list is (or will be) cached at.
Ensures the SPDX list is cached locally and returns its path. Fetches
from https://spdx.org/licenses/licenses.json on first call.
Returns the canonical list of current SPDX licence IDs — deprecated
identifiers (GPL-2.0+, GPL-2.0, etc.) are filtered out so they
surface as suggestion candidates for their replacements rather than
passing validation. Hex publish rejects deprecated IDs, so accepting
them here would just push the failure later.
Forces a refresh of the cached SPDX list. Use after an SPDX release if validation rejects an identifier you know to be current.
Returns :ok when id is a valid SPDX licence identifier; otherwise
{:error, suggestions} with up to n ranked suggestions (default 3).
Functions
@spec cache_path() :: Path.t()
Returns the absolute path the SPDX list is (or will be) cached at.
@spec ensure_cached!() :: Path.t()
Ensures the SPDX list is cached locally and returns its path. Fetches
from https://spdx.org/licenses/licenses.json on first call.
@spec license_ids() :: [String.t()]
Returns the canonical list of current SPDX licence IDs — deprecated
identifiers (GPL-2.0+, GPL-2.0, etc.) are filtered out so they
surface as suggestion candidates for their replacements rather than
passing validation. Hex publish rejects deprecated IDs, so accepting
them here would just push the failure later.
@spec refresh!() :: Path.t()
Forces a refresh of the cached SPDX list. Use after an SPDX release if validation rejects an identifier you know to be current.
@spec validate(String.t(), pos_integer()) :: :ok | {:error, [String.t()]}
Returns :ok when id is a valid SPDX licence identifier; otherwise
{:error, suggestions} with up to n ranked suggestions (default 3).
Ranking weights case-insensitive longest-common-prefix heavily, with
Jaro distance as a tiebreaker. Prefix weighting matters because SPDX
IDs cluster into families (GPL-2.0-only, GPL-2.0-or-later, LGPL-…)
and users typing a non-canonical form (GPL-2.0+) almost always want a
sibling within the same family — pure edit-distance metrics rank
equal-length unrelated IDs (MPL-2.0) above the actual family members.