# .github/workflows/ci.yml # Continuous Integration Pipeline for D3RO Voice Monorepo name: CI Pipeline on: push: branches: - main - develop - 'feature/**' - 'fix/**' pull_request: branches: - main - develop concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: # ────────────────────────────────────────────────────────────────── # 1. Code Quality, Linting & Typecheck # ────────────────────────────────────────────────────────────────── code-quality: name: Code Quality & Typecheck runs-on: ubuntu-latest steps: - name: Checkout Code uses: actions/checkout@v4 - name: Setup Node.js 24 uses: actions/setup-node@v6 with: node-version-file: '.nvmrc' cache: 'npm' - name: Install Dependencies run: npm ci - name: Credential Scanner Self-Test run: npm run security:secrets:test - name: Hard-Coded Credential Scan run: npm run security:secrets - name: Mobile Release Boundary Self-Test run: npm run release:mobile:boundary:test - name: Mobile Release Configuration Self-Test run: npm run release:mobile:config:test - name: Mobile Build Configuration Self-Test run: npm run release:mobile:build-config:test - name: Play Store Asset Contract run: npm run release:play:assets - name: Lint Check run: npm run lint - name: Typecheck All Workspaces run: npm run typecheck api-server-tests: name: .NET API Server Tests runs-on: ubuntu-latest steps: - name: Checkout Code uses: actions/checkout@v4 - name: Setup .NET 10 uses: actions/setup-dotnet@v5 with: dotnet-version: '10.0.302' - name: Restore API Test Dependencies run: dotnet restore apps/api-server.Tests/D3ROVoice.Api.Tests.csproj - name: Run API Authorization and Gateway Tests run: dotnet test apps/api-server.Tests/D3ROVoice.Api.Tests.csproj --configuration Release --no-restore -p:StaticWebAssetsEnabled=false edge-functions-quality: name: Supabase Edge Functions Typecheck & Tests runs-on: ubuntu-latest steps: - name: Checkout Code uses: actions/checkout@v4 - name: Setup Deno 2.8.1 uses: denoland/setup-deno@v2 with: deno-version: v2.8.1 - name: Check Every Edge Function Entrypoint shell: bash run: | set -euo pipefail for entrypoint in server/supabase/functions/*/index.ts; do deno check --config server/supabase/functions/deno.json "$entrypoint" done - name: Run Edge Function Contract Tests run: deno test --config server/supabase/functions/deno.json --allow-read --allow-env server/supabase/functions # ────────────────────────────────────────────────────────────────── # 2. Automated Test Matrix (Windows / macOS / Ubuntu) # ────────────────────────────────────────────────────────────────── test-matrix: name: Test Suite (${{ matrix.os }}) needs: code-quality strategy: fail-fast: false matrix: os: [windows-latest, macos-latest, ubuntu-latest] runs-on: ${{ matrix.os }} steps: - name: Checkout Code uses: actions/checkout@v4 - name: Setup Node.js 24 uses: actions/setup-node@v6 with: node-version-file: '.nvmrc' cache: 'npm' - name: Install Dependencies run: npm ci - name: Run Monorepo Test Suites (Vitest) run: npm test # ────────────────────────────────────────────────────────────────── # 3. Build Validation for All Workspaces # ────────────────────────────────────────────────────────────────── build-validation: name: Build Validation (${{ matrix.target }}) needs: code-quality strategy: matrix: include: - target: desktop os: windows-latest cmd: npm run build --workspace=@d3ro/desktop - target: admin os: ubuntu-latest cmd: npm run build --workspace=@d3ro/admin runs-on: ${{ matrix.os }} steps: - name: Checkout Code uses: actions/checkout@v4 - name: Setup Node.js 24 uses: actions/setup-node@v6 with: node-version-file: '.nvmrc' cache: 'npm' - name: Install Dependencies run: npm ci - name: Build Target Workspace run: ${{ matrix.cmd }} # ────────────────────────────────────────────────────────────────── # 4. Android x86_64 artifacts and native dependency gate # ────────────────────────────────────────────────────────────────── mobile-android: name: Mobile Android (universal debug + bundled universal E2E) needs: code-quality runs-on: ubuntu-latest steps: - name: Checkout Code uses: actions/checkout@v4 - name: Setup Node.js 24 uses: actions/setup-node@v6 with: node-version-file: '.nvmrc' cache: 'npm' - name: Setup JDK 17 uses: actions/setup-java@v5 with: distribution: temurin java-version: '17' - name: Setup Android SDK uses: android-actions/setup-android@v4 - name: Setup Gradle uses: gradle/actions/setup-gradle@v6 with: cache-provider: basic - name: Install Dependencies run: | npm ci npm --prefix apps/mobile-rn ci --workspaces=false - name: Prepare Verified Whisper Model run: node scripts/ci/prepare-whisper-model.mjs - name: Test Mobile TypeScript and Jest run: | npm --prefix apps/mobile-rn run lint npm --prefix apps/mobile-rn run typecheck npm --prefix apps/mobile-rn run test:ci - name: Build Universal Debug, CSPRNG Test, and Bundled Universal E2E APKs working-directory: apps/mobile-rn/android env: D3RO_VERSION_NAME: 0.0.0-e2e.${{ github.run_number }} D3RO_VERSION_CODE: ${{ github.run_number }} run: ./gradlew :app:assembleDebug :app:assembleDebugAndroidTest :app:assembleE2e -PreactNativeArchitectures=arm64-v8a,x86_64 --no-daemon - name: Verify BuildConfig and APK Runtime Contracts env: D3RO_VERSION_NAME: 0.0.0-e2e.${{ github.run_number }} D3RO_VERSION_CODE: ${{ github.run_number }} run: | set -euo pipefail DEBUG_APK=apps/mobile-rn/android/app/build/outputs/apk/debug/app-debug.apk E2E_APK=apps/mobile-rn/android/app/build/outputs/apk/e2e/app-e2e.apk test -f "$DEBUG_APK" test -f "$E2E_APK" node scripts/ci/verify-mobile-build-config.mjs debug \ | tee apps/mobile-rn/android/app/build/outputs/debug-build-config.json node scripts/ci/verify-mobile-build-config.mjs e2e \ | tee apps/mobile-rn/android/app/build/outputs/e2e-build-config.json node scripts/ci/verify-android-artifact.mjs --mode debug --apk "$DEBUG_APK" \ | tee apps/mobile-rn/android/app/build/outputs/debug-artifact-evidence.json node scripts/ci/verify-android-artifact.mjs \ --mode e2e \ --apk "$E2E_APK" \ --expected-version-name "$D3RO_VERSION_NAME" \ --expected-version-code "$D3RO_VERSION_CODE" \ | tee apps/mobile-rn/android/app/build/outputs/e2e-artifact-evidence.json node scripts/ci/verify-android-app-links.mjs \ | tee apps/mobile-rn/android/app/build/outputs/app-links-evidence.json sha256sum "$DEBUG_APK" "$E2E_APK" | tee apps/mobile-rn/android/app/build/outputs/android-ci.sha256 - name: Upload Universal Android Artifacts uses: actions/upload-artifact@v4 with: name: d3ro-mobile-android-universal-e2e path: | apps/mobile-rn/android/app/build/outputs/apk/debug/app-debug.apk apps/mobile-rn/android/app/build/outputs/apk/androidTest/debug/app-debug-androidTest.apk apps/mobile-rn/android/app/build/outputs/apk/e2e/app-e2e.apk apps/mobile-rn/android/app/build/outputs/android-ci.sha256 apps/mobile-rn/android/app/build/outputs/*-build-config.json apps/mobile-rn/android/app/build/outputs/*-artifact-evidence.json apps/mobile-rn/android/app/build/outputs/app-links-evidence.json if-no-files-found: error # ────────────────────────────────────────────────────────────────── # 5. Installed bundled APK on a clean API 35 x86_64 emulator # ────────────────────────────────────────────────────────────────── mobile-emulator-e2e: name: Mobile Emulator E2E (API 35) needs: mobile-android runs-on: ubuntu-latest steps: - name: Checkout Code uses: actions/checkout@v4 - name: Setup JDK 17 uses: actions/setup-java@v5 with: distribution: temurin java-version: '17' - name: Setup Android SDK uses: android-actions/setup-android@v4 - name: Download Bundled Android Artifact uses: actions/download-artifact@v4 with: name: d3ro-mobile-android-universal-e2e path: mobile-artifact - name: Install Verified Maestro CLI 2.7.0 run: | curl -fsSL https://github.com/mobile-dev-inc/maestro/releases/download/cli-2.7.0/maestro.zip -o /tmp/maestro.zip echo 'a4ccab6b604617e7aef6db4f885666056eabe5cfa32befaa3bc994041b8fcbb5 /tmp/maestro.zip' | sha256sum -c - unzip -q /tmp/maestro.zip -d "$RUNNER_TEMP/maestro" echo "$RUNNER_TEMP/maestro/maestro/bin" >> "$GITHUB_PATH" - name: Run Mandatory Clean-room and Optional External-account Journeys uses: reactivecircus/android-emulator-runner@v2 env: MOBILE_E2E_EMAIL: ${{ secrets.MOBILE_E2E_EMAIL }} MOBILE_E2E_PASSWORD: ${{ secrets.MOBILE_E2E_PASSWORD }} with: api-level: 35 target: google_apis arch: x86_64 profile: pixel_6 disable-animations: true emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none script: | set -euo pipefail DEBUG_APK="$(find mobile-artifact -path '*/apk/debug/app-debug.apk' -print -quit)" TEST_APK="$(find mobile-artifact -name app-debug-androidTest.apk -print -quit)" E2E_APK="$(find mobile-artifact -name app-e2e.apk -print -quit)" test -n "$DEBUG_APK" test -n "$TEST_APK" test -n "$E2E_APK" maestro --version bash scripts/ci/run-mobile-csprng-instrumentation.sh "$DEBUG_APK" "$TEST_APK" bash scripts/ci/run-mobile-emulator-gate.sh "$E2E_APK" - name: Upload Emulator Evidence if: always() uses: actions/upload-artifact@v4 with: name: d3ro-mobile-emulator-e2e path: | apps/mobile-rn/.maestro/*.junit.xml apps/mobile-rn/.maestro-output/ if-no-files-found: warn