Skip to main content

greenwash

Find required CI checks that can go green without doing the work.

PyPI Python License

A green required check is treated as proof the tests ran. It is not. From GitHub's own documentation:

Required status checks must have a successful, skipped, or neutral status before collaborators can make changes to a protected branch.

Skipped counts as passed. So a job gated behind an if: that evaluated false, a job skipped because the job it needed failed, a workflow filtered out by paths: and backstopped by a same-named always-green twin, or a step wrapped in continue-on-error: true all produce the same green tick as a full passing run. Nothing in the UI tells them apart. The merge succeeds, main breaks, and the pull request still shows all-green months later.

greenwash reads your branch rules and your workflow definitions and tells you which of your gates are real.

Installation

pip install greenwash

Requires Python 3.10+ and a GitHub token, from GITHUB_TOKEN, GH_TOKEN, or gh auth login.

Quick start

greenwash audit                       # the repository you are standing in
greenwash audit --repo owner/name
$ greenwash audit --repo home-assistant/core
home-assistant/core (dev)  8 required checks, 13 workflows, via ruleset

2 hollow, 5 blocked, 2 advisory

[hollow] Check all requirements  (skips-with-upstream)
  where   .github/workflows/ci.yaml:gen-requirements-all
  detail  needs: info, base
  why     if an upstream job fails or is skipped this job is skipped too,
          which satisfies the gate rather than failing it -- the failure
          merges

Three severities:

  • hollow — the check can report success without the work running.
  • blocked — the check can never report at all, so the gate is stale or the branch is unmergeable.
  • advisory — the signal is weakened but not void, or the finding depends on a choice you may have made deliberately.

What it looks for

Finding Severity What it means
skippable-condition hollow The job's if: can be false, and a skip satisfies the gate
skips-with-upstream hollow An unguarded needs: — when the upstream fails, this job skips and the failure merges
aggregate-gate-without-check hollow A job running under always() that never inspects needs.*.result, so it passes when everything it gates on failed
continue-on-error-job hollow The job reports success even when its steps fail
duplicate-check-name hollow Two or more jobs that can fire on a pull request report under the same name
required-check-has-no-job blocked Nothing in any workflow reports under this name
aggregate-gate-allows-skips advisory alls-green with allowed-skips, so a green tick does not prove those jobs ran
continue-on-error-step advisory A step can fail without failing the check
escape-hatch-step advisory || true or set +e discards a command's failure inside the shell
path-filtered-workflow advisory A paths: filter means the workflow may not run for some pull requests

In CI

greenwash audit --fail-on hollow

Exit 1 when any hollow finding exists, so a repository can gate on its own gates being real. --fail-on any includes advisories. --json gives the same data machine-readable.

What it does not do

It cannot tell you whether a job that ran did any real work. A job whose pytest step collected zero tests looks identical to one that ran the suite. This is a static audit of workflow definitions against branch rules; proving the work happened needs run history and a declared witness, which is not built yet.

It cannot read what your token cannot read. Classic branch protection needs admin. When a branch reports protected: true and no rules come back, greenwash says so and stops rather than reporting a clean bill of health — a tool that called an invisible gate a missing one would be committing the error it exists to find.

It does not resolve reusable workflows. A uses: workflow reports as caller / job, which this version does not follow, so those may show up as required-check-has-no-job. Third-party apps that post check runs will too.

Merge queues change when the gate applies. A job gated on merge_group is skipped on the pull request and runs in the queue. greenwash reports the definition; whether that is a problem depends on where you enforce merging.

Why the name

Greenwashing is presenting something as better than it is on the strength of a symbol rather than the substance behind it. A green tick that certifies nothing is the same trick.

License

MIT

Metadata

Release files for greenwash 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for greenwash 0.1.0
File Size Uploaded
greenwash-0.1.0.tar.gz 55.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for greenwash 0.1.0
File Interpreter ABI Platform
greenwash-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 71.1 kB

Release files / greenwash-0.1.0.tar.gz

Download URL greenwash-0.1.0.tar.gz
Size 55.1 kB
Tags Source
SHA-256 checksum
How to use checksums
367900a8ceb6d3eb59835d758c80c16410d08463416f4e78adc2b501c0052716
BLAKE2b-256 checksum
How to use checksums
239b6d14af226b3e4ceb382e01fb42651497a1d1c6f1fa835eebabee8bbe9f77
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 28, 2026.

Transparency log

Release files / greenwash-0.1.0-py3-none-any.whl

Download URL greenwash-0.1.0-py3-none-any.whl
Size 15.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
3d41160d60728889ff39239f08a50938fde90bc18f0b0e25ef238cfaf53ff3e4
BLAKE2b-256 checksum
How to use checksums
2967db6ee294dcdbabb48724f031bf6aadb376b1c0687ba9f12e6d3b86cd8a6e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 28, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page