Skip to content

REUSE 3.3 compliance

Every file in a repository should carry license and copyright information in a format that machines can parse. The REUSE specification (version 3.3) defines how to do this using SPDX headers, and provides a linter to verify compliance.

An SPDX header is a short block of structured comments placed at the top of a source file. It contains two machine-readable tags defined by the SPDX standard: one for copyright ownership and one for the license identifier. A typical header in a Python file looks like this:

# SPDX-FileCopyrightText: 2026 Jane Doe <jane@example.com>
#
# SPDX-License-Identifier: ISC

The SPDX-FileCopyrightText tag records who holds copyright and when. The SPDX-License-Identifier tag references a license from the SPDX license list by its short identifier, without needing to embed the full license text. Automated tools can scan these tags to produce license inventories and check compliance.

REUSE tracks licensing through two mechanisms that complement each other:

  1. SPDX headers embedded directly in source files (Python, YAML, JavaScript, Markdown)
  2. REUSE.toml for files that cannot contain comments (lock files, images, JSON, configuration)

A CI workflow then checks that every tracked file is accounted for. If something slips through, the build fails.

Add reuse to your development dependencies:

Terminal window
uv add --dev reuse

This file sits at the repository root and covers files that do not support inline comments. The format requires version = 1 and uses [[annotations]] blocks:

version = 1
[[annotations]]
path = [
".gitignore",
".python-version",
".releaserc.json",
"pyproject.toml",
"uv.lock",
"CHANGELOG.md",
"LICENSE.md",
"README.md",
]
SPDX-FileCopyrightText = "2026 Your Name <your@email.com>"
SPDX-License-Identifier = "ISC"

Group files under a single [[annotations]] block when they share the same license and copyright. Use separate blocks when they differ.

License texts go in a LICENSES/ directory at the repository root. The file name matches the SPDX identifier:

Terminal window
uv run reuse download ISC

This creates LICENSES/ISC.txt. If your project uses multiple licenses (for instance, code under ISC and documentation under CC-BY-4.0), download each one.

The reuse annotate command inserts headers in the correct comment syntax for each file type:

Terminal window
uv run reuse annotate --license ISC --copyright "Your Name <your@email.com>" src/my_package/__init__.py

For a Python file, this produces:

# SPDX-FileCopyrightText: 2026 Your Name <your@email.com>
#
# SPDX-License-Identifier: ISC

For Markdown files, the tool places SPDX tags inside the YAML frontmatter as comments:

---
# SPDX-FileCopyrightText: 2026 Your Name <your@email.com>
#
# SPDX-License-Identifier: ISC
title: Page title
---

For .mdx files that the tool does not recognize natively, pass --style=html:

Terminal window
uv run reuse annotate --style=html --license ISC --copyright "Your Name <your@email.com>" docs/src/content/docs/index.mdx
Terminal window
uv run reuse lint

The linter checks every file tracked by git. It reports which files lack license information, which licenses are referenced but missing from LICENSES/, and whether the REUSE.toml syntax is valid.

Add a GitHub Actions workflow that runs the REUSE linter on every push and pull request:

name: REUSE compliance
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
reuse:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: fsfe/reuse-action@v6

The fsfe/reuse-action runs reuse lint inside the container and fails the job if any file is not covered.

REUSE is license-agnostic. It works with any SPDX-recognized license. Some common choices for Python packages:

LicenseSPDX identifierPermits commercial useCopyleft
ISCISCYesNo
MITMITYesNo
Apache 2.0Apache-2.0YesNo
GPL 3.0GPL-3.0-onlyYesYes

The full list of identifiers is at spdx.org/licenses.

When you add a new file to the repository:

  • If it supports comments (.py, .yml, .js, .ts, .md), run reuse annotate on it
  • If it does not support comments (.json, .lock, images), add its path to REUSE.toml

Run uv run reuse lint locally before pushing to catch omissions early.