Skip to content

Back to the catalog

Butler
github.comrepo

Butler

Butler is a Python CLI/SQLite-backed tool that crawls all repositories in a GitHub organization to inventory workflows, actions, and reusable workflows, flagging unpinned/unpinnable third-party actions, references to archived or deleted repos, invalid runners, and organization/repo secrets & variables usage (including 'secrets: inherit'). It downloads workflow data via a GitHub PAT or GitHub App, processes it into a local database, and generates customizable HTML/CSV reports with SQL-based custom queries, aimed at large-scale GitHub Actions supply-chain and CI/CD hygiene auditing.

TL;DR Open-source auditor that scans an entire GitHub org's Actions workflows for unpinned actions, secret exposure, and stale reusable-workflow references.
Document

What it is

Butler's actual operation: a four-stage CLI pipeline (download → secrets_and_vars → process → report) that populates and then queries a SQLite database. The summary tells you what it finds; this tells you how you run it and what access it needs.

How it works

Each stage is a separate butler.py subcommand against the same --database file:

  1. download — collects workflows/actions. Target is --repo as org, org/name, or org/name@branch, or a file of targets. Flags control scope: --all-repos, --include-forks, --include-archived, --all-branches, --all-tags (branch/tag flags only work with a single --repo).
  2. secrets_and_vars — optional, separate command requiring extra permissions; pulls org/repo secrets and variables. The page recommends a GitHub App installed in the org for this.
  3. process — parses the downloaded data.
  4. report — emits CSV/HTML grouped into actions, hygiene, runners, secrets, workflows.

Preconditions

Needs a GitHub PAT (read from GITHUB_TOKEN by default). It supports rate-limit scaling via multiple tokens (GITHUB_TOKEN_1..N, selected with --token "GITHUB_TOKEN_*") or a GitHub App (--gh-app-key, --gh-app-installation-id, --gh-app-client-id). Secrets/variables inventory requires elevated permissions beyond a basic read token.

What to do

Setup and a full-org run:

python3 -m venv venv
. venv/bin/activate
pip3 install -r requirements.txt
export GITHUB_TOKEN=ghp_...
python butler.py download --repo "microsoft" --all-repos --threads 10 --very-verbose --database microsoft.db
python butler.py process --database ./microsoft.db --threads 10 --very-verbose
python butler.py report --database ./microsoft.db --output ./report --repo "github"

Secrets/variables (GitHub App recommended):

python butler.py secrets_and_vars --org "microsoft" --database ./data/microsoft.db --very-verbose --gh-app-key ...

Reports are SQL-driven and extensible. Custom queries are v2.0-only YAML files (--custom-query-path), each with name, description, filename, a group (from the five values above), and a sql block. Queries support placeholders :org, $_TRUSTED_ORGS_$, and $_UNSUPPORTED_RUNNERS_$ — so you can define trusted orgs and your own supported-runner allowlist rather than accept the defaults. Column display is configured per result column (hide, link/type). Override the whole report config with --config.

Caveats

secrets_and_vars is opt-in and permission-gated, so a default run inventories workflows/actions only. The linked sample reports (GitHub, OpenAI, Docker, AWS Labs) are noted as not mobile friendly. Only custom-query version 2.0 is supported.

Technologies named here

Techniques named here

  • Use of unsupported CI/CD runners
  • Use of 'secrets