ripbi-core 0.3.1

Static analysis engine for Power BI semantic models: TMDL and PBIR ingestion, DAX reference extraction, dependency graph, and reachability
Documentation
# ripbi

[![CI](https://github.com/bgarcevic/ripbi/actions/workflows/ci.yml/badge.svg)](https://github.com/bgarcevic/ripbi/actions/workflows/ci.yml)
[![crates.io](https://img.shields.io/crates/v/ripbi.svg)](https://crates.io/crates/ripbi)

Static analysis, linting, and tree-shaking for Power BI semantic models and DAX.

Docs: [bgarcevic.github.io/ripbi](https://bgarcevic.github.io/ripbi/).

I created ripbi because I liked Measure Killer, but I was looking for an
agent-friendly, free, fast tool to scan semantic models and the connected
reports to identify potentially unused semantic model objects. I tested it on a
shared semantic model with 14 connected reports, and it's 99% faster than
Measure Killer, reducing processing time from almost 4 minutes to a couple of
seconds.

It works without opening the reports or semantic models, so it can run as part
of a CI pipeline. It's also cross-platform, with no dependency on Power BI
Desktop or similar.

It currently works only on local PBIP projects — a TMDL semantic model plus
PBIR reports; `.pbix` and `.pbit` files are not supported yet. I plan to
implement guided automated cleanup, a UI, and tenant scanning.

## Install

macOS and Linux:

```sh
curl -fsSL https://raw.githubusercontent.com/bgarcevic/ripbi/main/install.sh | sh
```

Windows (PowerShell):

```powershell
irm https://raw.githubusercontent.com/bgarcevic/ripbi/main/install.ps1 | iex
```

Both scripts verify the download against the release's sha256 checksums and
install the binary into `~/.local/bin` as both `ripbi` and its short alias
`rib` — the two names are the same tool, so `rib scan` works anywhere
`ripbi scan` does. Or `cargo install ripbi`.

<details>
<summary>Pinning a version, reviewing the scripts first, building from source</summary>

Pin a version:

```sh
curl -fsSL https://raw.githubusercontent.com/bgarcevic/ripbi/main/install.sh | RIPBI_VERSION=v0.3.1 sh
```

```powershell
$env:RIPBI_VERSION = 'v0.3.1'; irm https://raw.githubusercontent.com/bgarcevic/ripbi/main/install.ps1 | iex
```

Both scripts are plain shell and PowerShell. Download them first if you would
rather read before running:

```sh
curl -fsSL https://raw.githubusercontent.com/bgarcevic/ripbi/main/install.sh -o install.sh
sh install.sh
```

```powershell
irm https://raw.githubusercontent.com/bgarcevic/ripbi/main/install.ps1 -OutFile install.ps1
powershell -ExecutionPolicy Bypass -File .\install.ps1  # the flag is only needed if your policy blocks script files
```

From a clone of this repository:

```sh
cargo install --path crates/ripbi-cli
```

</details>

## Updating

Installed with one of the scripts above? `ripbi update` downloads the latest
release, verifies its sha256 checksum, and replaces `ripbi` and `rib` in place:

```sh
ripbi update           # update in place; exits 0 when done
ripbi update --check   # report only: exits 1 when a newer release exists
```

Exit codes: `0` updated or up to date, `1` update available (only from
`--check`), `2` error (network, checksum, unsupported platform).

Every command checks for a new release at most once a day and prints a dim
one-line notice when one is available. That check is a plain `GET` of the
public release metadata — no data is sent — and `RIPBI_NO_UPDATE_CHECK=1`
disables it. It is also skipped automatically when stderr is not a terminal
(pipes, CI logs), when `CI` is set, or under `-q`.

Cargo-installed copies are never self-replaced: `ripbi update` prints the
`cargo install ripbi --force` command instead. A source build found under a
`target/` directory prints the install-script and `cargo install --path`
alternatives.

On Windows a running executable cannot be deleted, so a completed update may
leave `ripbi.exe.old` (or `rib.exe.old`) beside the new binary. It is safe to
delete, and the next update removes it.

## Quickstart

Clone the repository to get the sample projects, then scan one:

```sh
git clone https://github.com/bgarcevic/ripbi.git
cd ripbi
ripbi scan "samples/AdventureWorks Sales.pbip"
```

Output from the committed AdventureWorks sample (trimmed):

```text
130 objects, 74 reachable from 51 roots, 56 unused

Measures (11)
  'Sales'[Average Sales per Order]
    ← nothing references it
Columns (28)
  'Customer'[City]
    ← only used by hierarchy 'Customer'[Geography] — hierarchy level (also unused)
  …
```

The summary line counts the model's objects, how many the reports reach, and
how many are unused. Each finding says why it is dead: nothing references it,
or its only consumer is itself unused. Broken report bindings surface too
(issue #60): a visual whose field no longer resolves in the model — the
renamed column, the broken measure — is reported with its page, visual, and
reason. `--broken` scopes a run to breakage alone so CI can gate on it
separately from unused findings.

`ripbi scan` discovers the project itself: pass a `.pbip` file, a project
folder, a `.SemanticModel`, or a `.Report`, or nothing to scan the current
directory.

To scan one named model against every report bound to it, pass `--model`; each
`--report` folder is searched recursively for reports bound to that model:

```sh
ripbi scan --model "samples/AdventureWorks Sales.SemanticModel" --report samples/
```

The same search works without the flag when the path already names a semantic
model — `ripbi scan "samples/AdventureWorks Sales.SemanticModel" --report samples/`
walks plain `--report` folders exactly like `--model` mode.

Exit codes:

| Code | Meaning |
|------|---------|
| `0`  | Nothing unused |
| `1`  | Unused objects found |
| `2`  | Error (bad path, ingestion failure, …) |

```sh
ripbi scan -q   # no output; exit code only
```

## Output

The full output contract (human, `--summary`, `--plain`, `--json`, and
`ripbi.toml` configuration) is documented in the user guide's
[scan chapter](https://bgarcevic.github.io/ripbi/output.html).

## Contributing

Dev setup, workflow, and where things live are documented in
[CONTRIBUTING.md](CONTRIBUTING.md). The short version — CI is the definition
of done, and this is what CI runs:

```sh
cargo fmt --all --check
cargo clippy --workspace --all-targets --all-features --locked -- -D warnings
RUSTDOCFLAGS="-D warnings" cargo doc --workspace --no-deps --all-features --locked
cargo test --workspace
```

## License

Dual-licensed under [MIT](LICENSE-MIT) or [Apache-2.0](LICENSE-APACHE).
The `samples/` are Microsoft's own sample projects (MIT) and are not covered by
ripbi's license.