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