This release is a pre-release and may not be stable for production use.
Lint My Headers
A Rust CLI that checks Python copyright/license headers and safely refreshes recognized stale years. It never chooses ownership or licensing, inserts missing headers, or claims legal/SPDX/REUSE compliance.
Quick start
Install a published release, or build this checkout with Rust 1.93:
uv tool install lint-my-headers
# From source:
cargo build --release --locked
PyPI wheels contain native lmh and lint-my-headers executables; source distributions require Rust. Python 3.11+ is needed only for PyPI tooling and the optional launchers. python -m lint_my_headers replaces Python with the native process on Unix; Windows starts a child process. The callable main(argv) always returns the child's exit code. Checking and fixing run entirely in Rust.
Declare the policy in pyproject.toml:
[tool.lint-my-headers]
owner = "Example Organization"
starting-year = 2024
license = "Apache-2.0"
paths = ["src", "tests"]
ignore-files = ["version.py"]
ignore-folders = ["src/generated"]
lmh check # Read-only; configured paths
lmh check src/package tests/test_api.py # Explicit paths
lmh fix # Safe stale-year repairs only
lmh --version
lmh check --help
A valid header in 2026:
# Copyright (C) 2024-2026, Example Organization.
# This program is licensed under the Apache License 2.0.
# See LICENSE or go to <https://www.apache.org/licenses/LICENSE-2.0> for full license details.
Diagnostics name the location, reason, and repair eligibility:
src/example.py:1:1: LMH004 copyright year ends at 2025; expected 2026 [fixable]
Exit codes: 0 clean, 1 unresolved findings, 2 invocation/configuration/I/O error. fix preserves the creation year and changes no other bytes. Unknown, missing, malformed, duplicate, wrong-owner, or future-dated headers require manual review.
Configuration and safety
CLI policy options override configuration; --config selects another pyproject.toml. Configured paths are relative to that file; explicit CLI paths are relative to the invocation directory.
| Key / CLI option | Meaning and default |
|---|---|
owner / --owner |
Required exact single-line copyright owner. |
starting-year / --starting-year |
Required earliest accepted file creation year. |
license / --license |
SPDX identifier selecting a prose notice; requires local LICENSE. |
license-notice / --license-notice |
Custom notice file; configure exactly one license source. |
paths / positional paths |
Selected files or directories; default .. |
ignore-files / --ignore-files |
Exact basenames; default __init__.py. |
ignore-folders / --ignore-folders |
Excluded subtrees; default .github. |
CLI ignore lists are comma-separated; configuration lists are TOML arrays. Exclusions also apply to explicit inputs. Diagnostics use sorted, project-relative / paths, including .. for explicitly selected external files.
Python shebangs, UTF-8 BOMs, and PEP 263 cookies are preserved. Verified encodings are UTF-8, ASCII, Latin-1, and Windows-1252; other codecs fail without repair. Ambiguous newer Python string syntax also fails closed.
Directory discovery skips symlinks/reparse points. Explicit linked files may be checked, but repairs refuse symlinks, linked parents, reparse points, and multiple hard links. Before atomic replacement, file identity and contents are revalidated; the repaired bytes must pass the same parser.
The bundled SPDX snapshot is v3.28.0. All previously accepted v3.17 names/URLs remain valid, including the legacy KiCad-libraries-exception. Compound SPDX expressions and new exception semantics are unsupported.
Agents and JSON
Use lmh check --output-format json. Successfully parsed JSON-mode commands write only JSON to stdout; malformed CLI syntax remains a stderr usage error.
{
"schema_version": 1,
"tool_version": "0.6.0",
"command": "check",
"config_path": "pyproject.toml",
"checked": 1,
"changed": [],
"diagnostics": [
{
"path": "src/example.py",
"line": 1,
"column": 1,
"code": "LMH004",
"message": "copyright year ends at 2025; expected 2026",
"fixable": true
}
],
"expected_header": "# Copyright (C) <FILE_CREATION_YEAR>-2026, Example Organization.\n...",
"error": null
}
| Code | Meaning |
|---|---|
LMH001 |
Missing header |
LMH002 |
Owner mismatch |
LMH003 |
Invalid, reversed, future, or out-of-policy year |
LMH004 |
Recognized stale year; repairable only when fixable is true |
LMH005 |
Missing/mismatched license notice |
LMH006 |
Malformed, misplaced, duplicated, or ambiguous layout |
LMH007 |
Unsupported encoding or invalid bytes |
LMH008 |
Unsafe or concurrently changed repair target |
LMH900 |
Command/configuration/I/O error, in error rather than diagnostics |
Copy this instruction into an agent task:
Read the declared owner, starting year, license, and paths; ask if any are missing.
Run lmh check --output-format json. Exit 1 means findings; exit 2 means stop.
Only run lmh fix when source changes are authorized, then recheck and inspect the
targeted diff. Never infer legal facts or install, commit, or push without permission.
The optional agent skill uses this same contract. Its retained model-evaluation results are historical Python-era evidence, not Rust evaluations.
Integrations
For pre-commit or prek, install a published wheel in the hook's isolated Python 3.11+ environment. Replace <RELEASE_VERSION> with an exact published version; --only-binary prevents an unexpected Rust build:
repos:
- repo: local
hooks:
- id: lmh
name: Lint My Headers
entry: lmh check
language: python
types: [python]
additional_dependencies:
- --only-binary=lint-my-headers
- lint-my-headers==<RELEASE_VERSION>
To test an unreleased checkout instead, the first-party source hook remains available:
repos:
- repo: https://github.com/frgfm/lint-my-headers
rev: <TAG_OR_IMMUTABLE_SHA>
hooks:
- id: lmh
The source hook compiles Rust on first installation with the pinned toolchain. Pre-commit's Rust installer does not pass --locked; the Action's source mode does.
Pull-request CI can run the same hook or the GitHub Action:
steps:
- uses: actions/checkout@v7
- uses: frgfm/lint-my-headers@<RELEASE_TAG_OR_SHA>
The Action reads repository policy; its documented inputs override it. By default it uses uv to install the exact PyPI version declared by the Action ref, with source builds disabled. version: '0.6.0' can select an exact published version; version: source explicitly builds the checked-out Action code using Cargo.lock. Floating versions, missing releases, and unavailable wheels fail rather than falling back to a compiler. Prefer immutable release SHAs. Until publication, use source mode for local/unreleased refs; the wheel recipes above are release-gated, not claims that v0.6 is already on PyPI.
Action outputs are compact JSON arrays: issues contains unresolved paths; changed contains completed writes. On exit 2, issues stays [], while changed retains any completed repairs.
The optional January 1 workflow opens or updates one reviewable year-refresh PR. It never writes directly to protected main.
Maintenance
See CONTRIBUTING.md for checks and RELEASE_NOTES.md for the breaking rename and publication gates. Runtime logic is Rust; the Python package contains only launchers and source data. Licensed under Apache-2.0.
Metadata
Release files for lint-my-headers 0.6.0rc1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| lint_my_headers-0.6.0rc1.tar.gz | 117.8 kB | Details |
Built distributions (wheels)
| File | Reset | |||
|---|---|---|---|---|
| lint_my_headers-0.6.0rc1-py3-none-win_arm64.whl | Python 3 | none | Windows ARM64 | Details |
| lint_my_headers-0.6.0rc1-py3-none-win_amd64.whl | Python 3 | none | Windows x86-64 | Details |
| lint_my_headers-0.6.0rc1-py3-none-musllinux_1_2_x86_64.whl | Python 3 | none | Linux musl 1.2+ x86-64 | Details |
| lint_my_headers-0.6.0rc1-py3-none-musllinux_1_2_aarch64.whl | Python 3 | none | Linux musl 1.2+ ARM64 | Details |
| lint_my_headers-0.6.0rc1-py3-none-manylinux_2_28_x86_64.whl | Python 3 | none | Linux glibc 2.28+ x86-64 | Details |
| lint_my_headers-0.6.0rc1-py3-none-manylinux_2_28_aarch64.whl | Python 3 | none | Linux glibc 2.28+ ARM64 | Details |
| lint_my_headers-0.6.0rc1-py3-none-macosx_11_0_arm64.whl | Python 3 | none | macOS 11.0+ ARM64 | Details |
| lint_my_headers-0.6.0rc1-py3-none-macosx_10_12_x86_64.whl | Python 3 | none | macOS 10.12+ x86-64 | Details |
Total release size: 21.6 MB
Release files / lint_my_headers-0.6.0rc1.tar.gz
| Download URL | lint_my_headers-0.6.0rc1.tar.gz |
|---|---|
| Size | 117.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
69da05b298212a3b62acc785d77ecfdfc6c22b210e8713258807949d27defc16
|
|
BLAKE2b-256 checksum How to use checksums |
1c85b953b172b3e238ded17fd9964e9641bfa00baca42eb0b3588340b8b1b5d0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / lint_my_headers-0.6.0rc1-py3-none-win_arm64.whl
| Download URL | lint_my_headers-0.6.0rc1-py3-none-win_arm64.whl |
|---|---|
| Size | 2.5 MB |
| Tags | Python 3 Windows ARM64 |
|
SHA-256 checksum How to use checksums |
55c8927ce049cf1c6c5170da3c8742c5463a000a833c3f97e4ba90406730477e
|
|
BLAKE2b-256 checksum How to use checksums |
712590fb9134e982456a1a2c872a50951409183ad6902592554920a382966b89
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / lint_my_headers-0.6.0rc1-py3-none-win_amd64.whl
| Download URL | lint_my_headers-0.6.0rc1-py3-none-win_amd64.whl |
|---|---|
| Size | 2.6 MB |
| Tags | Python 3 Windows x86-64 |
|
SHA-256 checksum How to use checksums |
b147d0e9662af7033326a48bf40bab322d19637ed64f598fe5ca7422541eca76
|
|
BLAKE2b-256 checksum How to use checksums |
7792141f5a4853807450a9a39c7648383b5214965ccab2e9c7a85e96b6f272aa
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / lint_my_headers-0.6.0rc1-py3-none-musllinux_1_2_x86_64.whl
| Download URL | lint_my_headers-0.6.0rc1-py3-none-musllinux_1_2_x86_64.whl |
|---|---|
| Size | 3.0 MB |
| Tags | Linux musl 1.2+ x86-64 Python 3 |
|
SHA-256 checksum How to use checksums |
9621b1bdb1e6fbdbaae3e9d5e8c24ffda95530ee5d8bb2d5bfbae6c0a45af6c9
|
|
BLAKE2b-256 checksum How to use checksums |
3e0bf4204691640ecd929d95f048a0689f2f7ae1020a14ea6f2bed8497726315
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / lint_my_headers-0.6.0rc1-py3-none-musllinux_1_2_aarch64.whl
| Download URL | lint_my_headers-0.6.0rc1-py3-none-musllinux_1_2_aarch64.whl |
|---|---|
| Size | 2.7 MB |
| Tags | Linux musl 1.2+ ARM64 Python 3 |
|
SHA-256 checksum How to use checksums |
bcafb207000b28aacedc936c323f2374d90fcbd0a290ed92d6556be109f8e9d5
|
|
BLAKE2b-256 checksum How to use checksums |
6abcfef679e98775cb68f08f2b841758e7b0935dba1e237ef999fab406338cc9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / lint_my_headers-0.6.0rc1-py3-none-manylinux_2_28_x86_64.whl
| Download URL | lint_my_headers-0.6.0rc1-py3-none-manylinux_2_28_x86_64.whl |
|---|---|
| Size | 2.8 MB |
| Tags | Linux glibc 2.28+ x86-64 Python 3 |
|
SHA-256 checksum How to use checksums |
40831d08d963659e43d2a6b06869e999c8485003ab4879e8a6fbc62fff1ec1c1
|
|
BLAKE2b-256 checksum How to use checksums |
4f1c95e44a26cc9b117df532582f839a1fd419ac3a79648dc4a26af4be094ca9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / lint_my_headers-0.6.0rc1-py3-none-manylinux_2_28_aarch64.whl
| Download URL | lint_my_headers-0.6.0rc1-py3-none-manylinux_2_28_aarch64.whl |
|---|---|
| Size | 2.7 MB |
| Tags | Linux glibc 2.28+ ARM64 Python 3 |
|
SHA-256 checksum How to use checksums |
cee6413b835de2cb31956719b83248bf473e0485da7a0cacb114c32f5ed381d5
|
|
BLAKE2b-256 checksum How to use checksums |
e40207a327f6e51b350026906950191826a527647764786d3ff88e872c52e879
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / lint_my_headers-0.6.0rc1-py3-none-macosx_11_0_arm64.whl
| Download URL | lint_my_headers-0.6.0rc1-py3-none-macosx_11_0_arm64.whl |
|---|---|
| Size | 2.6 MB |
| Tags | Python 3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
6baceff9d7eff1cdd9aa3355393011f991ede4f80913bfd97e44d08a4ef92e39
|
|
BLAKE2b-256 checksum How to use checksums |
7724a0b01afd70d649327f69538e5dd945f4e959b396d21d5ece9994ebbef066
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / lint_my_headers-0.6.0rc1-py3-none-macosx_10_12_x86_64.whl
| Download URL | lint_my_headers-0.6.0rc1-py3-none-macosx_10_12_x86_64.whl |
|---|---|
| Size | 2.7 MB |
| Tags | Python 3 macOS 10.12+ x86-64 |
|
SHA-256 checksum How to use checksums |
02afeabb0046f5d04e97ff046246b7d9bbf48d440899f48d47895c11396e727a
|
|
BLAKE2b-256 checksum How to use checksums |
0c0b4e3aa44a1b206dfeba08bef111980d8302cd7fcf6d34cc155219f0bdf155
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|