Thanks to all participants here for their help. By now, I am fairly clear about how to proceed.
Additional Switches
The REUSE plugin needs two additional switches. The fine-tuning is a bit different from what I had originally expected:
root_license::Bool = true
license_approval::String = "code" # "strict" | "code" | "none"
Root License
If root_license=true — the default — then one of the following will happen:
- If there is a single plain license for all files in the project, i.e. no
WITH exception and effectively license == artifact_license == docs_license == docs_asset_license, then that license text is copied from LICENSES/ to the root directory as LICENSE.
- In all other cases,
LICENSE is written as a simple text file pointing to the REUSE layout, i.e. to SPDX file headers, REUSE.toml, and LICENSES/. This avoids pretending that a multi-license project has one single root license text.
License Approval Checking
license_approval controls optional checks against license approval metadata. The default is "code", which should match the usual expectations for Julia packages intended for General.
Invalid values should throw an error. They should not silently fall back to "none".
The behavior should be:
-
license_approval="code" requires that the primary code license expression has an OSI-approved path according to the bundled SPDX License List metadata. For example, MIT OR LicenseRef-X passes because the expression provides an OSI-approved path. WITH exceptions are rejected here for now, as a conservative interpretation.
-
license_approval="strict" includes the requirements of "code" and additionally requires all other license expressions to have either an OSI-approved or FSF-libre path according to the bundled SPDX metadata. For example, CC-BY-NC-4.0 would fail.
-
license_approval="none" disables OSI/FSF-libre approval checks and allows the broadest REUSE-compliant setup, including custom LicenseRef-* licenses and license exceptions.
A failed approval check does not mean that a license is invalid, non-free, or unsuitable. It only means that the requested approval property cannot be verified from the bundled metadata.
In my view, this is the most honest compromise: it nudges users toward the simple happy path and makes REUSE-compliant setup easy, while still allowing the full REUSE/SPDX model where users explicitly ask for it.
Examples
Hello REUSE
Template(;
plugins=[
!License,
Reuse(),
],
)
This sets up the package with everything under the "MIT" license. A copy of that license text will be available as LICENSES/MIT.txt and, because this is a single-license setup, also as root LICENSE.
REUSE.toml is written and SPDX file headers are added to files in src/, test/, and benchmark/, for example. A ## Licensingsection is added to README.md, naming MIT as the license for the code and pointing out that the project follows REUSE.
General registry ready
Template(;
plugins=[
!License,
GitHubActions(),
Reuse(;
license = "EUPL-1.2+"
),
],
)
This is similar to the previous example, but here the primary code license is EUPL-1.2+, a valid SPDX 2.3 license expression using the + operator for “or later”. The generated project will contain LICENSES/EUPL-1.2.txt, and that text is copied to root LICENSE.
Additionally, reuse lint is added to the GitHub Actions workflow.
Free-play
Template(;
plugins=[
!License,
GitHubActions(),
Reuse(;
license = "GPL-3.0-or-later",
artifact_license = "CC0-1.0",
docs_license = "CC-BY-SA-4.0",
license_approval = "strict"
),
],
)
This is similar to General registry ready, but now different license expressions are used. Code is licensed under GPL-3.0-or-later, artifacts under CC0-1.0, and documentation — including assets — under CC-BY-SA-4.0.
Since this is no longer a single-license setup, the root LICENSE file becomes a pointer to the REUSE layout instead of duplicating one license text.
With license_approval="strict", non-code license domains are also checked against approval/libre metadata. For example, a non-commercial documentation license such as CC-BY-NC-4.0 would fail this check.