Skip to content
undontPublic

About

extract CI metrics and surface test failures from GitHub Actions logs

Topics

Resources

Stars

1 star

Watchers

1 watching

Forks

Latest commit

 

History

29 Commits

Folders and files

Repository files navigation


bench

Extract CI metrics and surface test failures from GitHub Actions logs.

CI Release gh extension Go macOS Linux

Quick Start · Commands · Framework Support · Configuration · Examples


A gh CLI extension. gh bench stats pulls numeric values from CI logs and aggregates them (median, mean, p95). gh bench failures extracts structured test failures using framework-aware parsers.

Quick Start

# Install
gh extension install undont/gh-bench

# Aggregate a metric across recent runs
gh bench stats --workflow ci.yml --pattern 'duration=(?P<ms>[0-9.]+)ms'

# Surface test failures from a specific run
gh bench failures --runs 12345678

Commands

gh bench stats

Extract a numeric value from logs across multiple workflow runs and compute aggregations.

gh bench stats --workflow ci.yml --pattern 'median=(?P<ms>[0-9.]+)ms'
gh bench stats --workflow ci.yml --pattern 'duration=(?P<s>[0-9.]+)s' --agg median,p95,min,max
gh bench stats --runs 111,222,333 --pattern 'score=(?P<val>[0-9.]+)'
Flag Short Default Description
--workflow -w Workflow filename or name
--runs -r Comma-separated list of run IDs
--pattern -P Regex with a named capture group (?P<name>...). Optionally include (?P<label>...) for row context
--preset -p Use a built-in pattern preset (see --list-presets)
--list-presets -L List available pattern presets and exit
--limit -l 10 Max number of runs to fetch
--branch -b Filter runs by branch
--agg -a median Aggregations: median, mean, p95, min, max (comma-separated)
--match -m first Which matches to extract per run: first, all
--step -s Filter logs to a specific step name (substring match)
--concurrency -c 5 Concurrent log fetchers
--format -f table Output format: table, json, csv

Either --pattern or --preset is required. Either --workflow or --runs is required.

gh bench failures

Fetch failed CI runs, identify failing steps, and extract structured errors using framework-aware parsers. Each failure is annotated with the timestamp from the raw CI log, so you can see exactly when it occurred.

By default, only runs from branches with open pull requests are shown. Use --all to include all failed runs, or --branch to target a specific branch.

When a run has been re-run (multiple attempts), only the latest attempt's failures are shown. Steps from re-runs are annotated with the attempt number, e.g. Step: Run tests (attempt 2).

gh bench failures --workflow ci.yml
gh bench failures -w ci.yml --all
gh bench failures -r 12345678
gh bench failures -w ci.yml -b main -l 10
gh bench failures --runs 12345678 --format json
gh bench failures -w ci.yml --group
Flag Short Default Description
--workflow -w Workflow filename or name
--runs -r Comma-separated list of run IDs
--limit -l 5 Max failed runs to fetch
--branch -b Filter by branch (disables open-PR filtering)
--all -a false Include all failed runs, not just those with open PRs
--group -g false Group identical failures across runs
--exclude-step -x Exclude steps matching name (case-insensitive substring, repeatable)
--concurrency -c 5 Concurrent log fetchers
--format -f table Output format: table, json, csv

Either --workflow or --runs is required.


Framework Support

The failures command auto-detects the test framework from log output and extracts structured failure information.

Framework Detection Extracted Fields
.NET (xUnit, NUnit, MSTest) Failed TestName [duration], error CS*:, summary lines Test name, duration, exception, assertion, stack trace location
Go --- FAIL: TestName (duration), FAIL\tpackage Test name, duration, error message, file:line
Vitest / Jest ✗ Suite > Test, FAIL *.test.tsx, or tsc diagnostics when running --typecheck Test name, error type, expected/actual, file:line:col
Fallback No framework detected Last 30 non-boilerplate lines, ##[error] messages

The fallback parser strips GitHub Actions boilerplate (env vars, annotations, cleanup lines, shell script source) and prioritises ##[error] messages.


Configuration

Create a .gh-bench.yml file in your repository root to set project-level defaults. CLI flags override config values when explicitly set.

# .gh-bench.yml
workflow: ci.yml

failures:
  exclude-steps:
    - "Check CI status"
    - "Summary"
Key Applies to Description
workflow stats, failures Default workflow (replaces --workflow flag)
failures.exclude-steps failures Steps to exclude by name (replaces --exclude-step flag)

The config file is discovered by walking up from the current directory to the nearest .git root.


Examples

Tracking build duration across runs

$ gh bench stats --workflow ci.yml --pattern 'Total time:\s*(?P<seconds>[0-9.]+)s' --agg median,p95

RUN ID          TITLE                           VALUE
23364348137     ACME-1338 Replace law firm ...   84.5
23348219428     ACME-1335 Add Roslyn analys...   91.0
23341291675     ACME-1332 Rename hooks ...       103.2
────────────────────────────────────────────────────────────────────
median: 91.0  p95: 103.2

Per-package Go test durations

Using --match all with the go-test preset extracts every package duration and labels each row with the package name:

$ gh bench stats --preset go-test --workflow ci.yml --match all --limit 1

RUN ID          LABEL                                         VALUE
23402873321     audit                                         1.057
23402873321     cli                                           1.073
23402873321     lockfile                                      1.027
23402873321     scanner                                       166.842
23402873321     server                                        1.031
────────────────────────────────────────────────────────────
median: 1.1

Labels come from the (?P<label>...) capture group in the pattern. When labels share a common path prefix, the table automatically strips it for readability. Full paths are preserved in --format json and --format csv output.

Extracting .NET test failures

$ gh bench failures --runs 23341983210

● RUN 23341983210 — ACME-1335 Add Roslyn analyser (2026-03-20T12:03:31Z)
  Step: Run integration-platform tests
  Framework: dotnet

  Failed Tests (1)

  ✗ Acme.IntegrationTests.TokenManagement.ExchangeCodeForTokens_ReturnsBadRequest [1 s] @ 20/03/26 12:03:45
      Shouldly.ShouldAssertException : errors[0].Reason
      should start with "Microsoft 365 authentication fai"
      but was "An unexpected error occurred..."
      at ExchangeCodeForTokensIntegrationTests.cs:line 181

JSON output for scripting

$ gh bench failures --runs 23341983210 --json | jq '.[].failures[].test_name'
"Acme.IntegrationTests.TokenManagement.ExchangeCodeForTokens_ReturnsBadRequest"

Building from Source

git clone https://github.com/undont/gh-bench
cd gh-bench
make build      # Build the binary
make install    # Build and install as gh extension
make test       # Run tests with race detection + coverage
make check      # Run all checks (vet, lint, test)
All Makefile targets
Target Description
build Build the binary
install Build and install as gh extension
uninstall Remove the gh extension
clean Remove build artefacts
test Run tests with race detection and coverage
cover Show coverage report in browser
lint Run golangci-lint
fmt Format all Go files
vet Run go vet
check Run all checks (vet, lint, test)

Use FILTER="TestName" to run specific tests: make test FILTER=TestDotnet

About

extract CI metrics and surface test failures from GitHub Actions logs

Topics

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Contributors

Languages