Quality gates for Python repos that developers can run locally, in CI, or hand to an AI agent without inventing a new workflow each time.
shipgate gives you one project config, a bundled catalog of checks, quiet success, and structured failure reports. It is inspired by Trunk and pre-commit, but keeps the surface area small:
shipgate install
shipgate format
shipgate checkpip install shipgate
# or
uv add --dev shipgateRequires Python 3.11–3.14.
Create shipgate.yaml:
suite: standard
env: managed
target: .
error-format: compact
configs:
mode: autoThen run:
shipgate install # install tools needed by suite
shipgate format # apply formatter/autofix checks
shipgate check # report-only quality checksshipgate always respects .gitignore; bundled defaults also skip .venv/, .shipgate/, and reports/ even when your suite YAML does not list them.
The suite: value is the project default. You do not need to repeat --suite standard; the CLI reads it from shipgate.yaml.
Success is silent and exits 0. Failures exit 1, write a JSON report under reports/failures/, and print the same report through your configured error-format.
- Suite: a named checklist, such as
python-quality,standard, orall. - Check: one rule runner inside a suite, such as
ruff.lintorty.check. check: report-only. It should not rewrite your files.format: applies write/fix checks, such as formatters.install: installs the tools needed by the selected suite.
Most teams pick a suite once in shipgate.yaml and run the same three commands everywhere.
suite: python-quality # default checklist for install/check/format
env: managed # managed tools under .shipgate/tools
target: . # scan root; defaults to .
error-format: compact # json | log | text | compact | github
configs:
mode: auto # repo configs first, bundled fallbackUse --suite only when you want a one-off override:
shipgate check --suite extended
shipgate install --suite standardCopy shipgate.yaml.example for an annotated config with ignores, custom checks, gates, and custom error formatters.
Suites are bundled starting points. They choose which checks run; tools still discover their own files and .gitignore is always respected.
| Suite | Use it for |
|---|---|
all |
Run every bundled check |
all-lint |
Full lint/analysis + tests (no format check or apply) |
python-quality |
Core Python lint, format-check, and type checks |
formatting |
Report-only formatting drift |
format |
Formatter/autofix checks that write files |
standard |
Broader portable quality baseline |
extended |
Slower optional checks on top of standard |
policy |
Bundled script gates |
demo |
Internal/sample fixture suite |
Examples:
# Local Python repo
suite: python-quality
error-format: text# CI baseline with GitHub annotations
suite: standard
error-format: github# Max coverage while developing shipgate itself
suite: all
error-format: compactOn failure, shipgate always writes the canonical JSON report to disk:
reports/failures/ruff.lint-20260716T163000Z/report.json
error-format only controls what is printed to stderr.
| Format | Example output |
|---|---|
json |
Pretty JSON report, including report_path |
log |
2026-07-16T12:00:00+00:00 [error] ruff.lint/E501 src/app.py:42: Line too long |
text |
- [error] E501: Line too long (src/app.py:42) |
compact |
src/app.py:42: error: E501 Line too long |
github |
::error file=src/app.py,title=ruff.lint/E501,line=42::Line too long |
Default is json when error-format is omitted.
Custom formatters live in shipgate.yaml:
suite: all
error-format: short
error-formatters:
short:
kind: finding_line
template: "{severity}\t{rule_id}\t{file}:{line}\t{message}"
jq-summary:
kind: jq
program: >
.findings[] | "[\(.severity)] \(.rule_id) \(.message)"finding_line placeholders: severity, rule_id, message, file, line, check_id, report_path.
jq formatters require jq on PATH.
shipgate install
shipgate format
shipgate checkInspect what is available:
shipgate list suites
shipgate list tools
shipgate list checksRun a single check:
shipgate check --check ruff.lint --target .Export the failure report schema:
shipgate schema > failure-report.schema.jsonBrowse suite runs and findings in a local web UI:
pip install 'shipgate[server]'
shipgate server
shipgate server --port 8765 --openUse gates when a repo needs a policy that is not covered by the bundled catalog.
shipgate gate init module-size --description "Cap module line counts"That creates a shell gate under .shipgate/gates/ and a catalog entry under .shipgate/catalog/checks/. Enable it from shipgate.yaml:
checks:
- id: gate.module-sizeMinimal GitHub Actions job:
name: quality
on:
pull_request:
push:
branches: [main]
jobs:
shipgate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v6
- run: uvx shipgate install
- run: uvx shipgate checkFor GitHub PR annotations, set:
error-format: githubSee CONTRIBUTING.md for local development, adding tools/checks, and release notes.
| Tool | Purpose |
|---|---|
| Bandit | Security issue scanner for Python |
| codespell | Common misspellings in text and code |
| deadcode | Unused Python code via static analysis |
| Gitleaks | Secret scanning for git repositories |
| Hadolint | Dockerfile linter |
| JSCPD | Copy/paste / duplication detector |
| markdownlint | Markdown style linter |
| mdformat | Markdown formatter |
| mutmut | Mutation testing for Python |
| pydeps | Python dependency graphs and cycle detection |
| pytest | Test runner (optional coverage via pytest-cov) |
| Radon | Cyclomatic complexity and maintainability metrics |
| Ruff | Fast Python linter and formatter |
| Script gates | Project-local bash policy checks (shipgate gate init) |
| Semgrep | Pattern-based security and quality analysis |
| ShellCheck | Static analysis for shell scripts |
| shfmt | Shell script formatter |
| Sourcery | Automated Python review / refactor suggestions |
| ty | Astral static type checker for Python |
| Vulture | Dead Python code with high confidence |
| yamlfmt | YAML formatter |
| yamllint | YAML syntax and style linter |
List the live catalog anytime with shipgate list tools.
MIT