Fast, minimal license header manager for monorepos and CI pipelines.
addlicense scans a directory tree, detects which files are missing a license header, and injects one. On the second run, it does nothing — already-licensed files are left untouched.
addlicense --license MIT .
No config required for the common case. See the Wiki for architecture decisions, advanced usage, and contributor guides.
Homebrew
brew install GregoireF/tap/addlicenseBinary — download from Releases (Linux, macOS, Windows — amd64 / arm64).
Docker — no installation needed:
docker run --rm -v "$PWD:/src" -w /src ghcr.io/gregoiref/addlicense --license MIT .Go install
go install github.com/GregoireF/addlicense/cmd/addlicense@latestNote: if you have google/addlicense installed via
go install, the binaries share the same name and the lastgo installwins. The two tools have incompatible CLIs (different flags and defaults). Install one or the other, or use the binary download / Docker approach to avoid the collision.
# Add MIT headers to the whole project
addlicense --license MIT --author "Acme Corp" .
# Check only — exit 1 with list of unlicensed files (useful in CI)
addlicense --check .
# Remove existing headers
addlicense --remove .
# Replace all headers with a new licence in one pass
addlicense --update --license Apache-2.0 --author "Acme Corp" .
# Preview changes without writing to disk
addlicense --dry-run .
# REUSE/FSFE compliance — emit SPDX-FileCopyrightText: instead of Copyright
addlicense --license MIT --reuse .
# Multiple copyright holders — comma-separated
addlicense --license MIT --author "Alice, Bob" .
# Multiple copyright holders from a file (one per line, # comments ignored)
addlicense --license MIT --author-file AUTHORS .
# Show what would change without writing (JSON Lines output, exit 1 if changes needed)
addlicense --diff .
# Generate SPDX 2.3 SBOM (EU Cyber Resilience Act compliance)
addlicense --sbom sbom.spdx .
addlicense --sbom - . | head # stdout
# Custom template
addlicense --template ./header.txt .
# Ignore paths
addlicense --ignore "dist,vendor,*.gen.go" .| Flag | Short | Default | Description |
|---|---|---|---|
--license |
-l |
MIT |
SPDX license identifier |
--author |
-a |
— | Copyright holder (comma-separated for multiple: "Alice, Bob") |
--year |
-y |
current year | Copyright year |
--template |
-t |
— | Path to a custom header template file |
--ignore |
-i |
see below | Comma-separated glob patterns to skip |
--check |
-c |
false |
Check mode — no writes, exit 1 if any file is missing a header |
--remove |
-R |
false |
Strip existing license headers |
--update |
-u |
false |
Replace headers in one pass (--remove + inject) |
--dry-run |
-n |
false |
Preview changes without writing to disk |
--diff |
false |
Emit JSON Lines with rendered header for each file that would change; no writes, exit 1 if any file would be modified | |
--verbose |
-v |
false |
Print every processed file, including already-licensed ones |
--quiet |
-q |
false |
Suppress all stdout; errors still go to stderr |
--format |
-f |
text |
Output format: text or json (JSON Lines) |
--workers |
NumCPU |
Parallel goroutines for file processing | |
--reuse |
-r |
false |
REUSE/FSFE mode — emit SPDX-FileCopyrightText: |
--year-range |
false |
Preserve original copyright year on --update — emits YYYY-YYYY range |
|
--dep5 |
false |
Generate .reuse/dep5 for files that cannot carry inline headers (images, binaries…) |
|
--author-file |
— | Path to a file listing copyright holders, one per line (mutually exclusive with --author) |
|
--sbom |
— | Write a SPDX 2.3 tag-value document to this path (- for stdout). Reads existing headers; no files modified. Mutually exclusive with --check/--remove/--update/--diff/--dep5/--dry-run |
|
--version |
Print version and build info |
Mutually exclusive: --verbose/--quiet · --check/--remove · --check/--update · --remove/--update · --diff/--check · --author/--author-file · --sbom/(most other modes)
Default ignore list: vendor, node_modules, .git, dist, build, *.pb.go, *.gen.go
--ignore matching rules: Each pattern is tested against the relative file path and the basename. ** doublestar patterns are supported (**/generated/**, docs/**/*.md). Simple glob patterns (*.pb.go) match files at any depth. Plain strings without wildcards also match as substrings (generated matches auto_generated.go).
Multi-author with --update: --update replaces the entire existing header with the new one. If --author "Alice, Bob" is set, the result is two copyright lines for Alice and Bob — the previous author is not preserved. To keep an existing author, include them in the --author list.
| Extensions | Comment style |
|---|---|
.go .ts .tsx .js .jsx .rs .swift .kt .scala .php .cs .proto .zig |
// line |
.java .c .cpp .h .css .scss |
/* block */ |
.html .vue .svelte .md .mdx |
<!-- block --> |
.py .sh .bash .yaml .yml .tf .toml .rb .nix .dockerfile .mk |
# line |
.sql .lua |
-- line |
Request a language — adding one takes < 5 minutes (see Wiki: Adding a Language).
Any SPDX identifier works. Built-in templates:
MIT · Apache-2.0 · GPL-3.0-only · AGPL-3.0-only · LGPL-2.1-only · LGPL-3.0-only · EUPL-1.2 · MPL-2.0 · BSD-2-Clause · BSD-3-Clause
Any other identifier uses a generic template: Copyright <year> <author> / SPDX-License-Identifier: <id>.
Auto-detected in priority order: .addlicenserc.yaml → .addlicenserc.yml → .addlicenserc.json → addlicense.json. CLI flags always take precedence.
# .addlicenserc.yaml
license: Apache-2.0
author: Acme Corp
year: 2026
reuse: false
ignore:
- vendor
- node_modules
- "*.gen.go"GitHub Action (recommended)
- uses: GregoireF/addlicense-action@v1
with:
args: --check .Works on Linux, macOS, and Windows runners (amd64 + arm64). See Wiki: CI Integration for advanced examples.
pre-commit (since v0.4.0 — binary installed automatically)
repos:
- repo: https://github.com/GregoireF/addlicense
rev: v0.4.0
hooks:
- id: addlicense-checkDocker (no install)
- name: Check license headers
run: |
docker run --rm -v "$PWD:/src" -w /src \
ghcr.io/gregoiref/addlicense:latest --check .More examples (GitLab CI, curl install, Makefile, JSON output) → Wiki: CI Integration.
addlicense is built for speed. The worker pool scales linearly with CPU count — the default (--workers 0) uses all available cores.
| Scenario | Files | Time |
|---|---|---|
| Add headers (1 worker) | 200 | ~159 ms |
| Add headers (4 workers) | 200 | ~50 ms |
| Add headers (NumCPU = 20) | 200 | ~38 ms |
| Add headers | 1 000 | ~184 ms |
| Check only (all licensed) | 1 000 | ~55 ms |
HasHeader per file |
1 | ~27 µs |
Measured on an i7-12700F (20 logical cores), NVMe SSD, Go 1.23, Linux.
Run the benchmarks yourself:
go test -bench=. -benchtime=3s ./tests/bench/ # end-to-end pipeline
go test -bench=. -benchtime=3s ./internal/injector/ # injector micro-benchmarks--sbom <file> generates a minimal SPDX 2.3 tag-value document from the existing licence headers in your codebase. No files are modified.
# Generate sbom.spdx from current header state
addlicense --sbom sbom.spdx .
# Stream to stdout
addlicense --sbom - .
# Combine with --license to fill in a fallback for unlicensed files
addlicense --license MIT --sbom sbom.spdx .For each file the tool scans:
LicenseInfoInFile— theSPDX-License-Identifier:value found in the file header, orNOASSERTIONif none.LicenseConcluded— same as above; falls back to--licensewhen the file has no header.FileCopyrightText— the copyright line extracted from the header, orNOASSERTION.
The document satisfies the EU CRA requirement for file-level SPDX identification (ENISA CRA guidance, SPDX 2.3 file-element section). For a full dependency-graph SBOM (packages, purls, CVEs) use a dedicated tool such as syft or cdxgen alongside addlicense.
addlicense is importable as a Go library — no subprocess, no CLI:
import addlicense "github.com/GregoireF/addlicense/pkg/addlicense"
// Add MIT headers to all supported files under ./src
err := addlicense.Run(addlicense.Options{
License: "MIT",
Author: "Acme Corp",
Year: 2026,
Paths: []string{"./src"},
})
// Check mode — returns non-nil error if any file is missing a header
err = addlicense.Run(addlicense.Options{
CheckOnly: true,
Paths: []string{"."},
})All CLI flags map to Options fields. The public API is a distinct type from internal types — the internal/ packages may evolve without breaking callers.
// Extend the default ignore list without modifying it
opts := addlicense.Options{
License: "Apache-2.0",
Paths: []string{"."},
}
opts.Ignore = append(append([]string{}, addlicense.DefaultIgnore...), "testdata")See pkg/addlicense/addlicense.go for the full API reference and pkg.go.dev for documentation.
| Package | Description |
|---|---|
| GregoireF/addlicense-action | GitHub Action — add, check, and remove headers in CI |
| GregoireF/addlicense-npm | npm package — @gregoiref/addlicense for Node.js projects and CI |
| GregoireF/addlicense-winget | WinGet manifests — winget install GregoireF.addlicense |
| GregoireF/homebrew-tap | Homebrew tap — brew install GregoireF/tap/addlicense |
All ecosystem packages are versioned in sync with the core binary and updated automatically on each release.
Issues and PRs are welcome. Quick start:
git clone https://github.com/GregoireF/addlicense.git
cd addlicense
go test -race -coverprofile=coverage.out -coverpkg=./... ./...
golangci-lint runPlease follow Conventional Commits — enforced by CI. Full guide → CONTRIBUTING.md · Wiki.
MIT — see LICENSE.