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)
- Add your API key to your CI secrets as
PREVIEWAPK_API_KEY. - Know your device id (run
previewapk statuslocally, or readGET /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.