CLI & CI

The point of storing collections as files is that something other than a GUI can run them. The CLI shares the same runner as the desktop app, so a request cannot behave one way on your machine and another way in CI.

Install

curl -fsSL https://mandalo.dev/install.sh | sh

That works on macOS and Linux, on both Intel and ARM. It picks the archive for your machine off the latest GitHub release, checks it against the release's SHA256SUMS, refuses to go on if the checksum does not match, and installs into ~/.local/bin — no sudo, and it tells you where the binary went. Set MANDALO_INSTALL_DIR to put it somewhere else, or MANDALO_VERSION to pin a tag.

There is also a Homebrew tap — brew install De-Rus/tap/mandalo — and on Windows a .zip on the release page. Every platform archive is listed there by target triple, alongside SHA256SUMS if you would rather verify and unpack it yourself.

cargo install mandalo-cli does not work: nothing is published to crates.io yet. Building from a checkout is cargo build --profile release-cli -p mandalo-cli.

The command surface

mandalo run <collection> [--folder PATH] [--env NAME] [--reporter pretty|json|junit] [--fail-fast]
mandalo send <collection> <request-path> [--env NAME]
mandalo ls
mandalo env list | get <name> | set <name> <key> <value>
mandalo import <file>
mandalo export <file>
mandalo scan [--staged]
  • run executes every request in a collection, in order, applying the same assertions, captures and scripts the app applies. It exits non-zero if any request fails.
  • send runs a single saved request — mandalo send payments-api auth/login.http --env staging — and prints the response.
  • ls prints the collection tree, which is the fastest way to learn the request paths send wants.
  • env reads environments and writes non-secret variables into them.
  • import and export are the Postman importer and the bundle exporter from the app.
  • scan looks for credential-shaped literals in workspace files and exits non-zero on a hit — with --staged it looks only at what you are about to commit, which makes it a usable pre-commit hook.

Two global flags matter: --workspace selects the workspace directory, and --strict-network refuses requests to loopback, private, link-local and cloud metadata addresses — the flag you want on a shared runner.

Secrets

Secrets are not meant to live in the workspace files. Any variable the environment does not define is read from the process environment as MANDALO_SECRET__<ENV>__<KEY>: the environment name and variable name upper-cased, with - replaced by _. Resolved secret values are scrubbed from the runner's own output, and under GitHub Actions they are additionally registered as masked values. The resolution rules are the same ones described in Environments & variables.

A GitHub Actions workflow

Copy this into .github/workflows/ and it runs.

name: api-checks

on:
  pull_request:
  schedule:
    - cron: "0 6 * * *"

jobs:
  smoke:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install mandalo
        run: |
          curl -fsSL https://mandalo.dev/install.sh | sh
          echo "$HOME/.local/bin" >> "$GITHUB_PATH"

      - name: Run the payments suite
        env:
          MANDALO_SECRET__STAGING__TOKEN: ${{ secrets.STAGING_API_TOKEN }}
        run: |
          mandalo run payments-api \
            --workspace ./api \
            --env staging \
            --strict-network \
            --reporter junit > results.xml

      - name: Publish results
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: api-results
          path: results.xml

The shape is the point: the collection under review is the collection that runs, the environment file in the repo supplies the non-secret variables, and the one secret comes from the Actions secret store and never touches a file.

Pre-commit hygiene

#!/bin/sh
# .git/hooks/pre-commit
mandalo scan --staged || exit 1

Committing a token by accident is the failure mode that actually happens with file-based collections. A staged scan is the cheap guard; the habits in Collections & git are the real fix.

Following along

Watch the repository for the release that ships the binary, and the changelog for the announcement. Until then, the desktop app is the supported way to run requests.