pipetree

Scans a directory for Woodpecker CI pipeline definitions and prints a tree of projects -> pipelines (with launch criteria) -> steps. Meant as a fast way to get an overview of a fleet of repos' CI architecture, and as a foundation for later automated analysis (inefficiency linting, an MCP wrapper) — the JSON output is the stable machine-readable interface for that.

What it finds

Per project (nearest ancestor directory containing .git, or the scan root if none):

  • .woodpecker.yml / .woodpecker.yaml
  • any *.yml / *.yaml directly under .woodpecker/

For each pipeline file it extracts: launch criteria (when, branches, pipeline-level depends_on), matrix axes, and every step in declaration order — image, commands, per-step when, depends_on, group, detach.

A pipeline or step with no when clause at all runs unconditionally in Woodpecker — easy to write by accident, so pipetree flags it explicitly as [always-run] (tree) / "always_run": true (JSON) rather than leaving it indistinguishable from "no when line to show".

By default, projects whose git origin remote isn't git.oleks.space/oleks/* are excluded from scans — this keeps vendored upstream mirrors' own CI test fixtures (e.g. the woodpecker repo's own test-dag/test-when/... corpus) out of fleet-wide results. Pass -all to include everything.

-analyze also flags two Woodpecker footguns that are statically visible in a step's commands: text, both of which have cost a real pipeline run:

  • a stray apostrophe inside a -c bash -euxc '...'-style single-quoted shell block — including one inside a # comment — closes the quote early and hands the rest of the script to the outer shell, usually surfacing as a baffling "command not found" for a tool that IS installed (oleks/mempalace pipeline 218).
  • an unescaped ${VAR} in a step command — Woodpecker substitutes braced ${VAR} in the raw pipeline text at parse time, before the shell runs, so a shell variable meant for the shell needs to be escaped as $${VAR} (Woodpecker's own ${CI_*} builtins and a pipeline's own matrix: axis names are the legitimate unescaped exceptions and aren't flagged).

Build

go build ./...

Usage

pipetree                      # scan the current directory
pipetree -C ~/projects        # scan a specific directory
pipetree -C ~/projects -json  # machine-readable output
pipetree -show-commands       # include each step's shell commands in the tree
pipetree -C ~/projects -filter 'building*'  # only project names matching a glob
pipetree -C ~/projects -all   # also include non-git.oleks.space remotes
pipetree -C ~/projects -summary  # fleet-wide rollup counts, not the full tree
pipetree -C ~/projects -analyze  # shared images, broken depends_on references
pipetree -C ~/projects -health   # annotate pipelines with last run status/age
pipetree -C ~/projects -health -summary  # rollup counts, incl. healthy/failing/other

-health needs WOODPECKER_SERVER/WOODPECKER_TOKEN in the environment (or -server/-token) — same convention as woodpecker-cli. It's opt-in: scans behave identically without it, no network access by default. A repo that isn't registered in Woodpecker at all is skipped with an informational message, not treated as an error.

Example:

2 project(s), 3 pipeline(s), 7 step(s)
├── forrest (/home/oleks/projects/forrest)
│   ├── .woodpecker.yml [pipeline: default]
│   │   ├── when: event=push,tag
│   │   ├── step: install (image: node:20)
│   │   ├── step: build (image: node:20) [when: event=push branch=main]
│   │   └── step: deploy (image: alpine) [depends_on: build]
│   └── .woodpecker/release.yml [pipeline: release]
│       ├── when: event=tag
│       └── step: publish (image: alpine)
└── gitea-mcp (/home/oleks/projects/gitea-mcp)
    └── .woodpecker.yml [pipeline: default]
        └── step: test (image: golang:1.22)

Status

v0.2/v0.3 shipped: scan + parse + tree/JSON/summary/analyze render, plus an opt-in live-health layer against the Woodpecker API. See SPEC.md for what's shipped, review feedback, and the roadmap (v0.4: pipetree lint cross-checked against woodpecker-cli lint, and an MCP wrapper).

woodpecker-cli (the official CLI) validates a single file against Woodpecker's real schema (woodpecker-cli lint <file>) and can query live pipeline status/history from the server — useful as a correctness oracle for this parser and as the source of the "is it actually healthy" data pipetree doesn't have on its own. See SPEC.md for how the two fit together.

S
Description
CLI that scans a directory for Woodpecker CI pipelines and prints a tree of projects -> pipelines (launch criteria) -> steps.
Readme
185 KiB
Languages
Go 100%