CLI Installation & Authentication
Install the Mergify CLI and authenticate it to manage your merge queue, freezes, and stacked pull requests from the terminal.
The Mergify CLI lets you interact with Mergify features directly from your terminal. This page covers installation, authentication, and the exit codes commands return; for the commands themselves, see the CLI reference.
Installation
Section titled InstallationmacOS (Homebrew)
Section titled macOS (Homebrew)On macOS, the recommended way to install the CLI is through Mergify’s Homebrew tap:
brew install mergifyio/tap/mergify-cliUpgrade with brew upgrade mergify-cli.
Linux and macOS (install script)
Section titled Linux and macOS (install script)On Linux, or on macOS if you’d rather not use Homebrew, install with the official script:
curl -fsSL https://raw.githubusercontent.com/Mergifyio/mergify-cli/main/install.sh | shThis installs mergify to ~/.local/bin. Set MERGIFY_INSTALL_DIR to pick a
different location, or MERGIFY_VERSION to pin a specific release:
curl -fsSL https://raw.githubusercontent.com/Mergifyio/mergify-cli/main/install.sh | MERGIFY_INSTALL_DIR="$HOME/bin" shOnce installed this way, upgrade with mergify self-update.
Windows
Section titled WindowsDownload mergify-<version>-x86_64-pc-windows-msvc.zip from the
latest release,
extract it, and put mergify.exe anywhere on your PATH.
GitHub Actions
Section titled GitHub ActionsTo install the CLI in a GitHub Actions workflow, use the
Mergifyio/setup-cli action. It
downloads the prebuilt mergify binary, verifies it against the release
SHA256SUMS, and adds it to the PATH. No Python or extra toolchain is
required, and it runs on Linux and macOS runners.
- uses: Mergifyio/setup-cli@v1- run: mergify --versionBy default the action installs a pinned version, which keeps your CI
reproducible. Set the mergify_cli_version input to latest to install the
newest release instead. The action also exposes the version it actually
installed as the mergify_cli_version output:
- uses: Mergifyio/setup-cli@v1 id: setup-cli with: mergify_cli_version: latest
- run: echo "Installed mergify-cli ${{ steps.setup-cli.outputs.mergify_cli_version }}"Authentication
Section titled AuthenticationThe CLI talks to two APIs, and they take different credentials:
-
The Mergify API backs
mergify queue,mergify events,mergify freeze,mergify ci,mergify tests, andmergify config simulate. (mergify config validateneeds no credential: it fetches the public schema and checks your file locally.) -
The GitHub API backs Stacks, which creates and updates pull requests on GitHub directly.
mergify auth login covers the Mergify half. Stacks still needs a GitHub token
of its own. The auth command reference lists the options each
mergify auth command takes.
Sign in to Mergify
Section titled Sign in to Mergifymergify auth loginThe CLI opens the approval page in your browser and prints the URL and a short
code. Type the code in, check the name on the token so you recognize the machine
later, and authorize. The CLI picks up its credential and stores it. This is an
OAuth 2.0 device authorization
grant, so nothing listens on a local
port and the browser does not have to be on the same machine. Over SSH or inside
a container, open the printed URL on a machine that has one. Pass --no-browser
to print the URL without opening a browser.
That name arrives filled in as Mergify CLI on <hostname>: the CLI reads the
machine’s hostname and sends it when it opens the grant. --device-name sends a
name of your choosing instead, and --device-name= sends none, leaving the
token under a default every machine shares. The name travels with the grant,
before anyone approves it, so editing the field on the approval page is too late
to keep a hostname off Mergify — --device-name= is what does that.
What you get is a Mergify user token. It identifies you, reaches exactly what your dashboard session reaches, and is never sent to GitHub.
The CLI stores it in your operating system’s credential store: Keychain on macOS, the Secret Service on Linux, Credential Manager on Windows. Where none is available, on a headless container for instance, it falls back to a file in your configuration directory, restricted to your user. On a container image or a shared runner, treat that file as the secret it is.
mergify auth status reports whether this machine holds a credential:
mergify auth statusmergify auth logout revokes it on the server, not just on this machine:
mergify auth logoutYou can also list and revoke your tokens under Settings > Developer > CLI Tokens in the dashboard. That list is personal, not per-organization: it shows the same tokens whichever organization you have selected. A token lasts a year, and you can hold 20 at a time. At that ceiling the approval page refuses the new login and tells you to revoke one, so watch the browser rather than the terminal if a sign-in never completes.
On-premise installations
Section titled On-premise installationsAll three commands take --api-url, and so does every other command that calls
the Mergify API:
mergify auth login --api-url https://mergify.example.comCredentials are stored per API URL, so one machine can hold a login for the
hosted service and one for an on-premise installation at the same
time. That also means the URL has to match afterwards. A command that resolves a
different API URL does not find the credential you stored, and falls through to
the next source in the list below. Set MERGIFY_API_URL once instead of passing
--api-url every time:
export MERGIFY_API_URL=https://mergify.example.commergify auth logout and mergify auth status are per URL too, so signing out
of one installation leaves the other’s credential in place.
How Mergify API commands find a token
Section titled How Mergify API commands find a tokenCommands that call the Mergify API resolve a credential in this order:
- The
--tokenoption on the command. - The
MERGIFY_TOKENenvironment variable. - The credential stored by
mergify auth login, for that API URL. GITHUB_TOKEN. Deprecated.gh auth token, from the GitHub CLI. Deprecated.
The mergify ci commands that call the API are the exception, because the
endpoints they post to accept nothing but an application key.
mergify ci scopes-send, mergify ci junit-process, and the deprecated
mergify ci junit-upload all skip step 3: they never read the credential
mergify auth login stored, since those endpoints would refuse it anyway. The
rest of the order stands, so a job that exports GITHUB_TOKEN alone still
reaches step 4. The failure it gets back is the endpoint refusing a GitHub
token rather than the CLI finding no credential. Set MERGIFY_TOKEN to an
application key, as the warning they print tells
you to.
There is no global --token on mergify itself, so pass it to the subcommand:
mergify <command> --token your_token_hereEach command’s entry in the CLI reference lists the options it accepts,
--token included.
The stored credential deliberately outranks GITHUB_TOKEN, so a GITHUB_TOKEN
left exported in a shell cannot quietly override the account you signed in as.
--token and MERGIFY_TOKEN stay above it, so a CI job that sets either keeps
working unchanged.
Application keys for CI
Section titled Application keys for CImergify auth login is interactive, which makes it the wrong tool for a CI job.
Unattended runs use an application key instead. Create one from your
dashboard and set it as MERGIFY_TOKEN:
export MERGIFY_TOKEN=your_token_hereAn application key carries either the admin scope or the ci scope, and
belongs to a GitHub account rather than to a single repository. See
Application Key Scopes for what each one
covers. mergify ci junit-process (and the deprecated mergify ci junit-upload)
and mergify ci scopes-send post to endpoints that accept nothing but a ci
key, so those commands need one specifically. A user token from
mergify auth login does not work for them.
The mergify tests commands go the other way. Their endpoints refuse a ci key
and answer 403, so they need an admin key, a GitHub personal access token,
or a user token. mergify tests quarantines list and
mergify tests quarantines get are the exception, since both read the same
quarantine list endpoint.
GitHub token for Stacks
Section titled GitHub token for StacksStacks commands act on pull requests through the GitHub API, so they need a
GitHub token specifically. mergify auth login does not cover them, and a
Mergify application key does not work here either.
They resolve that token on their own, in this order:
- The
--tokenoption on the command. - The
MERGIFY_TOKENenvironment variable. GITHUB_TOKEN.gh auth token, from the GitHub CLI.
Nothing here is deprecated: a GitHub token is what the GitHub API takes.
Every source except --token is checked for the mut_ prefix of a
Mergify user token before it is used.
GitHub can only answer 401 to one of those, so it is skipped and the next
source tried. That covers gh auth token as well, which echoes GITHUB_TOKEN
when the variable is set. So exporting the credential mergify auth login
stored does not break a Stacks command that has a real GitHub token further
down the list. A --token you pass explicitly is sent as given.
If you have the GitHub CLI (gh) installed and
authenticated, Stacks picks up its token automatically. Nothing else to
configure. Otherwise, create a personal access
token
and set it as an environment variable:
export GITHUB_TOKEN=your_token_hereExit Codes
Section titled Exit CodesThe CLI reports failures with these exit codes, so a CI job can tell a conflict apart from an API failure without parsing the output:
| Code | Name | Meaning |
|---|---|---|
0 | Success | Command completed successfully. |
1 | GenericError | Unclassified runtime failure (I/O error, bug, or captured panic). |
2 | — | Argument parsing or usage error. |
3 | StackNotFound | Stack, branch, or commit not found. |
4 | Conflict | Rebase or merge conflict. |
5 | GitHubApiError | GitHub API request failed. |
6 | MergifyApiError | Mergify API request failed. |
7 | InvalidState | CLI invariant violated (e.g. command run outside a valid context). |
8 | ConfigurationError | Configuration or credentials missing, unparseable, or failing validation. |
Code 2 is the only one with no name. The argument parser produces it before
any command runs, for an unknown flag or a missing argument, so there is no
command-level error behind it to name.
Was this page helpful?
Thanks for your feedback!