reuseify
Automate REUSE license annotation from git history.
reuseify inspects which files are missing license headers (via reuse lint),
looks up their git commit authors, and applies reuse annotate, all from a single CLI.
Installation
uv pip install .
Usage
The workflow is two steps: collect authors → annotate files.
Step 1: collect authors
reuseify get-authors [OPTIONS]
Runs reuse lint, finds every file missing a license header, looks up its git
commit authors, and writes a JSON file:
{
"src/foo.py": ["Alice", "Bob"],
"src/bar.c": ["Alice"],
"src/new.py": [] #NOT_IN_GIT
}
| Option | Short | Default | Description |
|---|---|---|---|
--output |
-o |
reuse_annotate_authors.json |
Output JSON file |
--include-not-in-git |
-i |
off | Include files with no git history (empty author list) |
--exclude PATTERN |
-e |
Extra glob pattern to exclude (matched per path component, repeatable) |
Files matching built-in patterns are always excluded:
__pycache__, .venv, venv, .env, env, .git, .vscode, .idea,
*.egg-info, *.pyc, dist, build, node_modules, .tox,
.mypy_cache, .pytest_cache, .ruff_cache.
Files ignored by .gitignore are also excluded
automatically.
Examples
# defaults
reuseify get-authors
# custom output path + include untracked files
reuseify get-authors --output authors.json --include-not-in-git
# add an extra exclusion pattern
reuseify get-authors --exclude reports --exclude "*.tmp"
Step 2: annotate files
reuseify annotate [OPTIONS] [REUSE ANNOTATE FLAGS...]
Reads the JSON file from Step 1 and calls reuse annotate for every file.
--contributor flags are injected automatically from the JSON data. Both --copyright and
--license must still be passed, otherwise the command fails fast, since reuse lint
requires both a copyright notice and a license identifier (contributor alone isn't enough).
All unrecognised flags are forwarded verbatim to reuse annotate, giving you
full control over --copyright, --license, --year, --style,
--fallback-dot-license, --force-dot-license, --skip-unrecognised, etc.
| Option | Short | Default | Description |
|---|---|---|---|
--input |
-i |
reuse_annotate_authors.json |
JSON file from get-authors |
--default-contributor NAME |
-d |
none | Fallback contributor for NOT_IN_GIT files (repeatable) |
--download |
-D |
off | Download missing license files (reuse download --all) after annotating |
Output is grouped: all successes first, then skips, then failures, then finally a summary.
Examples
# basic
reuseify annotate \
--copyright "2025 X-Men" \
--license Apache-2.0 \
--fallback-dot-license
# custom input + fallback contributor for untracked files
reuseify annotate \
--input authors.json \
--default-contributor "Charles Xavier" \
--copyright "2025 X-Men" \
--license Apache-2.0
# multiple default contributors
reuseify annotate \
--default-contributor "Professor X" \
--default-contributor "Cyclops" \
--copyright "2025 X-Men" \
--license MIT
Check compliance: lint
reuseify lint [OPTIONS]
Runs reuse lint to find git-tracked files missing a REUSE header or
referencing a license whose text isn't in LICENSES/. If a reuseify.toml
policy file exists (see below), it also checks that each governed file's
actual declared license and copyright match its assigned rule, not just
that some valid header is present.
| Option | Short | Default | Description |
|---|---|---|---|
--include-not-in-git |
-i |
off | Include files with no git history in the check |
--exclude PATTERN |
-e |
none | Extra glob pattern to exclude (matched per path component, repeatable) |
Exit codes: 0 compliant, 1 REUSE or policy violations found, 2 an
underlying reuse/reuseify tool failure (never treated as "compliant").
reuseify.toml: per-path license policy
For projects that use more than one license across different directories,
add a reuseify.toml at the project root. It replaces the need to pass
--copyright/--license on the command line, and turns reuseify lint into
a stricter check: it verifies each governed file has the correct license
for its path, not just some valid license.
[[rules]]
paths = ["src/**"]
copyright = "Sahil Jhawar"
license = "GPL-3.0-or-later"
[[rules]]
paths = ["vendor/**", "third_party/**"]
copyright = "Some Vendor"
license = "MIT"
# a rule matching an exact file always wins over a directory glob
[[rules]]
paths = ["vendor/special_file.py"]
copyright = "Sahil Jhawar"
license = "GPL-3.0-or-later"
[default]
copyright = "Sahil Jhawar"
license = "GPL-3.0-or-later"
[[rules]]:pathsis a glob (or list of globs) matched against the file's git-relative path. When a file matches more than one rule, the most specific one wins: an exact file path beats a directory glob, and among glob patterns, the longer one wins.[default]: used for any tracked file that matches no rule. Files matching neither a rule nor[default]are not governed by the policy and are ignored by bothlintandannotate.- Leave the year out of
copyright("Sahil Jhawar", not"2026 Sahil Jhawar"):reuse annotatealways prepends the current year itself, so including one produces a duplicated year in the header.
With a reuseify.toml in place, reuseify annotate no longer requires
--copyright/--license on the command line; each file is annotated with
its matched rule's values automatically:
reuseify get-authors
reuseify annotate --default-contributor "Charles Xavier"
Any --copyright/--license still passed on the command line is used only
as a fallback, for fields a matched rule or [default] leaves unset.
paths glob matching uses fnmatch, which is case-sensitive on POSIX/macOS
and case-insensitive on Windows. reuseify only targets POSIX/Unix/macOS
(see the classifiers in pyproject.toml), so write patterns case-exact with
forward slashes.
Pre-commit hook
reuseify ships a pre-commit hook that runs reuseify lint
and fails the commit if any git-tracked file is missing a REUSE license header.
Add this to your .pre-commit-config.yaml:
repos:
- repo: https://github.com/sahiljhawar/reuseify
rev: 1.0.2 # use the latest tag
hooks:
- id: reuseify-lint
Then install it once per clone:
pre-commit install
Disclaimer
reuse annotate --help
This project is not affiliated with the REUSE project or its maintainers in any way.
Metadata
Release files for reuseify 1.0.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| reuseify-1.0.2.tar.gz | 32.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| reuseify-1.0.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 62.0 kB
Release files / reuseify-1.0.2.tar.gz
| Download URL | reuseify-1.0.2.tar.gz |
|---|---|
| Size | 32.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9a9a6a8cc969afb0275da87a70b26be71e1e81de9b6d0b73f347fe3a24dcad74
|
|
BLAKE2b-256 checksum How to use checksums |
87cf6ca0b661baae8fdbc7a380bce6fe2551ac1719139734ac7f19a3d9e8befb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Release files / reuseify-1.0.2-py3-none-any.whl
| Download URL | reuseify-1.0.2-py3-none-any.whl |
|---|---|
| Size | 29.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a6f98cffaa3954444525e307120daa2972f4b14001dcf1faad0e93d14524a825
|
|
BLAKE2b-256 checksum How to use checksums |
f413fa61a4fb5531e685ec640e4684b8ba691500e9ea74a6a22bbbb221e60efb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|