{"id":37645223,"url":"https://github.com/eddieland/edlicense","last_synced_at":"2026-01-22T05:03:26.340Z","repository":{"id":332437379,"uuid":"940168339","full_name":"eddieland/edlicense","owner":"eddieland","description":"A tool that ensures source code files have copyright license headers. Features Git integration and automatic year updates. Recursively scans codebases with performance optimizations for large projects - an alternative to addlicense.","archived":false,"fork":false,"pushed_at":"2026-01-13T22:16:38.000Z","size":335,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-01-13T22:25:28.268Z","etag":null,"topics":["automation","cli","devops","rust"],"latest_commit_sha":null,"homepage":"https://github.com/eddieland/edlicense","language":"Rust","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/eddieland.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2025-02-27T18:12:53.000Z","updated_at":"2026-01-13T22:16:40.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/eddieland/edlicense","commit_stats":null,"previous_names":["eddieland/edlicense"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/eddieland/edlicense","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eddieland%2Fedlicense","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eddieland%2Fedlicense/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eddieland%2Fedlicense/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eddieland%2Fedlicense/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/eddieland","download_url":"https://codeload.github.com/eddieland/edlicense/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eddieland%2Fedlicense/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28655038,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-22T01:17:37.254Z","status":"online","status_checked_at":"2026-01-22T02:00:07.137Z","response_time":144,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["automation","cli","devops","rust"],"created_at":"2026-01-16T11:25:29.597Z","updated_at":"2026-01-22T05:03:26.334Z","avatar_url":"https://github.com/eddieland.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# edlicense\n\nA tool that ensures source code files have copyright license headers by scanning directory patterns recursively.\n\n`edlicense` modifies source files in place and avoids adding a license header to any file that already has one. It follows the Unix philosophy of tooling where possible and is designed with modern Rust best practices for high-performance CLI tools.\n\n## Philosophy\n\n`edlicense` is built for **large polyglot monorepos** (100k+ files) that lack centralized build systems like Bazel. Our guiding principles:\n\n- **Best-effort, not perfect**: We use fast heuristics adopted from [addlicense](https://github.com/google/addlicense), like checking for the word \"copyright\" in the first N characters rather than matching exact license text. This trades precision for speed.\n- **Human review is the safety net**: We assume every important file goes through code review where a human can catch formatting oddities. The goal is to get licenses on files reliably, not to handle every edge case perfectly.\n- **Heavy use of `.licenseignore`**: We expect you to liberally ignore files you don't care about or shouldn't add your license to (vendored code, generated files, assets, etc.).\n- **Git-first**: While `edlicense` works without a git repository, we're fundamentally a git-first tool. Features like `--git-only` (default in git repos) and `--ratchet` reflect this.\n\nIf you need byte-perfect license validation or work in a tightly controlled build environment, a build-system-integrated solution may serve you better. If you need something fast and pragmatic that scales to massive repos, `edlicense` is for you.\n\n## Why edlicense over addlicense?\n\n`edlicense` is inspired by Google's [addlicense](https://github.com/google/addlicense) tool but addresses several limitations:\n\n| Feature                | edlicense                                           | addlicense                                    |\n| ---------------------- | --------------------------------------------------- | --------------------------------------------- |\n| Implementation         | Rust                                                | Go                                            |\n| CLI interface          | Modern long options (--option-name)                 | Short flags (-flag)                           |\n| Default behavior       | Dry run mode (non-destructive)                      | Modify mode                                   |\n| Automatic year updates | ✅ Updates copyright years automatically            | ❌ No support for updating files w/ old years |\n| Ratchet mode           | ✅ Process only files changed since a git reference | ❌ Not available                              |\n| Git integration        | ✅ Option to only process git-tracked files         | ❌ Not available                              |\n\nKey advantages of `edlicense`:\n\n1. **Safety First**: Defaults to dry run mode, preventing accidental file modifications\n2. **Git Integration**:\n   - Ratchet mode for CI/CD pipelines to process only changed files\n   - Option to only process git-tracked files (default when in a git repository)\n3. **Automatic Updates**: Intelligently updates copyright years without manual intervention\n\n## Performance\n\nPerformance is an explicit goal for edlicense. We benchmark regularly against addlicense and work to identify optimization opportunities.\n\nIn our synthetic benchmarks processing thousands of files:\n\n| Scenario            | edlicense vs addlicense |\n| ------------------- | ----------------------- |\n| Small files (1KB)   | Comparable performance  |\n| Medium files (10KB) | ~1.5x faster            |\n| Large files (100KB) | ~2-4x faster            |\n\nBoth tools are fast enough for typical CI usage. Where edlicense tends to pull ahead is with larger codebases and larger source files, where I/O efficiency becomes more important.\n\nBenchmark methodology and raw results are available in `benchmarks/`. Performance characteristics vary by workload, file system, and hardware. Run your own benchmarks if performance is critical for your use case.\n\n## Features\n\n- Recursively scan directories and add license headers to source files\n- Automatic detection of file types and appropriate comment formatting\n- Dry run mode to verify license headers without modifying files (default behavior)\n- Ignore patterns to exclude specific files or directories (via CLI or `.licenseignore` files)\n- Support for `.licenseignore` files with gitignore-style pattern matching\n- Global ignore file support via `GLOBAL_LICENSE_IGNORE` environment variable\n- **Automatic year reference updates** - automatically updates copyright year references when the year changes (e.g., `(c) 2024` → `(c) 2025`)\n- **Ratchet mode** - only check and format files that have changed relative to a git reference (e.g., `origin/main`)\n- **Git repository integration** - option to only process files tracked by git (default when in a git repository)\n\n## Installation\n\n### From crates.io\n\n```bash\ncargo install edlicense\n```\n\n### From source\n\n```bash\ngit clone https://github.com/eddieland/edlicense.git\ncd edlicense\ncargo install --path .\n```\n\n### Using Docker\n\nedlicense is available as a Docker image, making it easy to run without installing Rust or any dependencies.\n\n#### Using Pre-built Images from GitHub Container Registry\n\nPre-built Docker images are automatically published to GitHub Container Registry when code is pushed to the main branch.\n\n```bash\n# Pull the latest production image\ndocker pull ghcr.io/eddieland/edlicense:latest\n\n# Pull the latest distroless image\ndocker pull ghcr.io/eddieland/edlicense:distroless-latest\n\n# Run using the production image\ndocker run --rm -v \"$(pwd):/workspace\" -w /workspace ghcr.io/eddieland/edlicense:latest src/\n\n# Run using the distroless image\ndocker run --rm -v \"$(pwd):/workspace\" -w /workspace ghcr.io/eddieland/edlicense:distroless-latest src/\n\n# Run in modify mode\ndocker run --rm -v \"$(pwd):/workspace\" -w /workspace ghcr.io/eddieland/edlicense:latest --modify src/\n```\n\nAvailable image tags:\n\n- `ghcr.io/eddieland/edlicense:latest` - Standard production image (latest version)\n- `ghcr.io/eddieland/edlicense:distroless-latest` - Distroless image (latest version)\n- `ghcr.io/eddieland/edlicense:\u003ccommit-hash\u003e` - Production image at a specific commit\n- `ghcr.io/eddieland/edlicense:distroless-\u003ccommit-hash\u003e` - Distroless image at a specific commit\n\n#### Building the Docker image locally\n\nThe project uses a single Dockerfile that can build production, distroless, and debug images:\n\n```bash\n# Build the lightweight production image (Debian-based)\nmake docker-build\n\n# Build the minimal distroless image\nmake docker-build-distroless\n\n# Build the debug/development image\nmake docker-build-debug\n\n# Build all images\nmake docker-build-all\n```\n\n#### Running with locally built Docker images\n\n```bash\n# Run edlicense on files in the current directory (dry run mode)\ndocker run --rm -v \"$(pwd):/workspace\" -w /workspace edlicense:latest src/\n\n# Using the make target (equivalent to above)\nmake docker-run ARGS=\"src/\"\n\n# Run with the distroless image (smaller and more secure)\nmake docker-run-distroless ARGS=\"src/\"\n\n# Run edlicense in modify mode\ndocker run --rm -v \"$(pwd):/workspace\" -w /workspace edlicense:latest --modify src/\n\n# Run with the debug image for development purposes\nmake docker-run-debug ARGS=\"cargo nextest run\"\n```\n\nThe Docker setup provides three image tags from the same Dockerfile:\n\n1. **Lightweight image** (`edlicense:latest`): A Debian-based image containing only the compiled binary and minimal dependencies, optimized for CI/CD pipelines and general production use.\n\n2. **Distroless image** (`edlicense:distroless`): An ultra-minimal image based on Google's distroless container, containing only the compiled binary and essential libraries with no shell or package manager. This provides the smallest possible attack surface and image size, ideal for security-sensitive deployments.\n\n3. **Debug image** (`edlicense:debug`): A development image containing the full Rust toolchain, source code, and development tools, useful for debugging and development.\n\nFor advanced Docker usage, including building downstream images and handling file permissions, see [Docker Usage Examples](examples/docker_usage.md).\n\n## Usage\n\n```\nedlicense [OPTIONS] \u003cPATTERNS\u003e...\n```\n\n### Arguments\n\n- `\u003cPATTERNS\u003e...` - File or directory patterns to process. Directories are processed recursively.\n\n### Options\n\n```\n--dry-run                     Dry run mode: only check for license headers without modifying files (default)\n--modify                      Modify mode: add or update license headers in files\n--show-diff                   Show diff of changes in dry run mode\n--save-diff \u003cFILE\u003e            Save diff of changes to a file in dry run mode\n--license-file \u003cLICENSE_FILE\u003e Custom license file to use\n--ignore \u003cIGNORE\u003e...          File patterns to ignore (supports glob patterns)\n--year \u003cYEAR\u003e                 Copyright year [default: current year]\n--verbose                     Verbose logging\n--ratchet \u003cREFERENCE\u003e         Ratchet mode: only check and format files that have changed relative to a git reference\n--preserve-years              Preserve existing years in license headers\n--global-ignore-file \u003cFILE\u003e   Path to a global license ignore file (overrides GLOBAL_LICENSE_IGNORE environment variable)\n--git-only                    Only consider files in the current git repository (default when in a git repository)\n--help                        Print help\n--version                     Print version\n```\n\nNote: `--dry-run` and `--modify` are mutually exclusive options. If neither is specified, dry run mode is used by default.\n\n## Examples\n\nCheck if all files have license headers without modifying them (dry run mode):\n\n```bash\nedlicense --dry-run src/ tests/\n```\n\nOr simply (since dry run is the default):\n\n```bash\nedlicense src/ tests/\n```\n\nShow diff of changes in dry run mode:\n\n```bash\nedlicense --show-diff src/ tests/\n```\n\nSave diff of changes to a file in dry run mode:\n\n```bash\nedlicense --save-diff=changes.diff src/ tests/\n```\n\nShow and save diff of changes:\n\n```bash\nedlicense --show-diff --save-diff=changes.diff src/ tests/\n```\n\nIgnore specific file patterns:\n\n```bash\nedlicense --ignore \"**/*.json\" --ignore \"vendor/**\" .\n```\n\nUse a specific year:\n\n```bash\nedlicense --year \"2020\" .\n```\n\nOnly check files that have changed relative to origin/main (dry run mode):\n\n```bash\nedlicense --ratchet \"origin/main\" src/\n```\n\nAdd or update license headers in files that have changed relative to origin/main:\n\n```bash\nedlicense --ratchet \"origin/main\" --modify src/\n```\n\nOnly process files tracked by git (this is the default when in a git repository):\n\n```bash\nedlicense --git-only src/\n```\n\nProcess all files, including those not tracked by git:\n\n```bash\nedlicense --git-only=false src/\n```\n\n## Automatic Year Updates\n\nUnlike the original `addlicense` tool, `edlicense` can automatically update copyright year references when the year changes. For example, if a file contains:\n\n```\nCopyright (c) 2024 Example Corp\n```\n\nAnd the current year is 2025, running `edlicense` will update it to:\n\n```\nCopyright (c) 2025 Example Corp\n```\n\n### Supported Copyright Formats\n\nTo avoid accidentally replacing arbitrary 4-digit numbers, year updates only apply to recognized copyright patterns:\n\n| Format               | Year Updated? | Example                        |\n| -------------------- | ------------- | ------------------------------ |\n| `Copyright (c) YEAR` | ✅ Yes        | `Copyright (c) 2024 Acme`      |\n| `Copyright © YEAR`   | ✅ Yes        | `Copyright © 2024 Acme`        |\n| `Copyright YEAR`     | ❌ No         | `Copyright 2024 Acme`          |\n| Year ranges          | ❌ No         | `Copyright (c) 2020-2024 Acme` |\n\nThe pattern matching is case-insensitive, so `COPYRIGHT (C) 2024` works the same as `Copyright (c) 2024`.\n\n**Why these restrictions?** The regex requires the `(c)` or `©` symbol to ensure we're matching an actual copyright statement, not some other 4-digit number in your code. Year ranges are intentionally preserved since updating `2020-2024` to `2025` would lose historical information.\n\nIf you use `--preserve-years`, no year updates will be performed regardless of format.\n\n## Ratchet Mode\n\nThe ratchet mode allows you to only check and format files that have changed relative to a git reference (e.g., `origin/main`). This is particularly useful in CI/CD pipelines where you want to ensure that only new or modified files have proper license headers.\n\nWhen using ratchet mode, `edlicense` will:\n\n1. Identify files that have been added, modified, or renamed since the specified git reference\n2. Only process those changed files, ignoring files that haven't changed\n3. Apply the same license checking or formatting rules to the changed files\n\nThis can significantly speed up processing in large repositories where only a small subset of files have changed.\n\nExample usage:\n\n```bash\n# Only check license headers in files changed since origin/main (dry run mode)\nedlicense --ratchet \"origin/main\" src/\n\n# Add license headers to files changed since a specific commit\nedlicense --ratchet \"abc123\" --modify --license-file LICENSE.txt src/\n```\n\n## Using .licenseignore Files\n\nYou can use `.licenseignore` files to specify patterns for files that should be ignored during license checking and updates, similar to how `.gitignore` files work:\n\n```bash\n# Create a .licenseignore file in your project\necho \"*.json\" \u003e .licenseignore\necho \"vendor/\" \u003e\u003e .licenseignore\necho \"**/node_modules/\" \u003e\u003e .licenseignore\n\n# Run edlicense (it will automatically use the .licenseignore file)\nedlicense src/\n```\n\nYou can also set a global ignore file using the `GLOBAL_LICENSE_IGNORE` environment variable:\n\n```bash\nexport GLOBAL_LICENSE_IGNORE=/path/to/global/licenseignore\nedlicense src/\n```\n\nFor more details and examples, see [.licenseignore Files](examples/licenseignore.md).\n\n## Git Repository Integration\n\nBy default, when running in a git repository, `edlicense` will only process files that are tracked by git. This helps ensure that only files that are part of your project get license headers, while ignoring build artifacts, temporary files, and other untracked files.\n\n\u003e **Important**: When git detection mode is enabled, `edlicense` uses your current working directory (`$CWD`) to determine whether it should only look at tracked files. You should always run edlicense from inside the git repository for correct operation.\n\nYou can control this behavior with the `--git-only` option:\n\n```bash\n# Only process files tracked by git (default when in a git repository)\nedlicense --git-only src/\n\n# Process all files, including those not tracked by git\nedlicense --git-only=false src/\n```\n\nThis feature works well with the ratchet mode, allowing you to focus only on files that are both tracked by git and have changed since a specific reference:\n\n```bash\n# Only process files that are tracked by git and have changed since origin/main\nedlicense --git-only --ratchet \"origin/main\" src/\n```\n\nIf you run `edlicense` from outside your git repository while using git detection mode, it will not be able to properly identify git-tracked files, which may result in no files being processed or incorrect files being processed.\n\nFor more details and examples, see [Git Integration](examples/git_integration.md).\n\nFor information on using edlicense in pre-commit hooks, see [Pre-commit Hooks](examples/pre_commit_hook.md).\n\n## Supported File Types\n\n`edlicense` supports a wide range of file types and automatically formats license headers with the appropriate comment style:\n\n### File Extensions by Comment Type\n\n| Comment Style                    | File Extensions and Types                                                                                                   |\n| -------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |\n| Block comments\u003cbr\u003e`/* ... */`    | `.c`, `.h`, `.gv`, `.java`, `.scala`, `.kt`, `.kts`                                                                         |\n| JSDoc comments\u003cbr\u003e`/** ... */`   | `.js`, `.mjs`, `.cjs`, `.jsx`, `.tsx`, `.css`, `.scss`, `.sass`, `.ts`                                                      |\n| Line comments\u003cbr\u003e`// ...`        | `.cc`, `.cpp`, `.cs`, `.go`, `.hcl`, `.hh`, `.hpp`, `.m`, `.mm`, `.proto`, `.rs`, `.swift`, `.dart`, `.groovy`, `.v`, `.sv` |\n| Hash comments\u003cbr\u003e`# ...`         | `.py`, `.sh`, `.yaml`, `.yml`, `.rb`, `.tcl`, `.tf`, `.bzl`, `.pl`, `.pp`, `.toml`                                          |\n| Lisp comments\u003cbr\u003e`;; ...`        | `.el`, `.lisp`                                                                                                              |\n| Erlang comments\u003cbr\u003e`% ...`       | `.erl`                                                                                                                      |\n| SQL/Haskell comments\u003cbr\u003e`-- ...` | `.hs`, `.sql`, `.sdl`                                                                                                       |\n| HTML comments\u003cbr\u003e`\u003c!-- ... --\u003e`  | `.html`, `.xml`, `.vue`, `.wxi`, `.wxl`, `.wxs`                                                                             |\n| Jinja2 comments\u003cbr\u003e`{# ... #}`   | `.j2`                                                                                                                       |\n| OCaml comments\u003cbr\u003e`(** ... *)`   | `.ml`, `.mli`, `.mll`, `.mly`                                                                                               |\n\n### Special File Handling\n\nIn addition to extensions, `edlicense` also handles special file types by name:\n\n- `cmakelists.txt`, `*.cmake.in`, `*.cmake`: Hash comments (`# ...`)\n- `dockerfile`, `*.dockerfile`: Hash comments (`# ...`)\n\n## Performance Testing\n\n`edlicense` includes performance tests to measure how efficiently it processes large numbers of files. These tests are useful for benchmarking and optimizing the tool's performance, especially for large codebases.\n\n### Running Performance Tests\n\nPerformance tests are disabled by default since they generate and process thousands of files. To run them, use the following Makefile targets:\n\n```bash\n# Run test for adding licenses to 10,000 files\nmake perf-test-add\n\n# Run test for updating license years in 10,000 files\nmake perf-test-update\n\n# Run test for checking license headers in 10,000 files\nmake perf-test-check\n\n# Run test with different file sizes\nmake perf-test-file-size\n\n# Run test with different thread counts\nmake perf-test-threads\n\n# Run comprehensive benchmark tests\nmake perf-benchmark\n\n# Run all performance tests (this may take a while)\nmake perf-test-all\n```\n\nAlternatively, you can run the tests directly with cargo:\n\n```bash\n# Run a specific performance test\ncargo nextest run --release -E 'test(test_add_license_performance)' --run-ignored=all --no-capture\n\n# Run all performance tests\ncargo nextest run --release --run-ignored=all --no-capture\n```\n\n### Performance Test Results\n\nThe performance tests measure:\n\n1. **Adding licenses** to large numbers of files (10,000+)\n2. **Updating years** in existing license headers\n3. **Checking for licenses** in check-only mode\n4. Impact of **file size** on processing time\n5. Impact of **thread count** on parallel processing performance\n\nResults are displayed in the console with timing information, making it easy to identify performance bottlenecks or improvements.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feddieland%2Fedlicense","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Feddieland%2Fedlicense","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feddieland%2Fedlicense/lists"}