Releases Upload Setup Admin

Setup Guide

Android Browser App

Install the companion Android app to browse and install release APKs directly from your phone:

Download Browser APK

Open this page in your phone's browser, tap the button, then open the downloaded APK. You may need to allow "Install unknown apps" for your browser the first time.

1. Get an API Token

Ask your admin to create a token from the admin dashboard, or create one yourself if you have admin access.

Tokens are shown only once — copy it immediately after creation.

2. CLI Tool (curl | bash)

Install the dist-ant command:

curl -fsSL https://dist.ant.sh/install.sh | bash

Then configure your credentials:

export DIST_ANT_URL=https://dist.ant.sh
export DIST_ANT_TOKEN=your-token-here

Add the exports to your ~/.bashrc or ~/.zshrc to persist them.

Usage

# Upload an APK
dist-ant app-release.apk

# Upload with notes and custom expiration
dist-ant app-release.apk --notes "v1.2.0 beta" --expires 7d

# Upload an iOS IPA
dist-ant MyApp.ipa --notes "TestFlight alternative"

# Get JSON output (for CI parsing)
dist-ant app-release.apk --json

Options

-n, --notes "text"Release notes
-e, --expires "30d"Expiration: 7d, 14d, 30d, 90d, never
-s, --server URLServer URL (overrides DIST_ANT_URL)
-t, --token TOKENAPI token (overrides DIST_ANT_TOKEN)
-j, --jsonOutput raw JSON response
-c, --chunkedForce chunked upload (auto for >95MB)

3. Gradle Integration (Android CI)

Add this task to your app/build.gradle.kts:

tasks.register("uploadApk") {
    dependsOn("assembleRelease")
    group = "distribution"
    description = "Upload release APK to dist-ant"

    doLast {
        val apkDir = layout.buildDirectory
            .dir("outputs/apk/release").get().asFile
        val apk = apkDir.listFiles()
            ?.firstOrNull { it.extension == "apk" }
            ?: error("No APK found in ${apkDir.absolutePath}")

        val serverUrl = project.findProperty("distAntUrl") as? String
            ?: System.getenv("DIST_ANT_URL")
            ?: error("Set distAntUrl property or DIST_ANT_URL env var")

        val token = project.findProperty("distAntToken") as? String
            ?: System.getenv("DIST_ANT_TOKEN")
            ?: error("Set distAntToken property or DIST_ANT_TOKEN env var")

        val process = ProcessBuilder(
            "curl", "-s", "-X", "POST",
            "$serverUrl/api/upload",
            "-H", "X-Auth-Token: $token",
            "-F", "file=@${apk.absolutePath}",
            "-F", "notes=CI build ${project.version}"
        ).redirectErrorStream(true).start()

        val output = process.inputStream.bufferedReader().readText()
        val exitCode = process.waitFor()

        if (exitCode == 0) {
            println("Upload successful: $output")
        } else {
            error("Upload failed: $output")
        }
    }
}

Run it

# With environment variables
export DIST_ANT_URL=https://dist.ant.sh
export DIST_ANT_TOKEN=your-token-here
./gradlew uploadApk

# Or with Gradle properties
./gradlew uploadApk \
  -PdistAntUrl=https://dist.ant.sh \
  -PdistAntToken=your-token-here

GitHub Actions Example

- name: Build & Upload APK
  env:
    DIST_ANT_URL: https://dist.ant.sh
    DIST_ANT_TOKEN: ${{ secrets.DIST_ANT_TOKEN }}
  run: ./gradlew uploadApk

4. Plain curl

If you prefer not to install anything:

# Upload
curl -X POST https://dist.ant.sh/api/upload \
  -H "X-Auth-Token: your-token-here" \
  -F "file=@app-release.apk" \
  -F "notes=Manual upload" \
  -F "expires_in=30d"

# Simple upload (returns just the install URL)
curl -X POST https://dist.ant.sh/upload \
  -H "X-Auth-Token: your-token-here" \
  -F "file=@app-release.apk"

# Delete a release
curl -X DELETE https://dist.ant.sh/api/delete/RELEASE_ID \
  -H "X-Auth-Token: your-token-here"

# List releases
curl https://dist.ant.sh/api/releases

# Latest release for a package
curl https://dist.ant.sh/api/bundle/com.example.app/latest