{"id":14970209,"url":"https://github.com/hashicorp/copywrite","last_synced_at":"2026-04-02T11:53:53.587Z","repository":{"id":65469072,"uuid":"591026247","full_name":"hashicorp/copywrite","owner":"hashicorp","description":"Automate copyright headers and license files at scale","archived":false,"fork":false,"pushed_at":"2026-03-24T06:06:31.000Z","size":374,"stargazers_count":108,"open_issues_count":28,"forks_count":22,"subscribers_count":4,"default_branch":"main","last_synced_at":"2026-03-25T07:48:57.134Z","etag":null,"topics":["compliance","copyright","licensing","oss","spdx"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hashicorp.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","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":null,"dco":null,"cla":null}},"created_at":"2023-01-19T19:02:36.000Z","updated_at":"2026-03-25T02:33:01.000Z","dependencies_parsed_at":"2025-11-27T13:00:51.313Z","dependency_job_id":null,"html_url":"https://github.com/hashicorp/copywrite","commit_stats":{"total_commits":69,"total_committers":17,"mean_commits":"4.0588235294117645","dds":0.6956521739130435,"last_synced_commit":"6ed520a710166c6094098b786c63f212604654a4"},"previous_names":[],"tags_count":25,"template":false,"template_full_name":null,"purl":"pkg:github/hashicorp/copywrite","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hashicorp%2Fcopywrite","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hashicorp%2Fcopywrite/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hashicorp%2Fcopywrite/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hashicorp%2Fcopywrite/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hashicorp","download_url":"https://codeload.github.com/hashicorp/copywrite/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hashicorp%2Fcopywrite/sbom","scorecard":{"id":457003,"data":{"date":"2025-08-11","repo":{"name":"github.com/hashicorp/copywrite","commit":"84bf52a19cf669d01b6d5f8204af0a29834b090b"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":6.7,"checks":[{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Maintained","score":10,"reason":"22 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Code-Review","score":10,"reason":"all changesets reviewed","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: topLevel 'contents' permission set to 'write': .github/workflows/build-and-release.yml:8","Info: topLevel 'contents' permission set to 'read': .github/workflows/golangci.yml:10","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":10,"reason":"all dependencies are pinned","details":["Info:  12 out of  12 GitHub-owned GitHubAction dependencies pinned","Info:   3 out of   3 third-party GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: Mozilla Public License 2.0: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":0,"reason":"Project has not signed or included provenance with any releases.","details":["Warn: release artifact v0.22.0 not signed: https://api.github.com/repos/hashicorp/copywrite/releases/206744853","Warn: release artifact v0.21.0 not signed: https://api.github.com/repos/hashicorp/copywrite/releases/201343187","Warn: release artifact v0.20.0 not signed: https://api.github.com/repos/hashicorp/copywrite/releases/196628001","Warn: release artifact v0.19.0 not signed: https://api.github.com/repos/hashicorp/copywrite/releases/162117173","Warn: release artifact v0.18.0 not signed: https://api.github.com/repos/hashicorp/copywrite/releases/131790221","Warn: release artifact v0.22.0 does not have provenance: https://api.github.com/repos/hashicorp/copywrite/releases/206744853","Warn: release artifact v0.21.0 does not have provenance: https://api.github.com/repos/hashicorp/copywrite/releases/201343187","Warn: release artifact v0.20.0 does not have provenance: https://api.github.com/repos/hashicorp/copywrite/releases/196628001","Warn: release artifact v0.19.0 does not have provenance: https://api.github.com/repos/hashicorp/copywrite/releases/162117173","Warn: release artifact v0.18.0 does not have provenance: https://api.github.com/repos/hashicorp/copywrite/releases/131790221"],"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Security-Policy","score":10,"reason":"security policy file detected","details":["Info: security policy file detected: github.com/hashicorp/.github/SECURITY.md:1","Info: Found linked content: github.com/hashicorp/.github/SECURITY.md:1","Info: Found disclosure, vulnerability, and/or timelines in security policy: github.com/hashicorp/.github/SECURITY.md:1","Info: Found text in security policy: github.com/hashicorp/.github/SECURITY.md:1"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Branch-Protection","score":6,"reason":"branch protection is not maximal on development and all release branches","details":["Info: 'allow deletion' disabled on branch 'main'","Info: 'force pushes' disabled on branch 'main'","Warn: 'branch protection settings apply to administrators' is disabled on branch 'main'","Info: 'stale review dismissal' is required to merge on branch 'main'","Warn: required approving review count is 1 on branch 'main'","Info: codeowner review is required on branch 'main'","Info: 'last push approval' is required to merge on branch 'main'","Warn: no status checks found to merge onto branch 'main'","Info: PRs are required in order to make changes on branch 'main'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Packaging","score":10,"reason":"packaging workflow detected","details":["Info: Project packages its releases by way of GitHub Actions.: .github/workflows/golangci.yml:72"],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 30 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-19T09:58:40.880Z","repository_id":65469072,"created_at":"2025-08-19T09:58:40.880Z","updated_at":"2025-08-19T09:58:40.880Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31305895,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-02T09:48:21.550Z","status":"ssl_error","status_checked_at":"2026-04-02T09:48:19.196Z","response_time":89,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["compliance","copyright","licensing","oss","spdx"],"created_at":"2024-09-24T13:43:17.475Z","updated_at":"2026-04-02T11:53:53.581Z","avatar_url":"https://github.com/hashicorp.png","language":"Go","funding_links":[],"categories":["Go"],"sub_categories":[],"readme":"# copywrite\n\nThis repo provides utilities for managing copyright headers and license files\nacross many repos at scale.\n\nFeatures:\n\n- Add or validate copyright headers on source code files\n- Add and/or manage LICENSE files with git-aware copyright year detection\n- Report on licenses used across multiple repositories\n- Automate compliance checks in CI/CD pipelines\n\n## Getting Started\n\nThe easiest way to get started is to use Homebrew:\n\n```sh\nbrew tap hashicorp/tap\nbrew install hashicorp/tap/copywrite\n```\n\nInstallers for Windows, Linux, and MacOS are also available on the [releases](https://github.com/hashicorp/copywrite/releases) page.\n\n## CLI Usage\n\nThis Go app is consumable as a command-line tool. Currently, the following subcommands are available:\n\n```none\n❯ copywrite\nCopywrite provides utilities for managing copyright headers and license\nfiles in HashiCorp repos.\n\nYou can use it to report on what licenses repos are using, add LICENSE files,\nand add or validate the presence of copyright headers on source code files.\n\nUsage:\n  copywrite [command]\n\nCommon Commands:\n  headers     Adds missing copyright headers and updates existing headers' year information.\n  init        Generates a .copywrite.hcl config for a new project\n  license     Validates that a LICENSE file is present and remediates any issues if found\n\nAdditional Commands:\n  completion  Generate the autocompletion script for the specified shell\n  debug       Prints env-specific debug information about copywrite\n  dispatch    Dispatches audit jobs for a list of repos\n  help        Help about any command\n  report      Performs a variety of reporting tasks\n\nFlags:\n      --config string   config file (default is .copywrite.hcl in current directory)\n  -h, --help            help for copywrite\n  -v, --version         version for copywrite\n\nUse \"copywrite [command] --help\" for more information about a command.\n```\n\n### Automatic Copyright Holder Migration\n\nThe `copywrite headers` command automatically detects and updates old copyright\nholders (such as \"HashiCorp, Inc.\") to the configured holder (default: \"IBM Corp.\")\nwhile preserving existing year information and updating year ranges.\n\nThis ensures that:\n\n- Old headers from merged PRs are automatically corrected\n- Manually copied headers are updated\n- Year ranges are kept current\n\nNo additional flags are needed - the migration happens automatically as part of\nthe normal headers command execution.\n\nTo get started with Copywrite on a new project, run `copywrite init`, which will\ninteractively help generate a `.copywrite.hcl` config file to add to Git.\n\nThe most common command you will use is `copywrite headers`, which will automatically\nscan all files in your repo and copyright headers to any that are missing:\n\n```sh\ncopywrite headers --spdx \"MPL-2.0\"\n```\n\nThe `copywrite license` command validates and manages LICENSE files with git-aware copyright years:\n\n```sh\ncopywrite license --spdx \"MPL-2.0\"\n```\n\n**Copyright Year Behavior:**\n\n- **Start Year**: Auto-detected from config file and if not found defaults to repository's first commit\n- **End Year**: Set to current year when an update is triggered (git history only determines if update is needed)\n- **Update Trigger**: Git detects if source code file was modified since the copyright end year\n\nYou may omit the `--spdx` flag if you add a `.copywrite.hcl` config, as outlined [here](#config-structure).\n\n### `--plan` Flag\n\nBoth the `headers` and `license` commands allow you to use a `--plan` flag, which\nperforms a dry-run and will outline what changes would be made. This flag also\nreturns a non-zero exit code if any changes are needed. As such, it can be used\nto validate if a repo is in compliance or not.\n\n## Technical Details\n\n### Copyright Year Logic\n\n**Source File Headers:**\n\n- End year: Set to current year when file's source code is modified\n- Git history determines if update is needed (compares file's last commit year to copyright end year)\n- When triggered, end year updates to current year\n- If project.ignore_year1 is true, start-year updates are skipped\n\n**LICENSE Files:**\n\n- End year: Set to current year when any project file is modified\n- Git history determines if update is needed (compares repo's last commit year to copyright end year)\n- When triggered, end year updates to current year\n- Preserves historical accuracy for archived projects (no forced updates)\n- If project.ignore_year1 is true, start-year updates are skipped (same behaviour as source file headers)\n\n**Key Distinction:** Git history is used as a trigger to determine *whether* an update is needed, but the actual end year value is always set to the current year when an update occurs.\n\n## Config Structure\n\n\u003e :bulb: You can automatically generate a new `.copywrite.hcl` config with the\n`copywrite init` command.\n\nA `.copywrite.hcl` file can be referenced to provide configuration information\nfor a given project. This file should be specific to each repo and checked into\ngit. If no configuration file is present, default values will be used throughout\nthe `copywrite` application. An example config structure is shown below:\n\n```hcl\n# (OPTIONAL) Overrides the copywrite config schema version\n# Default: 1\nschema_version = 1\n\nproject {\n  # (OPTIONAL) SPDX-compatible license identifier\n  # Leave blank if you don't wish to license the project\n  # Default: \"MPL-2.0\"\n  license = \"MPL-2.0\"\n\n  # (OPTIONAL) Represents the copyright holder used in all statements\n  # Default: IBM Corp.\n  # copyright_holder = \"\"\n\n  # (OPTIONAL) Represents the year that the project initially began\n  # This is used as the starting year in copyright statements\n  # If set and different from current year, headers will show: \"copyright_year, year-2\"\n  # If set and same as year-2, headers will show: \"copyright_year\"\n  # If not set (0), the tool will auto-detect from git history (first commit year)\n  # If auto-detection fails, it will fallback to current year only\n  # Default: 0 (auto-detect)\n  # copyright_year = 0\n\n  # (OPTIONAL) Ignore updates to the first year (start year) in copyright ranges.\n  # This does not change how end year is resolved.\n  # Default: false\n  # ignore_year1 = false\n\n  # (OPTIONAL) A list of globs that should not have copyright or license headers .\n  # Supports doublestar glob patterns for more flexibility in defining which\n  # files or folders should be ignored\n  # Default: []\n  header_ignore = [\n    # \"vendor/**\",\n    # \"**autogen**\",\n  ]\n\n  # (OPTIONAL) Links to an upstream repo for determining repo relationships\n  # This is for special cases and should not normally be set.\n  # Default: \"\"\n  # upstream = \"hashicorp/\u003cREPONAME\u003e\"\n}\n\n```\n\n## GitHub Authentication\n\nSome commands interact directly with GitHub's API (especially when a\n`.copywrite.hcl` config is not present for the project). In order to use these\ncommands successfully, multiple mechanisms are available to provide GitHub\ncredentials and are prioritized in the following order:\n\n- GitHub App credentials can be supplied via the `APP_ID`, `INSTALLATION_ID`, and `APP_PEM` environment variables or a `.env` file.\n- A `GITHUB_TOKEN` environment variable can be used with a Personal Access Token\n- If you use the [GitHub CLI](https://cli.github.com/), auth information can automatically be used\n\nIf none of the above methods work, `copywrite` will default to using an **unauthenticated** client.\n\nGitHub credentials are purposely excluded from the `.copywrite.hcl` config, as\nthat file is meant to be specific to each project and checked in to its repo.\n\n## GitHub Action\n\nTo make it easier to use `copywrite` in your own CI jobs (e.g., to add a PR check),\nyou can make use of the [hashicorp/setup-copywrite](https://github.com/marketplace/actions/setup-copywrite) GitHub Action. It\nautomatically installs the binary and adds it to your `$PATH` so you can call it\nfreely in later steps.\n\nNote: Using fetch-depth parameter is mandatory as the tool will not be able to effectively resolve the year information without it.\n\n**Impact of not updating year information:**\nIf year information is not updated time to time, then the repo can be out of compliance. IBM policy suggests keeping source code files updated with latest year of code changes in a source code file.\n\n```yaml\n  - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7\n    with:\n        fetch-depth: 0 # As git operations are involved, we will need a deep clone of the repo.\n\n  - name: Setup Copywrite\n    uses: hashicorp/setup-copywrite@867a1a2a064a0626db322392806428f7dc59cb3e # v1.1.2\n\n  - name: Check Header Compliance\n    run: copywrite headers --plan\n```\n\n:bulb: Running the copywrite command with the `--plan` flag will return a non-zero exit code if the repo is out of compliance.\n\n## Pre-Commit Hooks\n\nCopywrite can be used as a [Pre-Commit](https://pre-commit.com) Hook for those\nlooking to add copyright headers during local development. A list of supported\nhooks can be found in [here](./.pre-commit-hooks.yaml), but the most common use\ncase for adding missing copyright headers can be done by adding the following\nsnippet to your repo's `.pre-commit-config.yaml`:\n\n```yaml\n  - repo: https://github.com/hashicorp/copywrite\n    rev: v0.15.0 # Use any release tag\n    hooks:\n      - id: copywrite-headers\n```\n\n## Debugging\n\nCopywrite supports several built-in features to aid with debugging. The first\nand most commonly used one is configurable log levels. Copywrite checks the\n`COPYWRITE_LOG_LEVEL` environment variable to determine which verbosity to use.\nThe following log levels are supported:\n\n- `trace`\n- `debug`\n- `info`\n- `warn`\n- `error`\n- `off` (disables logging)\n\nCopywrite also checks for if the `RUNNER_DEBUG=1` environment variable is set,\nwhich will cause it to default to debug-level logging. This environment variable\nis set by Github Actions when in debug mode, and can be a useful default.\nThe `COPYWRITE_LOG_LEVEL` setting takes precedence, however.\n\nIt is often useful to introspect information about the state Copywrite finds\nitself in. The `copywrite debug` command can print the running configuration,\nwhether or not a config file was loaded, what GitHub auth type is in use, and\nmore. No sensitive information is printed, however.  \n\n## Development\n\nTo maintain a consistent developer experience, this repo comes bundled with VS Code settings. When opening the repo for the first time, you will be asked if you want to install [suggested extensions](./.vscode/extensions.json) and your workspace will be pre-configured with consistent format-on-save [settings](./.vscode/settings.json).\n\nBefore committing code, this repo has been setup to check Go files using [pre-commit git hooks](https://pre-commit.com/). To leverage pre-commit, developers must install pre-commit and associated tools locally:\n\n```bash\nbrew install pre-commit golangci-lint go-critic\n```\n\nVerify install went successfully with:\n\n```bash\npre-commit --version\n```\n\nOnce you verify `pre-commit` is installed locally, you can use pre-commit git hooks by installing them in this repo:\n\n```bash\npre-commit install\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhashicorp%2Fcopywrite","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhashicorp%2Fcopywrite","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhashicorp%2Fcopywrite/lists"}