Metadata-Version: 2.4
Name: bbdash
Version: 0.1.0
Summary: BitBake Dashboard — passive monitor for concurrent BitBake builds
Project-URL: Homepage, https://github.com/robwoolley/bbdash
Project-URL: Repository, https://github.com/robwoolley/bbdash
Project-URL: Issues, https://github.com/robwoolley/bbdash/issues
Author-email: Rob Woolley <rob@robertwoolley.com>
License: MIT
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Build Tools
Requires-Python: >=3.10
Requires-Dist: rich>=13.0
Requires-Dist: tomli>=2.0; python_version < '3.11'
Requires-Dist: typer>=0.12
Provides-Extra: mcp
Requires-Dist: mcp>=1.0; extra == 'mcp'
Provides-Extra: web
Requires-Dist: fastapi>=0.110; extra == 'web'
Requires-Dist: uvicorn>=0.29; extra == 'web'
Description-Content-Type: text/markdown

# BitBake Dashboard (`bbdash`)

Passive monitor for concurrent BitBake builds: reports which builds are
running across independent project directories — their state, targets,
workers, and tasks — using only `/proc` and read-only build-directory
artifacts. It never connects to `bitbake.sock` and never interferes with a
build.

Status: milestones 1–4 done (local CLI, MCP server, remote SSH hosts, web
dashboard). See [specs/](specs/) for the requirements, design, and task plan.

## Install

```console
$ uv tool install .          # from a checkout
$ bbdash --version
```

Or run in place during development: `uv run bbdash ...`.

## Usage

```console
$ bbdash list                 # one line per discovered build
$ bbdash status               # detailed view of the build enclosing $PWD
$ bbdash status DIR           # detailed view of a specific build dir
$ bbdash status DIR --fast    # L1 overview only (skip worker/task detail)
$ bbdash watch DIR            # live-refreshing view, Ctrl-C to quit
$ bbdash history DIR          # past buildstats runs, most recent first
$ bbdash --json list          # machine-readable Snapshot JSON (see below)
```

Global options (before the subcommand): `--root PATH` (repeatable — build
roots to scan; defaults to `~/.config/bbdash/config.toml`, then `$PWD`),
`--json`, `--no-procfs` (skip the `/proc` scan; artifact-only, deterministic),
`--host NAME` (repeatable) / `--all-hosts` (also query configured remote
hosts — `list` only, see below).

Exit codes: `0` ok, `1` error (e.g. `status` given an ambiguous directory
containing multiple builds), `2` no builds found — scriptable.

### Remote hosts (`bbdash --all-hosts list`)

Agentless: `bbdash` ships its collector as a single stdlib-only file and
runs it on each configured host via plain `ssh` — nothing needs to be
installed there beyond Python 3.10+.

```console
$ bbdash --all-hosts list          # local builds + every configured host
$ bbdash --host buildbox list      # local + just "buildbox" (repeatable)
```

A host that fails to respond (unreachable, times out, bad output) is
reported as a warning on stderr and simply left out — it never fails the
command or blocks the other hosts' results. This currently only applies to
`list`; `status`/`watch`/`history` resolve "which build" from a local
directory, a semantic that doesn't generalize to a remote host without more
design (see specs/design.md).

### Config file

`~/.config/bbdash/config.toml`:

```toml
roots = ["/home/you/yocto/builds"]
interval = 2.0      # bbdash watch default refresh, seconds
ssh_timeout = 10.0  # per-host SSH timeout, seconds

[[hosts]]
name = "buildbox"               # optional; defaults to `host`
host = "buildbox.internal"
user = "ci"                     # optional
roots = ["/srv/yocto/builds"]
ssh_opts = ["-p", "2222"]       # optional, passed straight to `ssh`
```

## JSON schema (`--json`)

Every command's `--json` output is a **Snapshot** (`schema_version: 1`,
`src/bbdash/collector/model.py`):

```jsonc
{
  "schema_version": 1,
  "host": "hostname",
  "collected_at": "2026-07-26T09:49:00+00:00",
  "builds": [{
    "build_dir": "/…/sample-yocto-sitl/build",
    "root": "sample-yocto-sitl",
    "host": null,   // set only when merged from --host/--all-hosts (M3)
    "state": "running",   // running|parsing|idle-server|finished|failed|stale-lock|unknown
    "state_evidence": ["server-pid-alive", "…"],
    "server": {"pid": 292830, "started_at": "…", "cpu_pct": 12.0, "rss_kb": 524288},
    "targets": ["mc:machine-a:sample-firmware"],
    "config": {"machine": null, "sstate_dir": "/…/.sstate-cache", "dl_dir": "/…", "...": "…"},
    "sources": {"procfs": "ok", "cookerdaemon_log": "ok", "buildstats": "absent"},
    "progress": {"current": 512, "total": 2041},
    "workers": [{"pid": 293001, "cpu_pct": 95.0, "tasks": [{"recipe": "…", "task": "do_compile"}]}],
    "recent_tasks": [{"recipe": "…", "task": "…", "outcome": "ok", "duration_s": 12.3}],
    "errors": [],
    "buildstats_dir": "tmp/buildstats/20260726134405"
  }],
  "shared": [{"kind": "sstate", "path": "/…/.sstate-cache", "builds": ["…", "…"]}]
}
```

Fields fed by an optional source (buildstats, console logs, conf vars) are
nullable; `sources` says why a value is missing (`absent` = not enabled/not
yet created, `unreadable` = permission/I-O error) rather than leaving you to
guess whether "null" means "disabled" or "broken". A `sources` key only
appears once its source was actually consulted at the requested detail level
— `list` (L1) omits `buildstats` entirely rather than reporting it `absent`.

`schema_version` gates compatibility: additive fields don't bump it, breaking
changes do.

## Web dashboard

```console
$ uv sync --extra web        # or: pip install bbdash[web]
$ bbdash serve                # http://127.0.0.1:8765, Ctrl-C to stop
$ bbdash --all-hosts serve    # include configured remote hosts too
```

A single auto-refreshing page (plain HTML/CSS/JS, no build step, no CDN
dependencies — reads fine offline) listing every discovered build; click a
row to drill into its config, live workers, recent tasks, errors, and data
gaps, same detail `bbdash status` shows. `GET /api/snapshot` (same query
params as the params above: `root`, `level`, `host`, `all_hosts`,
`no_procfs`) serves the identical Snapshot JSON directly, for scripting
against the running dashboard. Binds to `127.0.0.1` by default — this is a
passive local viewer, not meant to be exposed on a network; override with
`--bind`/`--port` if you know what you're doing.

## MCP server (for LLM clients)

```console
$ uv sync --extra mcp        # or: pip install bbdash[mcp]
$ claude mcp add bbdash -- uv run --extra mcp bbdash mcp
```

Exposes 5 read-only tools over stdio — `list_builds`, `build_status`,
`list_tasks`, `build_history`, `tail_log` — each a thin wrapper over the same
collector the CLI uses, every one annotated `readOnlyHint=True`,
`destructiveHint=False`. `list_builds` also takes an optional `hosts` list
(configured host names, or `["all"]`) to merge in remote builds the same way
`bbdash --all-hosts list` does. See `src/bbdash/mcp_server.py` for full tool
docstrings (units, enums, and what each field means) and
`specs/acceptance-m2.md` for a verified example transcript.

## Development

```console
$ uv sync
$ uv run pytest
$ uv run ruff check
```

Spec-driven: `specs/tasks.md` is the work queue; `specs/design.md` has the
architecture and the environment facts it's built on; `CLAUDE.md` documents
the hard rules (non-interference with live builds, stdlib-only collector,
fixture-only tests). `scripts/record-fixture.py` captures a real build
directory's small artifacts into `tests/fixtures/` for scenario tests.

## Releasing

The published version comes entirely from the git tag (`hatch-vcs`,
`pyproject.toml`'s `[tool.hatch.version]`) — nothing to bump by hand.

```console
$ git tag v0.2.0
$ git push origin v0.2.0
```

That triggers `.github/workflows/publish.yml`: run tests → build sdist +
wheel → wait for approval in the `pypi` GitHub Environment → publish to
PyPI via Trusted Publishing (OIDC, no stored token). Approve the run at
`github.com/robwoolley/bbdash/actions` once it's waiting. See
`specs/publishing.md` for the full setup (PyPI Trusted Publisher
configuration, required one-time steps).
