Flitzdocs
CLI

flitz publish

Build a bundle from your project against the pinned SDK and publish it; flags, apps and tracks, publish history, output streams, --json, and the on-demand SDK download.

publish is the build-and-ship command. With a pinned SDK, the whole happy path is:

cd /path/to/your/flutter/app
flitz publish --name "Onboarding redesign"
✓ Compiling 12.3s
✓ Packaging 4.1s
✓ Uploading 4.3s
Published acme_app on track feature/login in 20.9s

  Landing page  https://…
  Deeplink      flitz://download?url=…
  Bundle        https://….flitz
  Track         https://…/acme_app/feature-login

followed by a QR code that encodes the deeplink. --name is optional; it labels the publish in your publish history. Track is the track's link: it always opens the newest complete build on that track, so you can share it once — see Links.

Open the deeplink on a device that has your host installed and the bundle downloads and runs. The easiest way to get it onto a teammate's device is the landing-page link: it opens in any browser and shows the app, this build's title, track, versions, and comment, a button and a QR code that open the deeplink in your host, and a second button that opens the newest build on the track. If the app is set to Members only, the page says the build opens only in your organization's own app, for a signed-in member.

A bundle is a complete build of your app's Dart code — never a patch — and it runs only inside a host built against the same SDK tag. See How it works for the host/bundle split.

What happens

publish:

  1. Reads the pin from flitz.yaml in the current directory and uses the cached SDK for that tag, fetching it first if you allow (below).
  2. Reads the app's name and version from pubspec.yaml in the project folder, and resolves the app key, the track, and the scheme (below).
  3. Registers the publish with the Flitz service. This is the licensed step: an unauthenticated or unlicensed publish fails here with exit 3, before anything is compiled. When the SDK had to be downloaded first, a credential or license problem already stops the download, with the same exit code.
  4. Compiling — compiles your app to a bundle with the pinned SDK. A bundle does not depend on the target platform: the same .flitz runs on Android and iOS hosts, and building it needs no Android SDK, NDK, or Xcode.
  5. Packaging and Uploading — packs the bundle into a temporary file and uploads it in one request to your organization's bundle storage — Flitz's own, or a bucket you own — then deletes the file, whether or not the upload succeeded.
  6. Marks the publish complete, which makes its links work. The landing page needs no upload: it is built from the publish record when someone opens it.

Share within your team

Unless an Owner or Admin sets the app to Members only (Enterprise), anyone who holds a link can download the bundle, and a bundle runs with the full privileges of the host. Links do not expire: they keep working after your organization's license lapses, after the person who published leaves the organization, and after the API key rotates. To take a build down, delete the publish — flitz publishes delete or the portal; deleting the app takes down all of its links, and can be undone for 30 days. Flitz is for your own team's work-in-progress builds, reviewed by collaborators during development; it is not a way to get finished apps to people outside your team.

App, track, and scheme

Every publish belongs to an app of your organization, named by its key, and to a track of that app — usually your Git branch. See Apps and tracks for what they are and what each link opens. publish takes each value from the first place that sets it:

ValueTaken from, in order
App key--app, then app: in flitz.yaml, then the name: in pubspec.yaml
Track--track, then track: in flitz.yaml, then the project's current Git branch
Scheme--schema, then schema: in flitz.yaml, then flitz

Commit the values every checkout should share next to the SDK pin — see Configuration → flitz.yaml. flitz.yaml is read from the directory you run flitz in, also with --dir.

  • New apps. The first publish under the pubspec name creates its app without asking. A key you name yourself — with --app or app: — must already exist; otherwise publish asks App <key> does not exist in your organization. Create it? [y/N]. Declining publishes nothing. In CI, pass --yes to create it.
  • Tracks. A track is created by its first publish. Its key is the branch name with every character other than letters, digits, _, and - replaced by -, so feature/login publishes to the track feature-login. With no branch — a detached checkout, as in most CI jobs, or a folder outside Git — the publish lands on the app's Unassigned track; pass --track instead.
  • Archived and deleted apps refuse new publishes (app <key> is archived, app <key> is deleted). See flitz apps.

Flags

publish accepts the global options — --json, --yes, -v, --no-color — plus these:

FlagDefaultPurpose
--dir <path>.The Flutter project folder; must contain pubspec.yaml. flitz.yaml — the pin and the app:, track:, and schema: keys — is still read from the current directory.
--app <key>flitz.yaml app:, then the pubspec name:The app this publish belongs to: 1–100 letters, digits, _, or -. A key your organization does not have yet is created only after you confirm.
--track <name>flitz.yaml track:, then the current Git branchThe track this publish lands on, at most 255 characters.
--target <file>lib/main.dartDart entry point, relative to --dir. It must live under lib/; any other path publishes a bundle that fails when it loads.
--dart-define <KEY=VALUE>, -D—A compile-time constant, readable via String.fromEnvironment and friends. Repeatable; commas inside a value are not split. When a build fails, the build command the CLI echoes keeps each key and replaces its value with <redacted>, because defines routinely carry API keys.
--dart-define-from-file <path>—A .json or .env file of compile-time constants. Repeatable. Resolved relative to the directory you ran flitz from.
--name <text>—A display title for the publish, shown in the publish history and as the landing page's title. At most 120 characters.
--version-name <text>—A version label shown next to the pubspec.yaml version in the history and on the landing page. At most 64 characters.
--comment <text>—Free text stored with the publish, shown on the landing page, in the portal, and in flitz publishes --json. Anyone who opens the link can read it. At most 1000 characters.
--schema <scheme>flitz.yaml schema:, then flitzThe URL scheme of the deeplink, the QR codes, and the landing-page buttons. Lowercase, starts with a letter, then letters, digits, +, -, or . — at most 32 characters. Must match the scheme your host registers; see URL scheme. Affects only the links, never the bundle.
--qr-output <path>—Also write the deeplink QR as a standalone PNG. The parent directory must exist; an existing file is overwritten.
--no-qr—Suppress the inline terminal QR. The result block is still printed.

There is no --output flag: the .flitz exists only as a temporary file while publish uploads it.

An empty --name, --version-name, or --comment is the same as leaving it out. A value over its limit is refused before anything is built, naming the field — for example body/name must NOT have more than 120 characters (exit 1); shorten it and re-run.

--dart-define values become constants in the compiled bytecode; they do not change the deeplink or the landing page. Reserved framework-managed defines cannot be set this way.

Write -D with a space

The CLI looks for -v in every short option, including an attached -DKEY=VALUE. A value with a lowercase v in it — -DENV=dev, say — turns on stack traces and retry messages, and two vs (-DAPI=https://dev.vendor.io) fail with There is one verbosity level; pass -v (or --verbose) at most once. Write -D KEY=VALUE with a space, or --dart-define=KEY=VALUE, and the value is read as a value.

With a custom scheme, the deeplink, both QR codes, and the landing-page buttons use it. A track's page uses the scheme of the track's newest publish. A host that registers its own scheme should set schema: in the committed flitz.yaml, so every publish uses it.

Publish history

Every publish is recorded in your organization's history: its app and track, the app name and version from pubspec.yaml, the optional --name, --version-name, and --comment, who published it (a person, or the organization API key), and a state:

StateMeaning
completeThe bundle finished uploading, and publish marked the publish complete. Its links work.
pendingNot marked complete yet: the publish is in flight, or it failed or was interrupted before publish marked it complete.
abandonedStill pending an hour after it started. The bundle never landed.

A publish stays in the history until someone deletes it, or deletes its app. List, filter, and delete them with flitz publishes, or on the Publishes page of the portal.

Output and --json

Progress and result use different streams. The timed phases and the Published … in … line go to stderr; the labelled result block and the QR go to stdout. So

flitz publish > urls.txt

captures the result block and nothing else, while the terminal still shows the animated progress. In plain output (CI, --no-color, NO_COLOR) the phases appear as timestamped lines and there is no QR.

The inline QR appears only when both stdin and stdout are terminals and output is rich. Use --qr-output <file.png> to get one in every case.

The labelled block carries no line-format promise. Scripts read the result with --json, which replaces the block with one document on stdout and suppresses the inline QR:

flitz publish --json > result.json
{ "id": "…", "page_url": "https://…", "deeplink": "flitz://download?url=…", "bundle_url": "https://…", "app": { "key": "acme_app", "name": "acme_app" }, "track": { "key": "feature-login", "name": "feature/login" }, "track_url": "https://…/acme_app/feature-login" }

id identifies the publish in flitz publishes --json.

page=$(jq -r .page_url result.json)
deeplink=$(jq -r .deeplink result.json)
bundle=$(jq -r .bundle_url result.json)
track=$(jq -r .track_url result.json)

On failure publish --json emits no JSON, with one exception: the document is written before --qr-output runs, so a PNG that cannot be written exits 2 with the complete document already on stdout. Check the exit status first — see Exit codes.

On-demand SDK download and --yes

publish works on a fresh checkout that contains only flitz.yaml. When the pinned SDK is not in the cache, it offers to fetch it:

The pinned SDK <tag> is not installed. Download it now (about <size>)? [Y/n]

Accepting runs the download in the same invocation — without re-pinning — and then continues the build. Declining aborts with the pinned SDK <tag> is not installed and the flitz sdk use <tag> hint (exit 1).

Non-interactively — a CI job, a redirected terminal — publish does not start a large unattended download. Unless you pass --yes, it fails immediately (exit 1) with

The pinned SDK <tag> is not installed. Download it now (about <size>)? — and there is no terminal to ask on

and the hints flitz sdk use <tag> or --yes:

flitz publish --yes        # fresh checkout, no prompt, one command

--yes answers every prompt affirmatively — in publish that includes creating the app when --app or app: names a key your organization does not have yet; assume_yes: true in config.yaml does the same permanently. This offer exists only in publish. No other command downloads an SDK implicitly.

A one-command CI job, on a checkout that carries only flitz.yaml:

export FLITZ_APIKEY="$FLITZ_APIKEY_SECRET"   # from your CI secret store
flitz publish --yes --json --track "$BRANCH_NAME"   # your CI's branch variable

CI checks out a detached commit, which has no branch, so pass the branch with --track; without it the publish lands on the app's Unassigned track.

See flitz apikey.

When it fails

Most failures follow the message convention: one sentence, then the next command. The exit code says which class of thing went wrong.

You seeCodeMeaning
no SDK is pinned for this project: …1No flitz.yaml with an sdk: key in the current directory. Run flitz sdk use latest here — the pin is not read from --dir.
The pinned SDK <tag> is not installed. Download it now …? — and there is no terminal to ask on1Non-interactive without --yes. Run flitz sdk use <tag> first, or pass --yes.
the pinned SDK <tag> is not installed1You declined the download.
the pinned SDK <tag> could not be installed1The download started but failed; its own error is printed above. Run flitz sdk use <tag>.
no pubspec.yaml at …1The current directory, or --dir, is not a Flutter project.
<path>/pubspec.yaml has no `name:` (or an unusable name:)1Give the app a name: in pubspec.yaml.
<dir> is an app directory, not an app key1--app takes a key. Pass the folder as --dir <dir>.
the app key '<key>' from <source> must be 1 to 100 letters, digits, '_', or '-'1Fix --app, app: in flitz.yaml, or the pubspec name:.
the track '<track>' from <source> is longer than 255 characters1Shorten --track or track:.
the scheme '<scheme>' from <source> is not a lowercase URL scheme …1A scheme is lowercase, starts with a letter, holds only letters, digits, +, -, and ., and is at most 32 characters.
<path>/flitz.yaml is not valid YAML (or is not a mapping, or a key is not a string)1Fix flitz.yaml, or delete it and run flitz sdk use <tag> again.
App <key> does not exist in your organization. Create it? — and there is no terminal to ask on1A key you named is new and nobody can confirm it. Check it for typos, or pass --yes to create the app.
the app <key> was not created1You declined the prompt. Nothing was published.
app <key> is archived1The app refuses new publishes. Run flitz apps unarchive <key>.
app <key> is deleted1Run flitz apps restore <key> within 30 days of the delete; after that the key is free again.
FlutterRootVerificationException: FLUTTER_ROOT (<path>) is not a valid Flitz SDK.1The cached SDK is damaged. Run flitz sdk install --force <tag>.
you are not logged in to Flitz / your Flitz session has ended / your organization's Flitz license is not active3Credential or license. Nothing was compiled. See Troubleshooting.
kernel compilation failed (exit N)5The app does not compile; the compiler's diagnostics are printed above. See Troubleshooting → Build failures.
bytecode compilation failed (exit N)5The app uses a platform API that bundles cannot use. See Troubleshooting → Build failures.
body/name must NOT have more than 120 characters (or versionName, comment)1A --name, --version-name, or --comment value is over its limit.
the bundle upload for <stem> failed: …4The upload did not complete. A message ending in no response within 30s means it ran out of time (below). Re-run flitz publish.
the bundle destination for <stem> is already occupied4This publish's upload destination already holds an object. Re-run; every publish gets a new destination.
could not write the QR PNG to <path>: …2Everything was published and printed; only the --qr-output file failed.

The upload has 30 seconds

The bundle upload must finish within 30 seconds, and no option extends that. On a slow connection a large bundle fails with the bundle upload for <stem> failed: … ending in no response within 30s (exit 4). Re-run from a faster network, or trim large assets from the app.

A publish that fails after step 3 stays in the history as pending, then abandoned, and never changes what its track link opens. Ctrl-C during a publish stops the build tool it started, deletes its scratch directory, and exits 2. Re-running publish always starts a fresh publish.

Re-run with -v to watch the compilers live instead of only seeing their output quoted on failure.

On this page