A picky and eager Git hook runner.
Project description
goose
💻🪿
A picky and eager Git hook runner
- 🔒 Reproducible builds
- ⚡ Dynamic parallelism
- 💨 Small file-system footprint
- 🏃 Fast Python installs with uv
Installation
Refer to the documentation for alternative installation instructions.
Via uvx
uvx install git-goose
Github Actions
name: CI
on:
push:
branches: ["main"]
pull_request:
jobs:
lint:
name: Run goose checks
uses: antonagestam/goose/.github/workflows/run.yaml@main
Features
- Smart parallelism schedules hooks across CPUs while avoiding concurrent writes.
- Deterministic environments by using ecosystem-specific lock files.
- Environments are shared across hooks.
- Self-contained definitions means there's no need to push tool-specific configuration upstream, or to maintain brittle mirroring schemes.
Parallelism
Goose takes care to keep your CPUs as busy as possible, optimizing to have the full suite of hooks finish as soon as possible. It does this by distributing units of work to all available processing cores.
Parameterized hooks, or hooks that take files as command line arguments, are divided to one unit of work per available core. Whenever a core becomes available for more work, a new unit is chosen for execution.
The scheduler takes care to never run more than one mutating hook on the same file. It
does this by taking into account hooks marked as read_only and by comparing sets of
files a unit of work is assigned to. Two incompatible hooks can be simultaneously
working on two separate parts of the code-base.
Deterministic environments
Goose uses lock files to facilitate deterministic results across developer environments
and CI. You specify dependencies in goose.yaml, and invoking goose run will produce
the appropriate lock files under a .goose/ directory. The .goose/ directory is meant
to be checked into git, so that future invocations of goose run can use the lock files
it contains to produce identical environments for hooks to run in.
---
title: Lock file workflow
---
flowchart LR
cfg["Config in goose.yaml"] -- goose upgrade --> lf
lf["Lock files under .goose/"] -- goose run --> env
env["Environments"]
- Invoking
goose upgradecreates lock files under the in-tree.goosedirectory. - Invoking
goose runcreates out-of-tree environments from the lock files. By default they live under~/.cache/goose. - Hooks are executed in the generated environments.
Usage
Create a goose.yaml file in the repository root.
environments:
- id: python
ecosystem:
language: python
version: "3.14"
dependencies:
- ruff
hooks:
- id: ruff
environment: python
command: ruff
args: [check, --force-exclude, --fix]
types: [python]
- id: ruff-format
environment: python
command: ruff
args: [format, --force-exclude]
types: [python]
Bootstrap environments, generate lock files, and install dependencies.
$ goose upgrade
Run all hooks over all files.
$ goose run --select=all
Commit configuration and lock files.
$ git add goose.yaml .goose
$ git commit -m 'Add goose configuration'
Configure goose to run as git hook. Supported hooks are pre-commit and pre-push.
$ goose git-hook pre-commit
$ goose git-hook pre-push
Upgrading hook versions
As pinning of hook versions is handled with lock files, there's no need to change configuration to upgrade hook dependency versions, instead you just run the upgrade command.
$ goose upgrade
$ git add .goose
$ git commit -m 'Bump goose dependencies'
Example node hook
Goose currently supports Python and Node environments, here's an example using Prettier to format Markdown files.
environments:
- id: node
ecosystem:
language: node
version: "21.7.1"
dependencies:
- prettier
hooks:
- id: prettier
environment: node
command: prettier
types: [markdown]
args:
- --write
- --ignore-unknown
- --parser=markdown
- --print-width=88
- --prose-wrap=always
Read-only hooks
You will likely want to use a mix of pure linters, as well as formatters and
auto-fixers. Tools that don't mutate files can be more heavily parallelized by Goose,
because they can inspect overlapping sets of files simultaneously as other tools. To
enable this you set read_only: true in hook configuration.
environments:
- id: python
ecosystem:
language: python
version: "3.14"
dependencies:
- pre-commit-hooks
hooks:
- id: check-case-conflict
environment: python
command: check-case-conflict
read_only: true
- id: check-merge-conflict
environment: python
command: check-merge-conflict
read_only: true
types: [text]
- id: python-debug-statements
environment: python
command: debug-statement-hook
read_only: true
types: [python]
- id: detect-private-key
environment: python
command: detect-private-key
read_only: true
types: [text]
- id: end-of-file-fixer
environment: python
command: end-of-file-fixer
types: [text]
- id: trailing-whitespace-fixer
environment: python
command: trailing-whitespace-fixer
types: [text]
Hooks that do not specify read_only: true will never run simultaneously as other tools
over the same file.
Non-parameterized hooks
Some tools don't support passing files, or just work better if given the responsibility to parallelize work itself. One such tool is mypy. You can instruct goose to not pass filenames to a hook (and as a consequence, also not spawn multiple parallel jobs for this hook).
environments:
- id: mypy
ecosystem:
language: python
version: "3.14"
dependencies:
- mypy
hooks:
- id: mypy
environment: mypy
command: mypy
read_only: true
parameterize: false
Environment variables
Hook invocations are called with the same environment variables as goose is invoked
with, other than PATH being overridden to point at the environment of the hook.
Static environment variables can be configured in hook definitions. These will overwrite
inherited values, but cannot overwrite PATH.
hooks:
- id: mypy
environment: type-check
command: mypy
env_vars:
FORCE_COLOR: "1"
read_only: true
parameterize: false
Terse and loose environment configs
Environments can be configured less verbosely. The id field can be omitted and will
then default to the name of the ecosystem language.
Version can also be omitted in the configuration and will then cause the highest
available version to be pinned when calling goose upgrade. Since the ecosystem version
is pinned in the manifest regardless of whether it is specified in the config or not,
this omitting the version from the config is not any less safe.
environments:
- ecosystem: python
dependencies:
- ruff
hooks:
- id: ruff
environment: python
command: ruff
args: [check, --force-exclude, --fix]
types: [python]
- id: ruff-format
environment: python
command: ruff
args: [format, --force-exclude]
types: [python]
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file git_goose-0.14.3.tar.gz.
File metadata
- Download URL: git_goose-0.14.3.tar.gz
- Upload date:
- Size: 89.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1910fb1d8ce472ccec00ef875afcaf6c06a84c6bf0824714f86f6dc439ac5c44
|
|
| MD5 |
9de1f7a8915a1709db55bd0e35026f86
|
|
| BLAKE2b-256 |
c125fb7e77581fa45d31534b5b821e87cea7754870f135646b15b5e1eb983732
|
Provenance
The following attestation bundles were made for git_goose-0.14.3.tar.gz:
Publisher:
release.yaml on antonagestam/goose
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
git_goose-0.14.3.tar.gz -
Subject digest:
1910fb1d8ce472ccec00ef875afcaf6c06a84c6bf0824714f86f6dc439ac5c44 - Sigstore transparency entry: 685735933
- Sigstore integration time:
-
Permalink:
antonagestam/goose@81c569aa352fcbaae4596aef29e0d23bd7b6fcde -
Branch / Tag:
refs/tags/0.14.3 - Owner: https://github.com/antonagestam
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yaml@81c569aa352fcbaae4596aef29e0d23bd7b6fcde -
Trigger Event:
release
-
Statement type:
File details
Details for the file git_goose-0.14.3-py3-none-any.whl.
File metadata
- Download URL: git_goose-0.14.3-py3-none-any.whl
- Upload date:
- Size: 33.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
210040516b05dc483bef82cd38cd60d031fca3bf0044a7d06d8db4c82b060f01
|
|
| MD5 |
a47e5d9ae7d60e42c81d9140832f6136
|
|
| BLAKE2b-256 |
c9684ec6c1971e386efbc911a0fd4a538c8a4f8f7493d2934974142f0703b706
|
Provenance
The following attestation bundles were made for git_goose-0.14.3-py3-none-any.whl:
Publisher:
release.yaml on antonagestam/goose
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
git_goose-0.14.3-py3-none-any.whl -
Subject digest:
210040516b05dc483bef82cd38cd60d031fca3bf0044a7d06d8db4c82b060f01 - Sigstore transparency entry: 685735934
- Sigstore integration time:
-
Permalink:
antonagestam/goose@81c569aa352fcbaae4596aef29e0d23bd7b6fcde -
Branch / Tag:
refs/tags/0.14.3 - Owner: https://github.com/antonagestam
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yaml@81c569aa352fcbaae4596aef29e0d23bd7b6fcde -
Trigger Event:
release
-
Statement type: