Skip to content

amrmahdi/bkslow

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

3 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

🐌 bkslow

A CLI tool that turns Buildkite build URLs into waterfall timeline charts, helping you spot what's slow in your CI pipeline.

Install

Go

go install github.com/amrmahdi/bkslow/cmd/bkslow@latest

Docker (GHCR)

docker pull ghcr.io/amrmahdi/bkslow

Docker (build locally)

git clone https://github.com/amrmahdi/bkslow.git
cd bkslow
make docker

From source

git clone https://github.com/amrmahdi/bkslow.git
cd bkslow
make install

Setup

bkslow needs a Buildkite API token with read_builds and read_job_logs permissions. It looks for a token in this order:

  1. --token flag
  2. BUILDKITE_API_TOKEN environment variable
  3. ~/.config/bk.yaml (Buildkite CLI config, created by bk configure)

If you already use the Buildkite CLI, bkslow picks up your token automatically -- no extra setup needed.

Usage

Pass a Buildkite build URL to get a waterfall chart of all jobs:

bkslow "https://buildkite.com/myorg/mypipeline/builds/123"

Pass a job-specific URL to get a breakdown of what happened inside that job:

bkslow "https://buildkite.com/myorg/mypipeline/builds/123#job-id"
bkslow "https://buildkite.com/myorg/mypipeline/builds/123/steps/canvas?sid=step-id"

Docker

# Pass token via environment variable
docker run --rm -t -e BUILDKITE_API_TOKEN \
  ghcr.io/amrmahdi/bkslow "https://buildkite.com/myorg/pipeline/builds/123"

# Mount your Buildkite CLI config so bkslow picks up the token automatically
docker run --rm -t -v ~/.config/bk.yaml:/home/nonroot/.config/bk.yaml:ro \
  ghcr.io/amrmahdi/bkslow "https://buildkite.com/myorg/pipeline/builds/123"

# Pass token explicitly
docker run --rm -t ghcr.io/amrmahdi/bkslow --token tok_xxx \
  "https://buildkite.com/myorg/pipeline/builds/123"

Flags

Flag Short Default Description
--token -t Buildkite API token (or set BUILDKITE_API_TOKEN)
--sort -s start Sort jobs by start, name, or duration
--jobs script Show script jobs only or all (includes waits, triggers)
--hide-idle false Hide zero-duration jobs (skipped, blocked, waiting)
--json -j false Output as JSON instead of chart
--raw -r false Dump full log (ANSI-stripped) instead of chart
--waterfall false Force waterfall mode even when job ID is present
--width -w 0 Terminal width override (0 = auto-detect)
--name-width 40 Max name column width
--threshold 0 Hide entries shorter than this (e.g. 5s, 1m)

Interactive TUI

Use --tui to launch an interactive terminal UI:

bkslow --tui "https://buildkite.com/myorg/pipeline/builds/123"

The TUI lets you browse the build waterfall, drill into jobs, and inspect log sections.

Navigation:

Key Action
j/k or ↑/↓ Move cursor up/down
PgUp/PgDn Page up/down
Home/End Jump to top/bottom
Enter or β†’ Drill into job/section
Esc or ← Go back
q Quit

Search/Filter (waterfall and job detail views):

Key Action
/ Start fuzzy search β€” list filters live as you type
↑/↓ Navigate filtered results while typing
Enter Confirm filter
Esc Cancel search and restore previous view
n/N Cycle through filtered results (wraps around)
Esc (with active filter) Clear filter

Search (log view):

Key Action
/ Search log content (highlights matches)
n/N Jump to next/previous match
Esc Clear search highlights

Examples

Show the build waterfall, hiding anything under 10 seconds:

bkslow --threshold 10s "https://buildkite.com/myorg/pipeline/builds/456"

Sort jobs by duration to find the slowest ones:

bkslow -s duration "https://buildkite.com/myorg/pipeline/builds/456"

Drill into a specific job to see individual test/step timings:

bkslow --threshold 5s "https://buildkite.com/myorg/pipeline/builds/456#job-uuid"

Log Analysis

When you point bkslow at a specific job, it tries to parse the log into individual entries. It tries these strategies in order and uses the first one that matches:

  1. Pytest -- Detects path/test.py::test_name patterns with PASSED/FAILED/SKIPPED results. Each test becomes a waterfall entry.

  2. Docker build -- Detects #N [stage M/N] COMMAND and #N DONE Xs lines from docker build --progress plain. Each build step becomes a waterfall entry. Parallel stages show as overlapping bars.

  3. Shell commands -- Detects $ command lines that Buildkite adds when executing each command in your script. Each command becomes a waterfall entry with duration computed from timestamps between consecutive commands.

  4. Group sections -- Falls back to Buildkite's built-in section markers (~~~, ---, +++). This is what you get when none of the above match.

Gotchas

  • Minimum step threshold: Docker and shell command parsing both require at least 2 matching entries to activate. A single docker step or a single $ command will fall through to group section parsing.

  • Timestamps required for shell commands: The shell command parser needs OSC or [ISO timestamp] prefixed lines to compute durations. If your log has $ lines but no timestamps, it falls back to group sections.

  • Docker CACHED steps have no duration: Cached docker steps don't produce DONE lines, so they won't appear in the output. Use --threshold 0 if you want to see very short steps.

  • Name truncation: Job names are truncated to --name-width (default 40 chars). Docker step commands and shell commands are truncated to 80 chars.

  • Multi-org tokens: bkslow matches the org slug from the URL against ~/.config/bk.yaml. If the org isn't found, it falls back to the selected_org entry. If you have multiple orgs configured via bk configure, the right token is selected automatically.

  • Step ID resolution: When using ?sid= URLs, bkslow makes an extra API call to resolve the step ID to a job ID. If the step ID doesn't match any job in the build, it exits with an error.

  • Running builds: For in-progress builds, running jobs use the current time as their end time, so the chart reflects the duration so far.

  • Soft failures: Jobs that passed but have soft_failed=true are shown as "SFAIL" in the waterfall.

  • Docker terminal width: When running via Docker, terminal width auto-detection requires a TTY. Use docker run -t or pass --width explicitly.

About

Find what's slow in your Buildkite builds 🐌

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages