From 5c0f2c6dc1a2bd1408bf7fb254ee479b1866b83d Mon Sep 17 00:00:00 2001 From: Blair Hamilton Date: Mon, 7 Sep 2026 21:03:49 -0400 Subject: [PATCH] docs: the files a stranger looks for before adopting this Issue #43 asks what a port owes a reader who already uses the thing it reimplements. Attribution, the name collision and the shared cache all landed. The community health files did not, and their absence is the part a prospective adopter notices first: no CONTRIBUTING, no SECURITY, no issue templates, so every report arrives shaped however the reporter guessed. For this project that shape matters more than usual. The single most valuable thing anyone can send is a divergence report -- upstream does X, this does Y, both commands, both outputs -- because it is the one class of bug the project cannot find for itself. So the divergence template is the primary one and asks for exactly that pair, and both CONTRIBUTING and the issue chooser say plainly that a feature upstream lacks belongs on upstream's tracker, not here. SECURITY draws the line where it actually falls for a hook runner: the tool downloads and executes code by design, so the boundary is whose code and whether it can be made to run something unasked -- checksum verification in the action, path handling in the store, config parsing. A hostile entry in your own config is not a vulnerability in the runner. Also fixes a dead link. docs/parity.md offered test/integration/parity_report.json as the evidence behind the parity number, but that path is gitignored -- the file is generated, and CI uploads it as an artifact. A doc whose whole argument is "this is measured, not remembered" cannot point at a 404, so it now points at the artifact that exists. Refs #43 --- .github/ISSUE_TEMPLATE/bug.yml | 75 +++++++++++++++ .github/ISSUE_TEMPLATE/config.yml | 14 +++ .github/ISSUE_TEMPLATE/divergence.yml | 83 ++++++++++++++++ .github/ISSUE_TEMPLATE/language.yml | 64 +++++++++++++ .github/PULL_REQUEST_TEMPLATE.md | 27 ++++++ CODE_OF_CONDUCT.md | 130 ++++++++++++++++++++++++++ CONTRIBUTING.md | 109 +++++++++++++++++++++ README.md | 13 +++ SECURITY.md | 63 +++++++++++++ docs/parity.md | 2 +- 10 files changed, 579 insertions(+), 1 deletion(-) create mode 100644 .github/ISSUE_TEMPLATE/bug.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/divergence.yml create mode 100644 .github/ISSUE_TEMPLATE/language.yml create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 CODE_OF_CONDUCT.md create mode 100644 CONTRIBUTING.md create mode 100644 SECURITY.md diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml new file mode 100644 index 0000000..bcc1562 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -0,0 +1,75 @@ +name: Bug report +description: Something is broken in a way that is not a difference from upstream (a crash, a wrong exit code, a packaging problem). +title: "bug: " +labels: ["bug"] +body: + - type: markdown + attributes: + value: | + If the problem is that this tool behaves *differently from Python + pre-commit*, please use the **Divergence** template instead — it asks + for the comparison that makes the report actionable. + + - type: textarea + id: what + attributes: + label: What happened + description: Include the full output, and the command that produced it. + render: console + validations: + required: true + + - type: textarea + id: expected + attributes: + label: What you expected instead + validations: + required: true + + - type: textarea + id: repro + attributes: + label: Steps to reproduce + description: A scratch repo and the exact commands beats a description. If you cannot reproduce it reliably, say so — that is useful information rather than a reason not to file. + placeholder: | + 1. git init scratch && cd scratch + 2. cat > .pre-commit-config.yaml <<'YAML' + ... + YAML + 3. pre-commit run --all-files + validations: + required: true + + - type: input + id: version + attributes: + label: Version + description: Output of `pre-commit --version`. + validations: + required: true + + - type: dropdown + id: os + attributes: + label: Platform + options: + - Linux + - macOS (Apple Silicon) + - macOS (Intel) + - Windows + - Other + validations: + required: true + + - type: dropdown + id: install + attributes: + label: How you installed it + options: + - Homebrew (blairham/tap) + - Release archive + - go install + - Built from source + - GitHub Action (blairham/go-pre-commit@v4) + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..2f05c5b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,14 @@ +blank_issues_enabled: true +contact_links: + - name: Feature request for pre-commit itself + url: https://github.com/pre-commit/pre-commit/issues + about: This project deliberately has no features upstream lacks. Divergence is a bug here, so a new capability has to land upstream first — asking there helps everyone, and it arrives here as parity work. + - name: Question or idea + url: https://github.com/blairham/go-pre-commit/discussions + about: Not sure it is a bug, or want to talk something through first? Discussions is the better room. + - name: Should I use this at all? + url: https://github.com/blairham/go-pre-commit/blob/main/docs/comparison.md + about: An honest comparison that leads with the case for staying on Python pre-commit. + - name: Report a security vulnerability + url: https://github.com/blairham/go-pre-commit/security/advisories/new + about: Please report privately rather than in a public issue. diff --git a/.github/ISSUE_TEMPLATE/divergence.yml b/.github/ISSUE_TEMPLATE/divergence.yml new file mode 100644 index 0000000..82c22e0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/divergence.yml @@ -0,0 +1,83 @@ +name: Divergence from Python pre-commit +description: This tool behaves differently from upstream pre-commit. The most useful report you can file. +title: "divergence: " +labels: ["divergence"] +body: + - type: markdown + attributes: + value: | + This project has one job: behave like Python pre-commit. A difference + between the two is a bug here, even when the difference looks like an + improvement. + + The most useful report is **two commands and their two outputs.** + + - type: textarea + id: expected + attributes: + label: What upstream pre-commit does + description: The command you ran against Python pre-commit, and its output. + render: console + placeholder: | + $ python -m pre_commit run trailing-whitespace --all-files + Trim Trailing Whitespace.................................................Passed + validations: + required: true + + - type: textarea + id: actual + attributes: + label: What this tool does + description: The same command against this tool, and its output. + render: console + placeholder: | + $ pre-commit run trailing-whitespace --all-files + Trim Trailing Whitespace.................................................Failed + validations: + required: true + + - type: textarea + id: config + attributes: + label: .pre-commit-config.yaml + description: The config, if the behavior depends on it. Trim it to the smallest version that still shows the difference. + render: yaml + validations: + required: false + + - type: input + id: version-ours + attributes: + label: This tool's version + description: Output of `pre-commit --version` — it prints a `(build …)` suffix, which is how you know you ran this one. + placeholder: "pre-commit 4.6.6 (build ...)" + validations: + required: true + + - type: input + id: version-upstream + attributes: + label: Upstream version compared against + placeholder: "4.6.2" + validations: + required: true + + - type: dropdown + id: os + attributes: + label: Platform + options: + - Linux + - macOS (Apple Silicon) + - macOS (Intel) + - Windows + - Other + validations: + required: true + + - type: markdown + attributes: + value: | + **Note on platforms:** Linux and macOS are exercised in CI. Windows + builds and is smoke-tested, but is far less trodden — a Windows-only + report is still welcome and useful. diff --git a/.github/ISSUE_TEMPLATE/language.yml b/.github/ISSUE_TEMPLATE/language.yml new file mode 100644 index 0000000..1cf4ff5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/language.yml @@ -0,0 +1,64 @@ +name: Language backend report +description: A language backend is broken, or you want to help move one from "untested" to "proven". +title: "language: " +labels: ["language-backend"] +body: + - type: markdown + attributes: + value: | + Ten of the 22 language backends are implemented but effectively + unexercised — see the + [grading table](https://github.com/blairham/go-pre-commit/blob/main/docs/parity.md#language-support-graded). + + If you are the first person to try one of those, you are doing the + project a favor either way: it working is worth knowing, and it not + working is worth knowing sooner. + + - type: dropdown + id: language + attributes: + label: Which language backend + options: + - conda + - coursier + - dart + - docker + - docker_image + - dotnet + - fail + - golang + - haskell + - julia + - lua + - node + - perl + - pygrep + - python + - r + - ruby + - rust + - script + - swift + - system + - Other / not sure + validations: + required: true + + - type: dropdown + id: kind + attributes: + label: What kind of report is this + options: + - It is broken + - It works (confirming an untested backend) + - I want to add test coverage for it + validations: + required: true + + - type: textarea + id: detail + attributes: + label: Details + description: The hook config you used, and what happened. If it works, saying so plainly is enough. + validations: + required: true diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..d532d36 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,27 @@ +## Why + + + +## What changed + + + +## Parity + + + +## Checklist + +- [ ] `make check` passes (`fmt` + `vet` + `test` — note this does **not** run lint) +- [ ] `make lint` passes +- [ ] Tests do not touch real user state (`t.TempDir()` + `t.Setenv` for `HOME`, `PRE_COMMIT_HOME`, `XDG_CACHE_HOME`) +- [ ] If behavior changed, the differential parity suite still passes +- [ ] Docs updated if this changes something a user can observe + +Closes # diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..c5f8387 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,130 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual +identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the overall + community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or advances of + any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email address, + without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official email address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +**blairham@me.com**. + +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of +actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or permanent +ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the +community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.1, available at +https://www.contributor-covenant.org/version/2/1/code_of_conduct.html. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. + +[homepage]: https://www.contributor-covenant.org +[Mozilla CoC]: https://github.com/mozilla/diversity diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..90cb494 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,109 @@ +# Contributing + +Thanks for looking. This is a personal project with a single maintainer, so the +most useful thing you can do is usually smaller than you think. + +## The most valuable contribution is a divergence report + +This project has one job: behave like Python pre-commit. So the highest-value +bug report is not "this crashed" — it is **"upstream does X, this does Y"**. + +That report is worth more than a patch, because it is the thing this project +cannot generate for itself. A good one is two commands and two outputs: + +```console +$ pre-commit run trailing-whitespace --all-files # this tool +... + +$ python -m pre_commit run trailing-whitespace --all-files # upstream 4.6.2 +... +``` + +Include the versions of both, and the `.pre-commit-config.yaml` if the behavior +depends on it. See [docs/parity.md](docs/parity.md#reporting-a-divergence). + +**Divergence is a bug, never a feature.** If you want behavior Python +pre-commit does not have, this is the wrong tracker — asking +[upstream](https://github.com/pre-commit/pre-commit) helps everyone, and if +they ship it, it arrives here as parity work. A PR that adds a flag upstream +does not have will be declined, however good the flag is. + +## Before you open a pull request + +Open an issue first for anything that is not a small, obvious fix. This is a +single-maintainer project and an unsolicited large PR is likely to sit, or to +be declined for a reason that a five-line issue would have surfaced first. + +## Development + +Go is pinned in `go.mod` and `.tool-versions` (both must match exactly), and +runtime versions are managed with [asdf](https://asdf-vm.com). + +```bash +git clone https://github.com/blairham/go-pre-commit +cd go-pre-commit +asdf install # optional, if you use asdf +pre-commit install # or: go run . install + +make build # build/pre-commit +make test # go test -race ./... +make check # fmt + vet + test +make lint # go tool golangci-lint run ./... +``` + +**`make check` does not run `lint`.** It is `fmt + vet + test`. Run `make lint` +as well before pushing, or let CI tell you. + +Formatting and linting are pinned as Go tools in `go.mod` — `go tool gofumpt` +and `go tool golangci-lint`, not separately installed binaries — so the version +you run is the version CI runs. + +### The parity harness + +The claim on the front page is generated, not remembered. `docs/parity.md` is +written from a report produced by the differential suite, which runs this tool +and Python pre-commit against the same inputs and diffs them: + +```bash +go test ./test/integration/... -run Parity -v +``` + +It pins the upstream version it measures against and refuses any other, so the +number cannot quietly drift. If you change behavior, this suite is the thing +that decides whether you were right. + +### Tests must not touch your real state + +Hooks and caches are the whole subject matter here, which makes it very easy to +write a test that quietly eats a developer's `~/.cache/pre-commit` or their git +config. Redirect with `t.TempDir()` and `t.Setenv` — including `PRE_COMMIT_HOME`, +`XDG_CACHE_HOME` and `HOME` — and never assume the temp dir is a safe default. + +## Commits and pull requests + +- Work on a branch; do not commit to `main`. +- Commit messages explain **why**, not a restatement of the diff. +- Never bypass hooks with `--no-verify`. +- Put `Closes #N` in the PR body for the issues it resolves. +- No AI-attribution trailers in commit messages or PR bodies. + +## Adding a language backend + +Ten of the 22 language backends are implemented but effectively unexercised — +see the [grading table](docs/parity.md#language-support-graded). Moving one of +those rows from "untested" to "proven" is genuinely wanted work, and it is +mostly test-writing rather than implementation. Start by adding it to the +differential suite and seeing what breaks. + +## Scope + +- **In scope:** parity with Python pre-commit, the platforms we publish + binaries for, performance, and evidence that any of the above is true. +- **Out of scope:** features upstream does not have, changes to the config + format, and anything that would make a `.pre-commit-config.yaml` written for + this tool fail on the Python one. + +## License + +By contributing you agree that your contributions are licensed under the +[Apache License 2.0](LICENSE), and that you have the right to submit them. diff --git a/README.md b/README.md index a053edc..db80e22 100644 --- a/README.md +++ b/README.md @@ -248,6 +248,8 @@ CI builds, signs, and notarizes cross-platform binaries, publishes a GitHub rele | [Should you use this?](docs/comparison.md) | The case for staying on Python pre-commit, and the narrow case against it | | [Parity](docs/parity.md) | What is measured, what is not, and which languages are actually proven | | [Stability](docs/stability.md) | What the version number means, and what is frozen | +| [Contributing](CONTRIBUTING.md) | How to report a divergence — the most useful thing you can send | +| [Security](SECURITY.md) | What is in scope, and how to report privately | ## Attribution @@ -269,6 +271,17 @@ Their MIT license is reproduced in [NOTICE](NOTICE). Bugs you find here are this project's bugs, not theirs — please report them [here](https://github.com/blairham/go-pre-commit/issues) rather than upstream. +## Contributing + +The most valuable contribution is a **divergence report**: upstream does X, +this does Y, with both commands and both outputs. That is the one thing this +project cannot generate for itself — see +[CONTRIBUTING.md](CONTRIBUTING.md). + +Divergence is a bug here, never a feature. If you want behavior Python +pre-commit does not have, [upstream](https://github.com/pre-commit/pre-commit) +is the place to ask; if they ship it, it arrives here as parity work. + ## License [Apache-2.0](LICENSE), with third-party notices in [NOTICE](NOTICE). diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..af867bd --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,63 @@ +# Security Policy + +## Supported versions + +The latest release, and nothing else. This is a single-maintainer personal +project with no SLA — see [docs/stability.md](docs/stability.md#support). + +| Version | Supported | +|---|---| +| Latest release | ✅ | +| Anything older | ❌ — upgrade first, then report if it persists | + +## Reporting a vulnerability + +**Do not open a public issue.** + +Use GitHub's [private vulnerability reporting](https://github.com/blairham/go-pre-commit/security/advisories/new), +which is enabled on this repository. If that is not available to you, email +**blairham@me.com** with `go-pre-commit` in the subject. + +Please include what you would want to receive: the version, the platform, the +smallest reproduction you have, and what an attacker gets out of it. + +Expect an acknowledgement within a week. This is a side project, so that is a +best effort and not a commitment. If a fix is warranted it ships in the next +release with an advisory; you will be credited unless you ask not to be. + +## What is in scope + +This tool downloads and executes code by design — that is what a hook framework +does — so the interesting boundary is *whose* code, and whether the tool can be +made to run something the user did not ask for. + +- **Supply-chain integrity of the installer.** The composite action verifies + the release archive against `checksums.txt` before extracting it. A way to + make it skip, or pass, that check is in scope. +- **Cache and store handling.** Path traversal out of `~/.cache/pre-commit` + when cloning a hook repository or naming an environment directory; a + malicious repo or manifest that writes outside the store. +- **Config parsing.** A `.pre-commit-config.yaml` or `.pre-commit-hooks.yaml` + that causes execution the config does not describe — command injection + through a hook's `entry`, `args`, or filename arguments. +- **Git hook installation.** `install` writing something other than what it + reports, or clobbering an existing hook without the documented backup. +- **Divergence from upstream that has a security consequence** — for example, + honoring something upstream deliberately rejects. + +## What is not in scope + +- **Hooks doing what they were configured to do.** If your + `.pre-commit-config.yaml` points at a repository, this tool clones it and + runs it. That is the design, it is upstream's design, and a hostile entry in + your own config is not a vulnerability in the runner. Review what you add. +- **Behavior inherited from upstream by intent.** `clean` removes the entire + store, including environments Python pre-commit built — that is documented + and matches upstream. Report it upstream if you think it is wrong there. +- **The shared cache directory.** Sharing `~/.cache/pre-commit` with the Python + tool is deliberate and documented in + [docs/parity.md](docs/parity.md#deliberate-behaviors-that-surprise-people). +- **Vulnerabilities in hooks you run, or in the repositories they come from.** + Those belong to their maintainers. +- **Anything requiring an attacker who already has write access** to your repo, + your config, or your machine. diff --git a/docs/parity.md b/docs/parity.md index e1e6204..479ca0c 100644 --- a/docs/parity.md +++ b/docs/parity.md @@ -17,7 +17,7 @@ files on disk. It runs on every pull request. | Checks | **78** | | Passing | **78** (100.0%) | | Measured against | Python pre-commit **4.6.2**, pinned in CI | -| Report | [`test/integration/parity_report.json`](../test/integration/parity_report.json), regenerated by the suite | +| Report | `test/integration/parity_report.json`, regenerated by the suite on every run and uploaded by CI as the [`parity-report`](https://github.com/blairham/go-pre-commit/actions/workflows/ci.yml) artifact | The report records the version it measured against, because a parity percentage without that version is not a claim anyone can check. If it reads