GitHub Action reference
betadrop-app/upload-action publishes a build from a workflow and returns its install link. It is a composite action: it installs @betadrop/cli and runs betadrop publish --ci, so CI behaves exactly like a laptop and there is one upload client to keep true. For complete Gradle and Xcode workflows, read the GitHub Actions guide instead. This page is the input table.The minimum step
- name: Publish to BetaDrop
id: betadrop
uses: betadrop-app/upload-action@v1
with:
file: app/build/outputs/apk/release/*.apk
token: ${{ secrets.BETADROP_TOKEN }}Two required inputs and nothing else. Create the token under Settings → Developer → API tokens in the BetaDrop dashboard, then store it as a repository or organisation secret. Never inline in the workflow file, where it is readable by anyone who can read the repo and is captured in every fork.
Pin the tag. @v1 tracks backwards-compatible releases of the major version; pin a full version such as @v1.2.0 to freeze it entirely. A branch reference is not a supported way to consume this Action.
Inputs
| Input | Required | Description |
|---|---|---|
| file | Yes | Path to the build artifact (.ipa or .apk). A glob such as app/build/outputs/apk/release/*.apk is allowed, but it must match exactly one file. |
| token | Yes | A BetaDrop API token (bd_live_…), passed from a secret: ${{ secrets.BETADROP_TOKEN }}. |
| name | No | Override the build name shown on the install page. Defaults to the app's own name, read out of the archive. |
| notes | No | Release notes for this build. The commit message is the usual choice. Default: empty. |
| standing-link | No | A standing link slug to point at this build once it is live, so that fixed URL keeps serving whatever CI published last. Claim the standing link first: a free account holds one, Pro and Studio up to 50. A standing link that cannot be used fails the step before anything is uploaded. Needs @betadrop/cli 0.3.0 or newer, which the step checks and names. The older name for this input, channel, stays accepted indefinitely. |
| expires-in-days | No | Stop the install link working after this many days, clamped to the plan's maximum. Needs @betadrop/cli 0.4.0 or newer; the step names the installed version if it is older. |
| max-downloads | No | Stop the link after this many downloads. CLI 0.4.0+. |
| max-devices | No | Stop the link after this many different devices. With more than one limit set, the link stops at whichever comes first. CLI 0.4.0+. |
| inspect | No | Check the build will install from a link before uploading it — profile type and expiry, bundle ID, signing, testOnly — and fail the step with nothing uploaded if it will not. Default: false. CLI 0.4.0+. |
| comment-on-pr | No | Post the install link as a comment on the pull request, editing that same comment on every later push instead of adding another. Requires pull-requests: write on the job. Skipped with a notice on events that have no pull request, so a workflow that runs on both push and pull_request needs no condition. Default: false. |
| github-token | No | The token used for that comment. Defaults to the workflow's own ${{ github.token }}; override it only if the comment should come from a different account. |
| cli-version | No | Version of @betadrop/cli the Action installs. Defaults to latest; pin it for reproducible pipelines. |
That is the complete list. There is no input for platform or visibility: platform is inferred from the file. Expiry can be set here, with the three limits above, or changed on the build in the dashboard afterwards.
Outputs
| Output | Description |
|---|---|
| install-url | The OTA install link for the uploaded build. Read it as ${{ steps.<id>.outputs.install-url }}, which requires giving the step an id. |
| standing-link-url | The standing link's URL, when standing-link was set. Empty otherwise. This is the one to hand out, or to put behind a download button in a README: it still points at the newest build next week, which the per-build link above will not. channel-url is the older name for the same value and still works, so a workflow already reading it needs no edit. |
A link nobody sees is a wasted upload. The Action already writes it to the run summary on every green build, and comment-on-pr puts it on the pull request without a second step. Use the output when you want it somewhere else — chat, a release note, a downstream job. The integrations guide has those as copy-ready steps.
A complete job
name: Beta
on:
push:
branches: [main]
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# …your existing build steps produce the .apk or .ipa here…
- name: Publish to BetaDrop
id: betadrop
uses: betadrop-app/upload-action@v1
with:
file: app/build/outputs/apk/release/*.apk
token: ${{ secrets.BETADROP_TOKEN }}
name: MyApp ${{ github.ref_name }}
notes: ${{ github.event.head_commit.message }}
- name: Publish the link to the run summary
run: echo "${{ steps.betadrop.outputs.install-url }}" >> "$GITHUB_STEP_SUMMARY"The build steps are deliberately elided: whatever already produces your .apk or .ipa stays exactly as it is, and this Action is appended to it. If you do not have those steps yet, the guide carries full Gradle and Xcode jobs, including the export-options detail that trips up iOS runs.
When it fails
| Symptom | Cause and fix |
|---|---|
| "No file matches …" | The path or glob matched nothing on the runner. Check that the build step runs before this one and writes where you think — a step that lists the output directory is the fastest way to find out. |
| "… matches N files" | The glob matched more than one build. This is a deliberate failure rather than an arbitrary pick: silently uploading whichever file sorted first is worse than stopping. Narrow the pattern so exactly one build matches. |
| "Publish produced no output" | The upload did not complete. The CLI's own error is above this line in the log. An invalid or revoked token and a file over the size limit are the two common causes. |
| "… has no --channel flag" | standing-link (or its channel alias) was set, but the @betadrop/cli the step installed predates 0.3.0, where that flag landed. The message names the version it got and the version it needs. Set cli-version to 0.3.0 or newer, or 0.4.0 or newer if the step also uses the expiry limits or inspect. |
| The pull-request comment never appears | Deliberately a warning rather than a failure — the build published, and losing it to a missing permission would be the worse trade. The usual cause is the job lacking pull-requests: write; the other is an event with no pull request, reported as a notice. Runs from forked pull requests get a read-only token and cannot comment at all. |
| Fails only on pull requests from forks | Not the Action. GitHub does not expose repository secrets to workflows triggered by a pull_request event from a fork, so the token arrives empty. Publish on push to your own branches, or use an event that runs in the base repository's context. |
Notes on how it behaves
- Node is assumed, not installed. Every GitHub-hosted runner image ships Node, so the Action needs no setup step. On a self-hosted runner without Node 18 or newer, add one — the Action installs the CLI with
npm install -g. - Inputs are passed through the environment. They are not interpolated into the shell script, so a filename or a commit message containing shell metacharacters cannot inject a command. This is the standard composite-action footgun and it is worth knowing it has been avoided.
- Permissions, only if you comment. Publishing needs no
permissionsblock at all — the upload authenticates with your BetaDrop token and never touchesGITHUB_TOKEN. Settingcomment-on-pris the one exception: that step writes to the pull request, so the job needspull-requests: write. If it is missing the step warns and the run stays green, because a published build should not be lost to a permission. - It is the CLI. Anything documented on the CLI page about extensions, the ZIP magic-byte check, size limits or error behaviour applies here unchanged, because it is literally the same program running.
- The repository. Source and releases live at github.com/betadrop-app/upload-action. Note the namespace:
betadrop-app, notbetadrop, which is an unrelated account. Issues and pull requests are read there; a star is how the next team looking for this finds it rather than writing the step themselves.