Add Firebase Test Lab integration for Android instrumented tests

Implements issue #132. Builds debug app APK + androidTest APK via Dagger,
then runs them on Firebase Test Lab using the FIREBASE_TEST_LAB_SERVICE_ACCOUNT_KEY
secret and FIREBASE_PROJECT_ID variable.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Thomas SharedInbox
2026-05-21 17:20:26 +02:00
co-authored by Claude Sonnet 4.6
parent 2e080dd4ed
commit 01cbf5b805
3 changed files with 85 additions and 20 deletions
+26 -20
View File
@@ -1,14 +1,13 @@
# We switched to Dagger. Running the emulator tests in Dagger does not really work
# We will use an external service for device testing.
# TODO: Switch to device testing. First choose a service. Maybe codemagic.io
name: Android Emulator Tests (Disabled)
name: Android Firebase Test Lab
on:
workflow_dispatch: # Manual trigger only
push:
branches: [main]
pull_request:
jobs:
integration-android:
name: Android Emulator Integration Tests
firebase-tests:
name: Android Instrumented Tests (Firebase Test Lab)
runs-on: ubuntu-latest
timeout-minutes: 60
@@ -17,18 +16,25 @@ jobs:
with:
fetch-depth: 50
- name: Install Android SDK
- name: Install Dagger & Task
run: |
SDK="${ANDROID_HOME:-$HOME/Android/Sdk}"
if [ ! -d "$SDK/platforms/android-34" ]; then
wget -q https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip -O /tmp/cmdtools.zip
mkdir -p "$SDK/cmdline-tools"
unzip -q /tmp/cmdtools.zip -d "$SDK/cmdline-tools"
[ -d "$SDK/cmdline-tools/cmdline-tools" ] && mv "$SDK/cmdline-tools/cmdline-tools" "$SDK/cmdline-tools/latest"
yes | "$SDK/cmdline-tools/latest/bin/sdkmanager" --licenses >/dev/null 2>&1 || true
"$SDK/cmdline-tools/latest/bin/sdkmanager" "emulator" "system-images;android-34;google_apis;x86_64"
"$SDK/cmdline-tools/latest/bin/sdkmanager" "platform-tools" "build-tools;34.0.0" "platforms;android-34"
fi
mkdir -p $HOME/.local/bin
curl -L https://dl.dagger.io/dagger/install.sh | BIN_DIR=$HOME/.local/bin sh
curl -sL https://taskfile.dev/install.sh | sh -s -- -b $HOME/.local/bin
echo "$HOME/.local/bin" >> $GITHUB_PATH
sudo apt-get update && sudo apt-get install -y stunnel4 netcat-openbsd
- name: Run Android Integration Tests
run: task integration-android
- name: Setup Dagger Remote Engine (via stunnel)
env:
DAGGER_STUNNEL_URL: ${{ secrets.DAGGER_STUNNEL_URL }}
DAGGER_CA_CERT: ${{ secrets.DAGGER_CA_CERT }}
DAGGER_CLIENT_CERT: ${{ secrets.DAGGER_CLIENT_CERT }}
DAGGER_CLIENT_KEY: ${{ secrets.DAGGER_CLIENT_KEY }}
run: scripts/setup_dagger_remote.sh
- name: Run Android Tests on Firebase Test Lab
env:
FIREBASE_TEST_LAB_SERVICE_ACCOUNT_KEY: ${{ secrets.FIREBASE_TEST_LAB_SERVICE_ACCOUNT_KEY }}
FIREBASE_PROJECT_ID: ${{ vars.FIREBASE_PROJECT_ID }}
DAGGER_NO_NAG: "1"
run: task test-android-firebase
+10
View File
@@ -189,6 +189,16 @@ tasks:
cmds:
- dagger call --progress=plain -q -m ci --source=. test-sync-reliability
test-android-firebase:
desc: Build Android debug APKs and run instrumented tests on Firebase Test Lab (via Dagger)
preconditions:
- sh: test -n "$FIREBASE_TEST_LAB_SERVICE_ACCOUNT_KEY"
msg: "FIREBASE_TEST_LAB_SERVICE_ACCOUNT_KEY is not set"
- sh: test -n "$FIREBASE_PROJECT_ID"
msg: "FIREBASE_PROJECT_ID is not set"
cmds:
- dagger call --progress=plain -q -m ci --source=. test-android-firebase --service-account-key env:FIREBASE_TEST_LAB_SERVICE_ACCOUNT_KEY --project-id "$FIREBASE_PROJECT_ID"
ci-graph:
desc: Print a Mermaid diagram of the CI pipeline — paste into mermaid.live or any Markdown renderer
cmds:
+49
View File
@@ -252,6 +252,13 @@ func (m *Ci) androidSrc() *dagger.Directory {
})
}
// firebaseSrc is the source subset for Firebase Test Lab builds (app + instrumented tests).
func (m *Ci) firebaseSrc() *dagger.Directory {
return m.Source.Filter(dagger.DirectoryFilterOpts{
Include: []string{"lib/", "android/", "integration_test/", "assets/", "pubspec.yaml", "pubspec.lock", "drift_schemas/"},
})
}
// linuxSrc is the source subset for Linux builds and integration tests.
func (m *Ci) linuxSrc() *dagger.Directory {
return m.Source.Filter(dagger.DirectoryFilterOpts{
@@ -606,6 +613,48 @@ func (m *Ci) DeployApk(
Stdout(ctx)
}
// BuildAndroidDebugApks builds the debug app APK and the androidTest APK needed for Firebase Test Lab.
// Returns a flat directory with app-debug.apk and app-debug-androidTest.apk.
func (m *Ci) BuildAndroidDebugApks() *dagger.Directory {
built := m.setup(m.firebaseSrc()).
WithExec([]string{"flutter", "build", "apk", "--debug", "--no-pub"}).
WithWorkdir("/src/android").
WithExec([]string{"./gradlew", "app:assembleAndroidTest"}).
WithWorkdir("/src")
return dag.Directory().
WithFile("app-debug.apk",
built.File("build/app/outputs/flutter-apk/app-debug.apk")).
WithFile("app-debug-androidTest.apk",
built.File("android/app/build/outputs/apk/androidTest/debug/app-debug-androidTest.apk"))
}
// TestAndroidFirebase builds Android APKs and runs instrumented tests on Firebase Test Lab.
func (m *Ci) TestAndroidFirebase(
ctx context.Context,
serviceAccountKey *dagger.Secret,
projectID string,
) (string, error) {
apks := m.BuildAndroidDebugApks()
return dag.Container().
From("google/cloud-sdk:slim").
WithDirectory("/apks", apks).
WithSecretVariable("FIREBASE_SA_KEY", serviceAccountKey).
WithEnvVariable("FIREBASE_PROJECT_ID", projectID).
WithExec([]string{"/bin/bash", "-c",
`echo "$FIREBASE_SA_KEY" > /tmp/key.json && \
gcloud auth activate-service-account --key-file=/tmp/key.json && \
rm /tmp/key.json && \
gcloud config set project "$FIREBASE_PROJECT_ID" && \
gcloud firebase test android run \
--type instrumentation \
--app /apks/app-debug.apk \
--test /apks/app-debug-androidTest.apk \
--device model=Pixel6,version=33,locale=en,orientation=portrait`}).
Stdout(ctx)
}
// BuildAndroidRelease builds the AAB with a fixed build-number so Dagger can cache it.
// versionCode and signing are applied separately via StampAndroidVersionCode + SignAndroidBundle.
func (m *Ci) BuildAndroidRelease() *dagger.File {