CI recipes

The pattern is always the same three steps: build the APK, push it to your device, publish the link. Each run mints a fresh session link, so step three fetches the link of the run you just started — the PR gets the link that belongs to its build, and the link dies when the session ends.

Setup (once)

  1. Add your API key to your CI secrets as PREVIEWAPK_API_KEY.
  2. Know your device id (run previewapk status locally, or read GET /portal/api/state).

GitHub Actions

name: Android preview
on:
  pull_request:
jobs:
  preview:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-java@v4
        with: { distribution: temurin, java-version: "17" }
      - name: Build debug APK
        run: ./gradlew assembleDebug
      - name: Push to PreviewAPK
        env:
          PREVIEWAPK_API_KEY: ${{ secrets.PREVIEWAPK_API_KEY }}
        run: |
          curl -sO https://previewapk.siteport.site/cli/previewapk.py
          python3 previewapk.py --device dev1 \
            push app/build/outputs/apk/debug/app-debug.apk --run --driver user
      - name: Comment the session link
        env:
          PREVIEWAPK_API_KEY: 
          GH_TOKEN: 
        run: |
          for i in $(seq 1 30); do
            LINK=$(python3 previewapk.py --device dev1 links --json | python3 -c "import sys,json;print(json.load(sys.stdin).get('session_url') or '')")
            [ -n "$LINK" ] && break
            sleep 5
          done
          gh pr comment ${{ github.event.pull_request.number }} --body "📱 **Live preview of this PR's build:** $LINK — view-only, live while the session runs."

Notes: --driver user boots the session for the human reviewer (view-only link + owner's portal driving). Omit it — or pass --driver agent — in workflows where a bot should drive (next recipe). The session link exists only while the session is live; previewapk links returns it as session_url.

Agent-in-CI (screenshot gate)

An agent job can verify the build before the link is posted: start agent-driven, walk the changed flow, and fail the job if a screenshot check fails.

python3 previewapk.py --device dev1 push app.apk --run --driver agent
python3 previewapk.py --device dev1 status --json   # poll until live
python3 previewapk.py --device dev1 shot pr-screen.png
# …your agent/assertion step reads pr-screen.png…
python3 previewapk.py --device dev1 handoff user    # leave it ready for the reviewer

Any other CI (GitLab, Bitrise, Jenkins…)

The CLI step is provider-agnostic — anywhere a shell runs, this runs:

export PREVIEWAPK_API_KEY=$YOUR_SECRET
curl -sO https://previewapk.siteport.site/cli/previewapk.py
python3 previewapk.py push path/to/app.apk --run --driver user

No shell? The same push is one multipart POST /portal/api/upload — see the API reference.

Etiquette

One device runs one app at a time. If two PR jobs push concurrently, the second push while a session is live gets a 409 — serialize preview jobs per device (e.g. a concurrency group in Actions), or give parallel pipelines separate slots.