d3ro-voice/.github/workflows/ci.yml
2026-08-29 18:33:45 +09:00

323 lines
12 KiB
YAML

# .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