Skip to main content

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 the release wheel in the hook's isolated Python 3.11+ environment. --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==0.6.0

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@v0.6.0

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; the v0.6.0 release notes provide its commit SHA. Use source mode for local/unreleased refs.

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.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for lint-my-headers 0.6.0
File Size Uploaded
lint_my_headers-0.6.0.tar.gz 117.7 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for lint-my-headers 0.6.0
File
lint_my_headers-0.6.0-py3-none-win_arm64.whl Python 3 none Windows ARM64 Details
lint_my_headers-0.6.0-py3-none-win_amd64.whl Python 3 none Windows x86-64 Details
lint_my_headers-0.6.0-py3-none-musllinux_1_2_x86_64.whl Python 3 none Linux musl 1.2+ x86-64 Details
lint_my_headers-0.6.0-py3-none-musllinux_1_2_aarch64.whl Python 3 none Linux musl 1.2+ ARM64 Details
lint_my_headers-0.6.0-py3-none-manylinux_2_28_x86_64.whl Python 3 none Linux glibc 2.28+ x86-64 Details
lint_my_headers-0.6.0-py3-none-manylinux_2_28_aarch64.whl Python 3 none Linux glibc 2.28+ ARM64 Details
lint_my_headers-0.6.0-py3-none-macosx_11_0_arm64.whl Python 3 none macOS 11.0+ ARM64 Details
lint_my_headers-0.6.0-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.0.tar.gz

Download URL lint_my_headers-0.6.0.tar.gz
Size 117.7 kB
Tags Source
SHA-256 checksum
How to use checksums
9c329cf612a5f938364c7e1919bfcc6bebd056e9a28e8c8d9945d172aebf7485
BLAKE2b-256 checksum
How to use checksums
49a8205433f3092f753900d9bf09ad0bdc4167ff31be7df749f020e7c896d137
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.0-py3-none-win_arm64.whl

Download URL lint_my_headers-0.6.0-py3-none-win_arm64.whl
Size 2.5 MB
Tags Python 3 Windows ARM64
SHA-256 checksum
How to use checksums
335fef147996f065fb418277b633c4e6bf7583542a1480d1034fb5237b206a84
BLAKE2b-256 checksum
How to use checksums
75b88ea1c63ea9d57c0e81c5c13039e6bf8ca4ad67d44a655ae7f8b24bfc7d90
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.0-py3-none-win_amd64.whl

Download URL lint_my_headers-0.6.0-py3-none-win_amd64.whl
Size 2.6 MB
Tags Python 3 Windows x86-64
SHA-256 checksum
How to use checksums
b31461d4340b1703a0c2b7ba5f816a7e705aed239c19877060ce95f6730f98d5
BLAKE2b-256 checksum
How to use checksums
814aba02e30657bfb8d7a609fd4e6dd0689db31cb454bcc88fcf445cb7ab4cbd
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.0-py3-none-musllinux_1_2_x86_64.whl

Download URL lint_my_headers-0.6.0-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
1c923334054b2261aceff5d599ac06fd1b695fa252a60eb2be37b8a7506c9abc
BLAKE2b-256 checksum
How to use checksums
d26846badc53ef00efdce96d38f90be0f2b8dff145b46bf4ffaa8a9ce9f42d51
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.0-py3-none-musllinux_1_2_aarch64.whl

Download URL lint_my_headers-0.6.0-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
04b869ffce53c87f333e14bc44b151445636c819db01ef4a6d7ecdf626cac133
BLAKE2b-256 checksum
How to use checksums
a90185375b66c7f48b29a4716ddf2349440b7c3cf96ab10125cbd8db3f79b9d4
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.0-py3-none-manylinux_2_28_x86_64.whl

Download URL lint_my_headers-0.6.0-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
46ff642e749049067caedd3f8753b8a0ef2b56e01b14f42e982aae5e783b2c96
BLAKE2b-256 checksum
How to use checksums
a3e8bed4dcc40a18d57a71bdb8e48124e0c5f722b6108d308996514113aebab8
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.0-py3-none-manylinux_2_28_aarch64.whl

Download URL lint_my_headers-0.6.0-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
84ff1ee57703906099983db14c9bc837e6d24c13474c1e95b25e63863c4ba228
BLAKE2b-256 checksum
How to use checksums
8651d5ab37561b64727042111ab9e73dcdfaa80bf73b5658bb729a0fa75a8d9c
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.0-py3-none-macosx_11_0_arm64.whl

Download URL lint_my_headers-0.6.0-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
cae18db044984ac496cdc908f12c56fa7919cb1ef961817be07bd168372dffce
BLAKE2b-256 checksum
How to use checksums
d32ae26af1bb5f820633e81beaad752598c577c4db67b64264aa5b871d4318c4
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.0-py3-none-macosx_10_12_x86_64.whl

Download URL lint_my_headers-0.6.0-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
12704c3e3c3d74999b78b17346a6f8b3ba1de358cd291c00d98c19934c2940cc
BLAKE2b-256 checksum
How to use checksums
adebb9ebe442e2cb317cf8c526aeebf4b9d4084d0db88e98b4c11c2fb4f651e6
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 history Release notifications | RSS feed

0.7.0

9 release files

This release

0.6.0 This release

9 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page