Hashdrop
← All documentation

Uploading from npm scripts

One command that versions, builds and uploads — npm run drop:qa.

The Hashdrop CLI turns a release into one npm script: it decides the next version number, runs your build, and uploads the result with your git commit and release notes attached. Setup is a single interactive command.

Set up once

npx @hashdrop/cli init

init asks for a CI token (create one under CI tokens in the dashboard), detects whether the project is React Native, bare Android, iOS or Expo, proposes the right artifact path, and checks everything against the server before it saves a thing. It then writes the config and three scripts into your package.json:

{
  "scripts": {
    "drop:dev": "hashdrop --channel dev",
    "drop:qa": "hashdrop --channel qa",
    "drop:prelive": "hashdrop --channel prelive",
    "drop:live": "hashdrop --channel live"
  },
  "hashdrop": {
    "app": "your-app-slug",
    "build": "cd android && ./gradlew assembleRelease",
    "artifact": "android/app/build/outputs/apk/release/*.apk",
    "bump": "both"
  }
}
The token is never written into package.json or any committed file. It lives in ~/.config/hashdrop/token on that machine, or in the HASHDROP_TOKEN environment variable in CI.

Ship a build

npm run drop:qa -- --notes "Invoice rounding fix"

That one command: asks Hashdrop for the latest accepted version, writes the next one into your project, runs the build so the number is baked into the binary, uploads it, and prints the install link with a QR code in the terminal.

How versioning decides

The next number is always the platform's latest plus one — never the local file. Local checkouts go stale the moment a teammate or a CI machine uploads; Hashdrop saw every upload, so it is the one place the sequence cannot lie. The version name keeps whatever format you use: 2.4 becomes 2.5, 1.2.9 becomes 1.2.10. Your local file is corrected as a side effect, so every checkout converges on the truth.

  • First upload: your project's current version is used as-is, and the platform owns the sequence from then on
  • Deliberate jumps go through --set-version 2.0 — the sequence continues from there
  • If your local file is ahead of the platform, the CLI warns and names the flag before continuing
  • Rapid QA iterations: --bump-code-only moves the build number and leaves the name alone
The CLI only ever edits plain literals — versionCode 28, versionName "1.2.8", MARKETING_VERSION, or Expo's app.json. If your version is computed in build logic, it refuses rather than guesses; have your build read HASHDROP_BUILD_NUMBER and HASHDROP_VERSION_NAME instead, which the CLI sets on every run.

In CI

Set HASHDROP_TOKEN as a secret. If your pipeline already built the artifact, skip the build and hold the pipeline until the malware scan clears:

- name: Publish to Hashdrop
  env:
    HASHDROP_TOKEN: ${{ secrets.HASHDROP_TOKEN }}
  run: npx @hashdrop/cli --channel qa --no-build --wait --artifact android/app/build/outputs/apk/release/app-release.apk

--wait polls the scan and exits non-zero if the build is infected or could not be checked, so a build that Hashdrop will not distribute also fails the pipeline. Note that --no-build skips the version bump too — the number is already baked into the file you are uploading.

Checking a setup

npx @hashdrop/cli doctor

doctor verifies the token, the app slug, the artifact path and the version files in about two seconds — so a broken setup surfaces before a twenty-minute build, not after.