Skip to content

PUBLISH A BUILD FROM YOUR TERMINAL OR CI IN ONE COMMAND

Ship iOS & Android Buildsfrom Your Terminal with the BetaDrop CLI

One command uploads a signed .ipa or .apk and prints an install link testers open on their phone.The same command runs on your Mac and inside GitHub Actions, fastlane, GitLab CI, Bitrise, or Jenkins.

Terminal

BetaDrop CLI demo transcript:

npm install -g @betadrop/cli

bd login — log in via browser or API token

bd publish ./MyApp.ipa

Success! Build is live. Public link: https://betadrop.app/install/?i=abc123 — scan the QR code to install on your device.

Get started in 3 steps

From install to your first published build in under 2 minutes.

1

Install the CLI

Choose your preferred installation method. The recommended installer scripts automatically verify requirements.

curl -fsSL https://betadrop.app/install.sh | bash

System Requirements

  • Node.js: Node 24 (recommended) or Node 18+. The installer scripts check that npm is available.
  • OS: macOS, Linux, or Windows (via PowerShell installer script or WSL2).
  • CI runners: any runner with Node on it. The npm install is the portable one, so that is the line the pipeline examples below use.
  • Build from Source: pnpm is only required if building the CLI from source.
2

Log in to BetaDrop

Authenticate via your browser, or use an API token. On a CI runner you skip login entirely: set BETADROP_TOKEN in the environment and publish reads it.

betadrop login
3

Publish your build

Point betadrop publish at your IPA or APK. An install link and QR code are printed instantly.

betadrop publish ./MyApp.ipa

Commands

All commands below are shown using the betadrop command, but the short alias bd can be used interchangeably.

betadrop login
Authenticate interactively (browser or API token). Already logged in? You'll be asked what to do.
betadrop login --token bd_live_…
Non-interactive token login.
betadrop publish <file>
Upload an .ipa or .apk and print its install link + QR code.
betadrop publish <file> --ci
CI mode: prints only the install URL
betadrop publish <file> --json
Output result as JSON
betadrop whoami
Show the authenticated user, active token name, expiry, and last-used date. --json for machine output.
betadrop logout
Revoke the stored token server-side and remove local credentials.

Flags for publish

publish takes a path to a signed build and four flags. Everything else is read out of the binary itself: app name, version, bundle identifier, and whether it is an iOS or an Android build. There is nothing to configure and no project file to keep in sync.

--ci
Non-interactive output: no progress bar, no QR block, no summary. Standard output becomes exactly one line, the install URL, so a shell can capture it with url="$(betadrop publish app.apk --ci)".
--json
Prints the build id, short id, and install URL as JSON. Use it when a script needs more than the link, for example to store the build id in a release note.
--name <name>
Overrides the build name testers see in the install page. In CI this is where a run number or branch belongs, so two builds from the same day are told apart.
--notes <notes>
Attaches release notes to the build: what changed, or what you want tested. Testers read them on the install page before they tap install.

Files are checked before they are uploaded: the CLI reads the first four bytes and rejects anything that is not really a ZIP archive, so a renamed file fails in about a second instead of after the whole transfer. Only .ipa and .apk are accepted. BetaDrop does not distribute .aab and never re-signs a build. Uploads obey the same limits as the web uploader: up to 500 MB per build on Free and Pro, 1 GB on Studio, links live 3 days by default, and a free account can extend a link to 7 days.

Use BetaDrop in CI

The CLI is the whole CI integration. Even the packaged GitHub Action is a thin wrapper around it. There is no agent to install and no plugin to maintain: a runner installs the npm package and runs one command. In CI, betadrop publish reads its token from the BETADROP_TOKEN environment variable, so nothing interactive ever runs and betadrop login never has to execute on a build machine. Add the step after your build step, once the artifact actually exists on disk.

GitHub Actions

The packaged Action, on the Marketplace as BetaDrop Upload, wraps the CLI in one step: it installs the package, uploads the build, and exposes the link as an install-url output later steps can post to a pull request comment, a Slack channel, or the job summary.

.github/workflows/android-beta.yml
- name: Publish to BetaDrop
  id: betadrop
  uses: betadrop-app/upload-action@v1
  with:
    file: app/build/outputs/apk/release/*.apk
    token: ${{ secrets.BETADROP_TOKEN }}

Pin @v1, never @main. Calling the CLI directly in a run: step publishes identically. The generic snippet below is that version, and it keeps working if you ever move off GitHub. Full Android and iOS workflows, including certificate import on macOS runners →

fastlane

If building and signing already live in your Fastfile, distribution is one more line in the lane. sh() returns standard output, and with --ci standard output is exactly the install URL. The lane that produced the binary captures the link and can hand it to Slack. The same shape covers gym on iOS and gradle on Android. Complete beta lanes for both platforms →

GitLab CI, Bitrise, Jenkins, anything else

Nothing above is GitHub-specific. Any runner that can run Node 18 or newer runs the same two lines, in whatever a script step is called on your system.

npm install -g @betadrop/cli
betadrop publish ./build/MyApp.ipa --ci --name "MyApp build $BUILD_NUMBER"

Store the token in the secret store each system already has (a masked CI/CD variable on GitLab, a secret env var on Bitrise, a credentials binding on Jenkins), and expose it to the step as BETADROP_TOKEN so it stays out of the repository and out of the log.

Three of those now have their own guide, with the real artifact paths and secret handling for each: GitLab CI, Bitrise and Codemagic. See every guide on the integrations hub, including Flutter, React Native, Expo, and Unity build paths.

What to do with the link the step prints

A URL sitting in a build log helps nobody. Once the publish step has captured it, spend it somewhere a tester will actually look: a comment on the pull request that produced the build, a message in the channel where QA lives, the run summary your team already opens, or a row in the release checklist. Testers need nothing installed. They open the link on the phone, or scan the QR code from the install page, and the build installs from the browser.

Every publish creates a new build with its own link, so the link you sent yesterday keeps working and nothing is silently overwritten. That is worth knowing before you wire a pipeline that runs on every push: use --name with a run number or branch so the list stays readable, and remember one runner constraint the CLI cannot fix — an iOS archive has to be built and signed on macOS, so the iOS job needs a macOS runner even though the publish step itself runs anywhere.

Tokens, and where to keep them

A CLI token is an account credential. It uploads builds as you, so treat it the way you treat a deploy key.

Create tokens at betadrop.app under Settings → Developer → API tokens. They begin with bd_live_ and are shown once.

  • Store it as a secret, never inline. A token pasted into a workflow file, a Fastfile, or a Dockerfile is readable by everyone with repository access and survives in git history after you delete it. Put it in the secret store your CI already has and reference it from the step's environment.
  • One token per pipeline. Give the Android job, the iOS job, and your own laptop separate tokens. When a runner is retired or a contractor rotates off, you revoke one token instead of breaking every place that publishes.
  • Let the environment win. When BETADROP_TOKEN is set, the CLI uses it and never reads or writes the local credentials file. That is what makes a shared runner safe to publish from: nothing is left on the machine when the job ends.
  • Locally, login handles it. betadrop login writes the token to your home directory with owner-only permissions. betadrop whoami tells you which token is active, its name, expiry, and when it was last used — the quickest way to spot a token nobody has used in months.
  • Log out revokes. betadrop logout revokes the stored token server-side, not just on the machine you ran it on. A token you supplied through the environment is revoked from the dashboard instead. Keep a token out of echo and set -x output and it never reaches your build log at all.

When the web uploader is simpler

If you cut a tester build by hand every week or two, the CLI is more setup than the job needs. Installing a global npm package and minting a token takes longer than the upload itself. Drag the file onto upload an IPA or upload an APK and you have the same link and QR code — guest upload works with no account at all.

The same applies on a machine that is not yours, or when you are sharing a build someone else produced and never touch the build system. The CLI earns its place when publishing repeats: every push, every lane run, every nightly, every time a build should reach testers without a human remembering to send it.

And if your work already happens in an editor conversation, the BetaDrop MCP server does the same job without a terminal: Claude Code, Cursor, or Copilot uploads the build and hands back the install link. One thing no tool changes: Apple's signing rules still apply. An ad-hoc iOS build installs only on devices whose UDID is in the provisioning profile, and enterprise builds still need the one-time trust step on the device.

Frequently asked questions

How do I use the BetaDrop CLI in GitHub Actions?

Add one step to the job that already builds your app, after the artifact exists. The step runs `npm install -g @betadrop/cli`, then `betadrop publish <path-to-artifact> --ci`, with BETADROP_TOKEN passed in from `secrets`. Because --ci prints only the install URL, you can capture it into $GITHUB_OUTPUT and reuse it in a PR comment, a Slack message, or the job summary. Prefer a single step? The packaged Action — `uses: betadrop-app/upload-action@v1`, on the Marketplace as BetaDrop Upload — wraps this exact CLI call and sets the install-url output for you.

Where does the CLI get its token in CI?

In CI the BetaDrop CLI reads its token from the BETADROP_TOKEN environment variable. When that variable is set, the CLI uses it and never reads or writes the local credentials file, so `betadrop login` never has to run on a runner. Generate the token at betadrop.app under Settings → Developer → API tokens, store it in your CI system's secret store, and expose it to the publish step as an environment variable — never paste it into the workflow file itself.

Does the CLI work with fastlane, GitLab CI, Bitrise, and Jenkins?

Yes. Nothing about the CLI is GitHub-specific: any runner that can run Node 18 or newer can install the package and run `betadrop publish`. In fastlane, wrap it in sh() inside a lane and capture the returned URL. In GitLab CI, Bitrise, and Jenkins, it is the same two lines in a script step, with BETADROP_TOKEN supplied by that system's secret mechanism (masked CI/CD variable, secret env var, and credentials binding respectively).

What does the --ci flag actually change?

It switches publish into non-interactive output: no progress bar, no QR block, no summary. Standard output becomes exactly one line, the install URL, so `url="$(betadrop publish app.apk --ci)"` captures a usable link with no parsing. If a script needs more than the link, use --json instead, which prints the build id, short id, and install URL as JSON.

Can the CLI upload an AAB, or sign a build for me?

The CLI can neither upload an AAB nor sign a build for you. `betadrop publish` accepts .ipa and .apk only, and it checks the file's magic bytes before uploading, so a renamed or wrong-format file fails in about a second instead of after the whole upload. BetaDrop also never re-signs anything: an iOS build must already be signed, and it installs only on devices covered by its provisioning profile. Build and sign in Xcode, fastlane, or your CI, then publish the result.

Ready to ship faster?

Install the CLI, log in once, and publish your first build in under 2 minutes.

$npm install -g @betadrop/cli
Go to dashboard