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.

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 }}
        run: |
          buildtree upload \
            android/app/build/outputs/apk/release/app-release.apk \
            --project ${{ vars.BUILDTREE_PROJECT_SLUG }} \
            --env dev \
            --branch ${{ github.head_ref || github.ref_name }}

github.head_ref is the source branch on PR events and falls back to github.ref_name on push events.

iOS

iOS requires macOS runners and Apple Developer credentials. The example below uses Fastlane match.

# .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 }}
        run: |
          buildtree upload \
            ios/build/MyApp.ipa \
            --project ${{ vars.BUILDTREE_PROJECT_SLUG }} \
            --env dev \
            --branch ${{ github.head_ref || github.ref_name }}

Comment the install URL on the PR

- id: upload
  env:
    BUILDTREE_TOKEN: ${{ secrets.BUILDTREE_TOKEN }}
  run: |
    OUTPUT=$(buildtree upload \
      android/app/build/outputs/apk/release/app-release.apk \
      --project ${{ vars.BUILDTREE_PROJECT_SLUG }} \
      --env dev \
      --branch ${{ github.head_ref || github.ref_name }})
    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 }}`
      })

Tag a release on tag push

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

jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      # build steps as above
      - env:
          BUILDTREE_TOKEN: ${{ secrets.BUILDTREE_TOKEN }}
        run: |
          buildtree upload \
            android/app/build/outputs/apk/release/app-release.apk \
            --project ${{ vars.BUILDTREE_PROJECT_SLUG }} \
            --env prod \
            --release ${{ github.ref_name }}

The release-tag install URL /install/release/<project>/v1.2.0 stays frozen at this build.

GitHub Actions | buildtree docs