GitHub Actions

Generate an API token from the dashboard's API Tokens page. Add it as a GitHub repository secret named BUILDTREE_TOKEN. Add a repository variable BUILDTREE_PROJECT_SLUG for the project slug.

The examples below use a shell conditional so pushes to develop or main upload as the env checkpoint (no --branch), and PR / feature-branch pushes upload as branched builds. This matches the pre-merge QA workflow.

Android

# .github/workflows/buildtree-android.yml
name: buildtree (android)

on:
  pull_request:
  push:
    branches: [main, develop]

jobs:
  android:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-java@v4
        with:
          distribution: temurin
          java-version: "17"

      - uses: actions/setup-node@v4
        with:
          node-version: "22"

      - run: npm install -g @buildtree/cli

      - working-directory: android
        run: ./gradlew assembleRelease
        env:
          STORE_PASSWORD: ${{ secrets.ANDROID_STORE_PASSWORD }}
          KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}

      - env:
          BUILDTREE_TOKEN: ${{ secrets.BUILDTREE_TOKEN }}
          PROJECT: ${{ vars.BUILDTREE_PROJECT_SLUG }}
        run: |
          BRANCH="${GITHUB_HEAD_REF:-${GITHUB_REF_NAME}}"
          APK="android/app/build/outputs/apk/release/app-release.apk"
          if [ "$BRANCH" = "develop" ] || [ "$BRANCH" = "main" ]; then
            buildtree upload "$APK" --project "$PROJECT" --env dev
          else
            buildtree upload "$APK" --project "$PROJECT" --env dev --branch "$BRANCH"
          fi

GITHUB_HEAD_REF is the source branch on PR events; on push events it is empty and the script falls back to GITHUB_REF_NAME, the branch that was pushed.

iOS

iOS requires macOS runners and Apple Developer credentials. The example assumes a Fastlane lane named build_adhoc that produces an IPA at ios/build/MyApp.ipa. Adjust the lane name and output path to match your project.

# .github/workflows/buildtree-ios.yml
name: buildtree (ios)

on:
  pull_request:
  push:
    branches: [main, develop]

jobs:
  ios:
    runs-on: macos-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: "22"

      - uses: ruby/setup-ruby@v1
        with:
          ruby-version: "3.2"
          bundler-cache: true

      - env:
          MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
          APP_STORE_CONNECT_KEY: ${{ secrets.APP_STORE_CONNECT_KEY }}
        run: bundle exec fastlane match adhoc --readonly

      - run: npm install -g @buildtree/cli

      - run: bundle exec fastlane build_adhoc

      - env:
          BUILDTREE_TOKEN: ${{ secrets.BUILDTREE_TOKEN }}
          PROJECT: ${{ vars.BUILDTREE_PROJECT_SLUG }}
        run: |
          BRANCH="${GITHUB_HEAD_REF:-${GITHUB_REF_NAME}}"
          IPA="ios/build/MyApp.ipa"
          if [ "$BRANCH" = "develop" ] || [ "$BRANCH" = "main" ]; then
            buildtree upload "$IPA" --project "$PROJECT" --env dev
          else
            buildtree upload "$IPA" --project "$PROJECT" --env dev --branch "$BRANCH"
          fi

Comment the install URL on the PR

This pattern uploads the build, captures the pinned install URL from the CLI output, and posts it back on the PR as a comment. Setting NO_COLOR=1 on the env guarantees the CLI emits plain text that the awk pipe can parse reliably across runners.

- id: upload
  env:
    BUILDTREE_TOKEN: ${{ secrets.BUILDTREE_TOKEN }}
    PROJECT: ${{ vars.BUILDTREE_PROJECT_SLUG }}
    NO_COLOR: "1"
  run: |
    OUTPUT=$(buildtree upload \
      android/app/build/outputs/apk/release/app-release.apk \
      --project "$PROJECT" \
      --env dev \
      --branch "${{ github.head_ref }}")
    echo "$OUTPUT"
    PINNED_URL=$(echo "$OUTPUT" | grep "Pinned URL" | awk '{print $3}')
    echo "pinned=$PINNED_URL" >> $GITHUB_OUTPUT

- if: github.event_name == 'pull_request'
  uses: actions/github-script@v7
  with:
    script: |
      github.rest.issues.createComment({
        issue_number: context.issue.number,
        owner: context.repo.owner,
        repo: context.repo.repo,
        body: `Android build ready.\n\nInstall: ${{ steps.upload.outputs.pinned }}`
      })

The comment step only runs on pull_request events, so github.head_ref is guaranteed to be set in the upload step above (no conditional needed). If you want this same workflow to also run on push events to develop or main, switch the upload to the shell-conditional pattern shown in the Android example.

Tag a release on tag push

A dedicated workflow that fires when a v* tag is pushed. The release URL /install/release/<project>/v1.2.0 stays frozen at this build forever.

# .github/workflows/buildtree-release.yml
name: buildtree (release)

on:
  push:
    tags: ["v*"]

jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      # build steps as in the Android workflow above

      - env:
          BUILDTREE_TOKEN: ${{ secrets.BUILDTREE_TOKEN }}
          PROJECT: ${{ vars.BUILDTREE_PROJECT_SLUG }}
        run: |
          buildtree upload \
            android/app/build/outputs/apk/release/app-release.apk \
            --project "$PROJECT" \
            --env prod \
            --release "${GITHUB_REF_NAME}"

GITHUB_REF_NAME on a tag push is the tag name itself (for example, v1.2.0).

GitHub Actions | buildtree docs