Skip to content

Scripting the Shorebird CLI

The Shorebird CLI can run without a person at the keyboard, in a CI job, a script, or a coding agent. This page covers how it behaves there, how to get machine-readable output, and how to run destructive commands safely.

Shorebird stops asking questions when any of these is true:

  • The global --json flag is passed.
  • stdout or stdin isn’t a terminal, for example when output is piped to a file.
  • A CI environment variable is set, such as CI (with any value), BOT=true, or the variables set by GitHub Actions, Azure Pipelines, Travis, AppVeyor, Cirrus CI, AWS CodeBuild, or Jenkins.

In non-interactive mode, nothing waits for input:

  • Some prompts use a default. shorebird init uses your pubspec name as the app’s display name. shorebird patch without --release-version builds the app first to guess which release to patch, which costs an extra build or can build the patch without the release’s settings. Always pass --release-version; see Patching for details. shorebird patch without --track publishes to stable, and with Shorebird 1.6.135 or newer warns that a future release will require --track.
  • Other prompts fail. When there’s no safe default, the command exits with an error and a hint, usually naming the flag that provides the answer. For example, shorebird patch fails when the patch build’s native code differs from the release unless you pass --allow-native-diffs, and when its assets differ unless you pass --allow-asset-diffs (see Patch stops in CI). shorebird init fails if you belong to several organizations and don’t pass --org-id.
  • Progress spinners become plain lines when stdout isn’t a terminal or --json is passed. Each step prints Starting ... and then Done ... or Failed ... to stderr.

To keep a job from failing or guessing, pass every input as a flag: the app with --app-id or --flavor, the release with --release-version, and the track with --track. For shorebird patch, --release-version=latest targets the most recently updated release. Authenticate with a service account’s key in the SHOREBIRD_TOKEN environment variable, since shorebird login needs a browser. In GitHub Actions, a job can sign in with OIDC instead, with Shorebird 1.6.129 or newer.

Pass the global --json flag to get one JSON object on stdout instead of human-readable text. --json also turns on non-interactive mode and removes color codes.

These commands return structured JSON:

orgs, usage, members, service-accounts, and audit-log need Shorebird 1.6.135 or newer; before it, shorebird account orgs listed organizations. enterprise needs Shorebird 1.6.136 or newer. Each of their commands’ --help ends with an example --json run; run it to see which fields data holds. audit-log returns events in the audit log’s event format, and no service-accounts command ever returns a key’s secret. The credential matters:

  • shorebird members works with a shorebird login session, a personal API key, or a service account’s key or GitHub Actions OIDC sign-in, unless the account is an Auditor. A release and patch only key can’t use it. Inviting, changing roles, and removing need the Admin or Owner role, which no service account holds.
  • shorebird service-accounts needs the Admin or Owner role. Reading works with a login session or an Admin’s or Owner’s personal API key (not a release and patch only key), but the commands that change something need a shorebird login session with SHOREBIRD_TOKEN unset; the API refuses API keys and GitHub Actions OIDC for them. service-accounts keys create opens the console.
  • shorebird audit-log works with a login session, a key in SHOREBIRD_TOKEN, or GitHub Actions OIDC, as long as the credential belongs to an Owner, an Admin, or an Auditor service account; a release and patch only key can’t read it. It needs a plan that includes the audit log.
  • shorebird enterprise works with a shorebird login session or a personal API key. An organization’s service account can’t use it, with a key or GitHub Actions OIDC; a service account the enterprise owns can read only what its role allows, such as the audit log for an Auditor.

Every other command, such as shorebird init or shorebird cache clean, also accepts --json and runs non-interactively. Its logs go to stderr, and when it succeeds it prints a success object whose data is empty ({}), so stdout always holds exactly one JSON object. --help with --json works the same way: the usage text goes to stderr. This needs Shorebird 1.6.129 or newer; earlier releases leave stdout empty when these commands succeed.

shorebird api is the exception: it prints the API response body as-is and rejects --json. See Calling the API from the command line.

A successful command prints an object with status set to success. The command’s result is in data, and meta says which CLI version and command produced it:

shorebird --version --json
{
  "status": "success",
  "data": {
    "shorebird_version": "1.6.123",
    "flutter_version": "3.47.5",
    "flutter_revision": "ff900e7fbab20fdeb40905ee631baa8d49bd0fa1",
    "engine_revision": "c663a2682e6adfc60a1aaff99ff62baa891dcf87"
  },
  "meta": { "version": "1.6.123", "command": "version" }
}

The fields inside data depend on the command.

A failed command prints an object with status set to error and exits with a non-zero code:

{
  "status": "error",
  "error": {
    "code": "fetch_failed",
    "message": "Failed to fetch channels for app \"<app-id>\"."
  },
  "meta": { "version": "1.6.123", "command": "channels list" }
}

error.code is one of:

CodeMeaning
usage_errorInvalid or missing arguments. Exit code 64.
interactive_prompt_requiredThe command needed an answer to a prompt. Exit code 64.
fetch_failedShorebird couldn’t retrieve something it needed, such as an app.
precondition_failedA check before the command ran failed, such as not being logged in or not finding shorebird.yaml.
software_errorThe command failed unexpectedly.
process_exitThe command stopped with a non-zero exit code.
console_requiredThe action is done in the Shorebird console. error.hint holds the link. Exit code 69.

When there’s a way to recover, error.hint says what to do, such as the flag to add.

When the Shorebird API rejected the request, error.api_code holds the server’s error code, such as shorebird_plan_insufficient_tier for a feature your plan doesn’t include, and error.details holds any extra detail the server sent. Branch on api_code rather than on the message text. These fields need Shorebird 1.6.129 or newer.

When the account that pays for an app needs attention, for example because patch delivery is paused or nearing the plan’s install limit, or a payment failed, Shorebird 1.6.135 and newer print a notice from the Shorebird server as the last output of shorebird release and shorebird patch. The notice doesn’t change the exit code. On GitHub Actions it’s a warning annotation, so it shows on the run’s summary page without opening the log. With --json, it goes to stderr, and stdout still holds only the JSON object.

Commands that delete or remove something in the CLI, such as shorebird releases delete, shorebird channels delete, shorebird members remove and shorebird service-accounts delete, ask for confirmation in a terminal. Without a terminal, in CI, or with --json, they never prompt, so confirm with --yes or the command fails with a usage error and nothing is deleted:

shorebird releases delete --app-id <app-id> --release-version 1.0.0+1 --yes --json
shorebird channels delete --app-id <app-id> --name qa --yes --json

releases delete and --yes for channels delete need Shorebird 1.6.129 or newer; earlier releases confirmed channels delete with --confirm-name. members remove and the service-accounts commands need Shorebird 1.6.135 or newer.

Some commands, such as shorebird apps delete, shorebird account delete and shorebird service-accounts keys create, don’t perform the action: a person finishes it in the Shorebird console. In CI or with --json, they change nothing and exit with code 69 and the console_required error, whose hint holds the console link. See Commands that finish in the console for the full list. From 1.6.121 to 1.6.134, shorebird apps delete --confirm-name "<display name>" still deletes the app.

shorebird patches rollback and shorebird patches rollforward succeed when the patch is already in the requested state, so a retried job doesn’t fail. Pass --require-change to make the command exit with a usage error when nothing changed, if your script needs to know. With --json, the data.changed field reports the same thing:

shorebird patches rollback --release-version 1.0.0+1 --patch-number 2 --json

See Roll back from the CLI for the command’s options, and What happens when a patch is rolled back? for what it does on devices.