# CLI reference

> Markdown source of https://docs.magmamoose.com/chargate/cli/.

<!-- sources: src/chargate/cli.py, src/chargate/gate.py -->

Both GitHub surfaces drive the same `chargate` CLI. Exit codes: `0` pass ·
`1` blocking net-new findings · `2` setup/usage error.

```sh
chargate <filter-sarif | ci | sbom | local | install-hooks | uninstall-hooks | version> [options]
```

## `chargate filter-sarif`

The pure net-new filter: a SARIF report + a base/head → filtered SARIF + counts +
a gate exit code. Decoupled from GitHub Actions and unit-tested in isolation.

This subcommand and the counts JSON it writes are a **stable public interface** for
other tools: the counts document carries a `schema_version`, and a consumer must
hard-fail on a version it does not recognise rather than gate on a document it cannot
read. Key-by-key contract, invariants, and the exit codes a consumer must distinguish:
[Consuming the output](https://docs.magmamoose.com/chargate/consuming-output/).

```sh
chargate filter-sarif --sarif report.sarif --base "$BASE" --head "$HEAD" \
    --out net-new.sarif --counts-json counts.json --fail-on any
```

| Flag | Default | Purpose |
| --- | --- | --- |
| `--sarif` | (required) | Path to the full SARIF report. |
| `--base` | (required) | Base ref/SHA (PR target). |
| `--head` | `HEAD` | Head ref/SHA. |
| `--repo` | `.` | Path to the git repository. |
| `--precision` | `line` | Net-new precision: `line` or `file`. |
| `--no-location-policy` | `ignore` | Treatment of results with no file location: `ignore` (never block) or `block`. |
| `--no-region-fallback` | off | Disable file-level fallback for changed-file results lacking a `startLine`. |
| `--no-sops-ignore` | off | Gate on secret-scanner hits even on SOPS-encrypted values (`ENC[AES256_GCM,...]`). By default these are dropped as false positives, see [Net-new gating](https://docs.magmamoose.com/chargate/net-new/#sops-encrypted-secrets). |
| `--strip-prefix` | (none) | Path prefix to strip from SARIF URIs before matching (repeatable). |
| `--no-merge-base` | off | Diff `base..head` directly instead of `merge-base(base, head)..head`. |
| `--out` / `--full-out` / `--counts-json` | (none) | Write the net-new SARIF / a copy of the full SARIF / counts JSON. |
| `--fail-on` | `any` | Severity threshold that blocks: `any\|critical\|high\|medium\|low\|none`. |
| `--no-gate` | off | Always exit `0` (report only). |
| `--quiet` | off | Suppress the human summary. |

## `chargate ci`

The full CI flow: run MegaLinter, preserve the full SARIF, gate on net-new (PR
events only), and optionally ship to the sinks (DefectDojo / Dependency-Track).
Each sink is active iff its host/URL flag is set.

```sh
chargate ci --mode auto --flavor all --sarif-out full.sarif
```

Every image-selection flag also reads a `CHARGATE_*` env var when the flag is
omitted (`CHARGATE_MEGALINTER_REGISTRY`, `CHARGATE_MEGALINTER_NAMESPACE`,
`CHARGATE_MEGALINTER_IMAGE`, `CHARGATE_MEGALINTER_TAG`, `CHARGATE_DOCKER_PLATFORM`,
`CHARGATE_ARCH_STRATEGY`, `CHARGATE_JOBS`), so a self-hosted runner fleet can point
every repo at an internal mirror without editing any workflow. Explicit flag beats
env var beats built-in default.

Key flags beyond the shared filter options:

| Flag | Default | Purpose |
| --- | --- | --- |
| `--mode` | `auto` | `auto` (from `GITHUB_EVENT_NAME`), `pr` (net-new gate), or `baseline` (no gate). |
| `--sarif` | (none) | Use an existing SARIF instead of running MegaLinter. |
| `--flavor` | `all` | MegaLinter flavor (`all` = full image), or `quality` — a five-linter set chargate curates, with no upstream image, so it always runs standalone. See [The `quality` flavor](https://docs.magmamoose.com/chargate/setup/#the-quality-flavor). |
| `--megalinter-tag` | `v10.0.0` | MegaLinter image tag, or a `sha256:…` digest to pin. |
| `--megalinter-registry` | `ghcr.io` | Registry host. Docker Hub is frozen at `v9.4.0`, so it cannot serve `v9.5.0+`. |
| `--megalinter-namespace` | `oxsecurity` | Image namespace (set for a mirror / pull-through cache). |
| `--megalinter-image` | (none) | Full image reference, overriding registry/namespace/flavor/tag entirely. |
| `--docker-platform` | (none) | Value for `docker run --platform` (e.g. `linux/amd64` to force emulation). |
| `--arch-strategy` | `auto` | `auto` (flavor image on amd64, per-linter images on arm64) · `flavor` · `standalone` · `fail`. |
| `--standalone-linter` | (none) | Linter key for standalone mode (repeatable). Default: the flavor's SARIF-emitting set. |
| `--jobs` | `4` | Standalone mode: concurrent linter containers. |
| `--enable-linter` / `--disable-linter` | (none) | Toggle a linter (repeatable). |
| `--incremental` | off | PR/gate mode only. Runs MegaLinter over just the files changed vs the base (`VALIDATE_ALL_CODEBASE=false`) instead of the whole repo. The net-new gate still uses chargate's own diff, so this changes scan cost, not the verdict. |
| `--default-branch` | `""` | Base branch for incremental change detection. Sets MegaLinter's `DEFAULT_BRANCH`. |
| `--sarif-out` / `--filtered-out` / `--counts-json` | (none) | Write the full / net-new / counts outputs. The action always passes all three; the last two are the [documented consumer interface](https://docs.magmamoose.com/chargate/consuming-output/). |
| `--strict` | off | Fail the job if MegaLinter itself errors. (A SARIF with no runs fails without it, see [architecture](https://docs.magmamoose.com/chargate/architecture/).) |
| `--defectdojo-url` | (none) | DefectDojo base URL (enables import of the full SARIF). |
| `--defectdojo-token-env` | `DEFECTDOJO_TOKEN` | Env var holding the DD API token. |
| `--dd-product` / `--dd-engagement` / `--dd-engagement-id` | (none) | DefectDojo targeting. |
| `--dd-product-type` | (none) | DefectDojo product type name. Required only when the product does not exist yet and has to be auto-created. |
| `--dd-test-title` | (none) | Title for the DefectDojo test. |
| `--dd-tag` | (none) | Tag to attach to the import. Repeatable. |
| `--dd-import` / `--dd-no-close-old` / `--dd-insecure` | off | Use import (not reimport) / keep old findings / skip TLS verify. |
| `--dependency-track-url` | (none) | Dependency-Track base URL (enables CycloneDX BOM upload). |
| `--dt-api-key-env` | `DEPENDENCYTRACK_API_KEY` | Env var holding the DT API key. |
| `--bom` | (none) | Path to the CycloneDX BOM to upload (the action generates this with Syft). |
| `--dt-project-name` / `--dt-project-version` / `--dt-project-uuid` | (none) | Dependency-Track project targeting. |
| `--dt-parent-name` / `--dt-parent-version` | (none) | Parent project, when you keep Dependency-Track projects in a hierarchy. |
| `--dt-no-auto-create` / `--dt-is-latest` / `--dt-insecure` | off | Don't auto-create the project / mark latest / skip TLS verify. |
| `--dt-project-tag` | (none) | Dependency-Track project tag, repeatable. A `repo:<name>` tag is the join key for grouping a repo's projects. |
| `--pr-comment` | off | Post GHAS-style PR comments for net-new findings (PR/gate mode only). |
| `--pr-number` / `--repo-slug` | (none) | Pull request number and `owner/repo` to comment on. |
| `--github-token-env` | `GITHUB_TOKEN` | Env var with a token that has `pull-requests: write`. |
| `--pr-comment-mode` | `both` | `summary` (one updatable comment), `inline`, or `both`. |
| `--pr-comment-max-inline` | `50` | Cap on inline comments; the rest stay in the summary. |
| `--pr-comment-insecure` | off | Skip TLS verification for the GitHub API (GHES testing). |

PR comments are net-new only and failure-isolated: a GitHub API error is logged and
never changes the gate outcome. The host action sets `--pr-number` / `--repo-slug`
from the event and honors `GITHUB_API_URL` for GHES.

## `chargate sbom`

Ship a CycloneDX BOM to Dependency-Track on its own — no MegaLinter, no SARIF, no
gate. Seconds, not the ~15 minutes a scan costs.

`chargate ci` already carries the BOM sink, but only fires it on **non-PR** events:
a BOM per pull request would litter Dependency-Track with throwaway `N/merge`
project versions. A consumer whose gate runs on `pull_request` only — the usual
shape, because a full scan on every merge is the most expensive thing in CI — never
reaches that path, so the repo never appears in Dependency-Track at all. This
subcommand is that missing push-time path standing alone.

```sh
chargate sbom \
  --dependency-track-url https://dtrack.example.com \
  --bom sbom.cdx.json \
  --dt-project-name "$GITHUB_REPOSITORY" \
  --dt-project-version main
```

It takes the same Dependency-Track flags as `chargate ci` (`--dependency-track-url`,
`--dt-api-key-env`, `--bom`, `--dt-project-*`, `--dt-parent-*`, `--dt-no-auto-create`,
`--dt-is-latest`, `--dt-insecure`) plus:

| Flag | Default | Meaning |
| --- | --- | --- |
| `--strict` | off | Treat a Dependency-Track upload failure as fatal (exit `2`). |
| `--quiet` | off | Suppress the human summary. |

**Exit policy is the inverse of the sink-on-a-gate rule, deliberately.** A
*misconfigured* sink — no URL, no project, no API key, no BOM file — exits `2`.
This command exists only to upload, so "nothing to upload to" is a broken job, not
a sink someone switched off; silence in exactly that case is what let a whole org's
repos go missing from Dependency-Track unnoticed. A Dependency-Track *outage* stays
failure-isolated (warn, exit `0`) unless `--strict`, so a server that is merely down
cannot turn every repo's CI red.

On success it writes the project link to the job summary and to a
`dependency_track_url` action output.

## `chargate local`

Fast staged-file checks for pre-commit (gitleaks + ruff, each skipped if the tool
is absent). A first line, deliberately narrower than the full CI net.

```sh
chargate local path/to/file.py     # pre-commit passes the staged files
chargate local                      # no args -> checks staged files
```

## `chargate install-hooks`

Wire Chargate's git hooks into **every** repo globally, using the
[pre-commit](https://pre-commit.com) framework (which must be installed). It
generates `pre-commit` + `pre-push` + `commit-msg` dispatchers pointed at a global
`~/.pre-commit-config.yaml`, sets `core.hooksPath` (so the hooks apply to existing
repos immediately) and `init.templateDir` (so new clones inherit them).

```sh
chargate install-hooks          # refuses to clobber a hand-maintained config
chargate install-hooks --force  # overwrite a non-chargate ~/.pre-commit-config.yaml
```

Chargate's hooks live inside a regenerated `>>> chargate-managed >>>` block; any
repos you add outside that block are preserved on reinstall. Installed via Homebrew,
`brew install calebsargeant/tap/chargate` brings `pre-commit` along. See
[Setup → Global hook install](https://docs.magmamoose.com/chargate/setup/#global-hook-install-all-repos) for the full
walkthrough.

## `chargate uninstall-hooks`

Revert `install-hooks`, restoring (or unsetting) the prior global `core.hooksPath`
and `init.templateDir`.

```sh
chargate uninstall-hooks
```

## `chargate version`

Prints the chargate version (also `chargate --version`).
