RFC: REUSE compliance plugin for PkgTemplates.jl — API, docs, and root LICENSE behavior

Yeah, that’s really the crux of it. I’m all for reasonable, but your value of “surprising” may be different than mine or Acme Inc’s legal staff or Globex Corp’s internal SBOM audit. If I see prominent labels like this on a repository, I might not go looking any farther:

That’s what I’d think the GitHub “repository license” notice is.

That said, I do think we need to find ways to do better here. I’d love to similarly expose better mixed licensing information for packages with artifacts (like JLLs). There it’s typically far more surprising.

This just seems like a very bad rationale — that requirement is there to ensure folks know how to responsibly use a package. If it’s not accurate or representative, then it becomes less useful.

I think I now see the tension more clearly.

There are two valid but different concerns here:

  1. For current Julia tooling and General registration, a conventional root LICENSE file is useful, and probably expected for the ordinary package case.

  2. At the same time, a root LICENSE file should not silently imply that the whole repository is under one license when the generated REUSE metadata assigns different licenses to different parts of the repository.

So I think the safest default for a PkgTemplates plugin is probably the boring
happy path:

  • generate a conventional root LICENSE;
  • make the default generated layout uniformly licensed;
  • add the corresponding REUSE metadata from the start.

In other words, Reuse() should probably generate a registry-friendly package
where the root LICENSE and the REUSE metadata tell the same licensing story.

More elaborate REUSE setups should remain possible, for example different
licenses for code, docs, assets, or generated artifacts. But once the user chooses
such a non-uniform setup, the plugin should avoid pretending that a single root
license describes the whole repository. In that case, the authoritative licensing
information should be the SPDX headers, REUSE.toml, and LICENSES/; a root
LICENSE should either be omitted or generated only by explicit user choice (actually I am not for it).

That would make the default simple and compatible, while still allowing REUSE to
do what it is meant to do for more detailed file-level licensing.

Note that this is possible, because if only license is filled all other license expressions will default to that unless divergent expressions are given.

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.

Update:
The PR has now been updated with the discussed changes in place.
An updated documentation-preview is also available.

Feedback is very welcome.

I am closing this PR for now.

The SPDX parsing, expression normalization, SPDX license-list handling, license-text checks, and REUSE policy logic have become more general-purpose than I originally intended for this PkgTemplates.jl plugin.

I think the better architecture is to move that functionality into a separate package, with its own API, tests, documentation, governance, and release process. A future PkgTemplates.jl integration can then be a much thinner layer focused only on project generation.

Thanks for the review and discussion so far.
Stay tuned for REUSE support!