feat(release): prepare 1.1.0 candidate
This commit is contained in:
parent
5a34f66981
commit
5205dcdfa9
736 changed files with 115667 additions and 12203 deletions
3
.github/workflows/build-mac.yml
vendored
3
.github/workflows/build-mac.yml
vendored
|
|
@ -1,9 +1,6 @@
|
|||
name: Build macOS
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
notarize:
|
||||
|
|
|
|||
237
.github/workflows/ci.yml
vendored
237
.github/workflows/ci.yml
vendored
|
|
@ -30,22 +30,80 @@ jobs:
|
|||
- name: Checkout Code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js 20 LTS
|
||||
uses: actions/setup-node@v4
|
||||
- name: Setup Node.js 24
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 20
|
||||
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
|
||||
continue-on-error: true
|
||||
|
||||
- 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)
|
||||
# ──────────────────────────────────────────────────────────────────
|
||||
|
|
@ -61,10 +119,10 @@ jobs:
|
|||
- name: Checkout Code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js 20 LTS
|
||||
uses: actions/setup-node@v4
|
||||
- name: Setup Node.js 24
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 20
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'npm'
|
||||
|
||||
- name: Install Dependencies
|
||||
|
|
@ -93,10 +151,10 @@ jobs:
|
|||
- name: Checkout Code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js 20 LTS
|
||||
uses: actions/setup-node@v4
|
||||
- name: Setup Node.js 24
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 20
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'npm'
|
||||
|
||||
- name: Install Dependencies
|
||||
|
|
@ -104,3 +162,162 @@ jobs:
|
|||
|
||||
- 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
|
||||
|
|
|
|||
3
.github/workflows/deploy-site.yml
vendored
3
.github/workflows/deploy-site.yml
vendored
|
|
@ -31,6 +31,9 @@ jobs:
|
|||
working-directory: site
|
||||
run: npm ci
|
||||
|
||||
- name: Verify Mobile Release Publication Boundary
|
||||
run: node scripts/ci/verify-mobile-release-boundary.mjs --self-test
|
||||
|
||||
- name: Build
|
||||
working-directory: site
|
||||
run: npm run build
|
||||
|
|
|
|||
3
.github/workflows/release-signing-ca.yml
vendored
3
.github/workflows/release-signing-ca.yml
vendored
|
|
@ -1,9 +1,6 @@
|
|||
name: Release & Code Signing CA Pipeline
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
|
|
|
|||
400
.github/workflows/release.yml
vendored
400
.github/workflows/release.yml
vendored
|
|
@ -12,27 +12,92 @@ on:
|
|||
version:
|
||||
description: 'Release version (e.g. 1.0.0)'
|
||||
required: true
|
||||
default: '1.0.0'
|
||||
default: '1.1.0'
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
packages: write
|
||||
|
||||
jobs:
|
||||
release-preflight:
|
||||
name: Release Preflight
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Node.js 24
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'npm'
|
||||
|
||||
- name: Setup .NET 10
|
||||
uses: actions/setup-dotnet@v5
|
||||
with:
|
||||
dotnet-version: '10.0.302'
|
||||
|
||||
- name: Setup Deno 2.8.1
|
||||
uses: denoland/setup-deno@v2
|
||||
with:
|
||||
deno-version: v2.8.1
|
||||
|
||||
- name: Install JavaScript Dependencies
|
||||
run: |
|
||||
npm ci
|
||||
npm --prefix apps/mobile-rn ci --workspaces=false
|
||||
|
||||
- name: Verify Source, Security, Tests, and Play Assets
|
||||
run: |
|
||||
npm run version:check
|
||||
if [[ "$GITHUB_REF" == refs/tags/* ]]; then
|
||||
node scripts/ci/sync-version.mjs --check --tag "$GITHUB_REF_NAME"
|
||||
fi
|
||||
npm run release:metadata:test
|
||||
npm run security:secrets:test
|
||||
npm run security:secrets
|
||||
npm run release:mobile:boundary:test
|
||||
npm run release:mobile:config:test
|
||||
npm run release:mobile:build-config:test
|
||||
npm run release:play:assets
|
||||
npm run lint
|
||||
npm run typecheck
|
||||
npm test
|
||||
npm --prefix apps/mobile-rn run lint
|
||||
npm --prefix apps/mobile-rn run typecheck
|
||||
npm --prefix apps/mobile-rn run test:ci
|
||||
|
||||
- name: Check and Test Every Supabase Edge Function
|
||||
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
|
||||
deno test --config server/supabase/functions/deno.json --allow-read --allow-env server/supabase/functions
|
||||
|
||||
- name: Test .NET API Authorization and Gateway Boundaries
|
||||
run: |
|
||||
dotnet restore apps/api-server.Tests/D3ROVoice.Api.Tests.csproj
|
||||
dotnet test apps/api-server.Tests/D3ROVoice.Api.Tests.csproj --configuration Release --no-restore -p:StaticWebAssetsEnabled=false
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────
|
||||
# 1. Package Windows Installer (.exe & .blockmap & latest.yml)
|
||||
# ──────────────────────────────────────────────────────────────────
|
||||
package-windows:
|
||||
name: Package Windows Desktop App
|
||||
needs: release-preflight
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js 20 LTS
|
||||
uses: actions/setup-node@v4
|
||||
- name: Setup Node.js 24
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 20
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'npm'
|
||||
|
||||
- name: Install Dependencies
|
||||
|
|
@ -40,6 +105,7 @@ jobs:
|
|||
|
||||
- name: Build All Workspaces
|
||||
run: |
|
||||
npm run version:check
|
||||
npm run typecheck
|
||||
npm run build --workspace=@d3ro/desktop
|
||||
|
||||
|
|
@ -66,15 +132,16 @@ jobs:
|
|||
# ──────────────────────────────────────────────────────────────────
|
||||
package-macos:
|
||||
name: Package macOS Desktop App
|
||||
needs: release-preflight
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js 20 LTS
|
||||
uses: actions/setup-node@v4
|
||||
- name: Setup Node.js 24
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 20
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'npm'
|
||||
|
||||
- name: Install Dependencies
|
||||
|
|
@ -82,6 +149,7 @@ jobs:
|
|||
|
||||
- name: Build All Workspaces
|
||||
run: |
|
||||
npm run version:check
|
||||
npm run typecheck
|
||||
npm run build --workspace=@d3ro/desktop
|
||||
|
||||
|
|
@ -108,10 +176,267 @@ jobs:
|
|||
apps/desktop/release/*/latest-mac.yml
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────
|
||||
# 3. Build & Containerize Admin Dashboard
|
||||
# 3. Package signed Android APK/AAB (arm64, production-only config)
|
||||
# ──────────────────────────────────────────────────────────────────
|
||||
package-android:
|
||||
name: Package Android Mobile App
|
||||
needs: release-preflight
|
||||
runs-on: ubuntu-latest
|
||||
environment: mobile-production-release
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Establish Trusted Mobile Release Identity
|
||||
id: android-version
|
||||
shell: bash
|
||||
env:
|
||||
DISPATCH_VERSION: ${{ inputs.version }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
git fetch --no-tags origin main
|
||||
if [ "$GITHUB_EVENT_NAME" = "workflow_dispatch" ]; then
|
||||
test "$GITHUB_REF" = "refs/heads/main"
|
||||
test "$GITHUB_SHA" = "$(git rev-parse origin/main)"
|
||||
VERSION_NAME="$DISPATCH_VERSION"
|
||||
RELEASE_TAG="v$VERSION_NAME"
|
||||
else
|
||||
[[ "$GITHUB_REF" == refs/tags/v* ]]
|
||||
VERSION_NAME="${GITHUB_REF_NAME#v}"
|
||||
RELEASE_TAG="$GITHUB_REF_NAME"
|
||||
git merge-base --is-ancestor "$GITHUB_SHA" origin/main
|
||||
fi
|
||||
SSOT_VERSION="$(node -p "require('./release/product-version.json').version")"
|
||||
VERSION_CODE="$(node -p "require('./release/product-version.json').androidVersionCode")"
|
||||
[[ "$VERSION_NAME" =~ ^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$ ]]
|
||||
test "$VERSION_NAME" = "$SSOT_VERSION"
|
||||
test "$RELEASE_TAG" = "v$VERSION_NAME"
|
||||
[[ "$VERSION_CODE" =~ ^[1-9][0-9]{0,9}$ ]]
|
||||
test "$VERSION_CODE" -le 2100000000
|
||||
printf 'name=%s\n' "$VERSION_NAME" >> "$GITHUB_OUTPUT"
|
||||
printf 'code=%s\n' "$VERSION_CODE" >> "$GITHUB_OUTPUT"
|
||||
printf 'tag=%s\n' "$RELEASE_TAG" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Verify Immutable Checkout Identity
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
test "$GITHUB_SHA" = "$(git rev-parse HEAD)"
|
||||
test -z "$(git status --porcelain --untracked-files=all)"
|
||||
git rev-parse "${GITHUB_SHA}^{tree}"
|
||||
|
||||
- name: Setup Node.js 24
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'npm'
|
||||
|
||||
- name: Verify Mobile Release Boundary Source Contract
|
||||
run: node scripts/ci/verify-mobile-release-boundary.mjs --self-test
|
||||
|
||||
- name: Verify Mobile Release Configuration Contract
|
||||
run: npm run release:mobile:config:test
|
||||
|
||||
- name: Verify Mobile Build Configuration Contract
|
||||
run: npm run release:mobile:build-config:test
|
||||
|
||||
- name: Verify Play Store Asset Contract
|
||||
run: npm run release:play:assets
|
||||
|
||||
- name: Require Restricted AAB Handoff Visibility
|
||||
shell: bash
|
||||
env:
|
||||
REPOSITORY_VISIBILITY: ${{ github.event.repository.visibility }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
test "$REPOSITORY_VISIBILITY" = "private"
|
||||
|
||||
- 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: Install Pinned Official Bundletool
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
curl --fail --silent --show-error --location \
|
||||
--output "$RUNNER_TEMP/bundletool-all-1.18.3.jar" \
|
||||
https://github.com/google/bundletool/releases/download/1.18.3/bundletool-all-1.18.3.jar
|
||||
printf '%s %s\n' \
|
||||
a099cfa1543f55593bc2ed16a70a7c67fe54b1747bb7301f37fdfd6d91028e29 \
|
||||
"$RUNNER_TEMP/bundletool-all-1.18.3.jar" | sha256sum --check --strict
|
||||
|
||||
- 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: Materialize Release-only Configuration
|
||||
shell: bash
|
||||
env:
|
||||
ANDROID_RELEASE_KEYSTORE_B64: ${{ secrets.ANDROID_RELEASE_KEYSTORE_B64 }}
|
||||
ANDROID_GOOGLE_SERVICES_JSON_B64: ${{ secrets.ANDROID_GOOGLE_SERVICES_JSON_B64 }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
umask 077
|
||||
test -n "$ANDROID_RELEASE_KEYSTORE_B64"
|
||||
test -n "$ANDROID_GOOGLE_SERVICES_JSON_B64"
|
||||
printf '%s' "$ANDROID_RELEASE_KEYSTORE_B64" | base64 --decode > apps/mobile-rn/android/app/release.keystore
|
||||
printf '%s' "$ANDROID_GOOGLE_SERVICES_JSON_B64" | base64 --decode > apps/mobile-rn/android/app/google-services.json
|
||||
test -s apps/mobile-rn/android/app/release.keystore
|
||||
test -s apps/mobile-rn/android/app/google-services.json
|
||||
|
||||
- name: Prepare Verified Whisper Model
|
||||
run: node scripts/ci/prepare-whisper-model.mjs
|
||||
|
||||
- name: Verify Production Firebase and AdMob Configuration
|
||||
env:
|
||||
D3RO_FIREBASE_EXPECTED_PROJECT_ID: ${{ secrets.FIREBASE_PROJECT_ID }}
|
||||
D3RO_FIREBASE_EXPECTED_PROJECT_NUMBER: ${{ secrets.FIREBASE_PROJECT_NUMBER }}
|
||||
D3RO_FIREBASE_EXPECTED_MOBILESDK_APP_ID: ${{ secrets.FIREBASE_MOBILESDK_APP_ID }}
|
||||
run: |
|
||||
export D3RO_ADMOB_APP_ID="$(node -p "require('./release/android-release-identity.json').adMobAppId")"
|
||||
export D3RO_ADMOB_BANNER_UNIT_ID="$(node -p "require('./release/android-release-identity.json').adMobBannerUnitId")"
|
||||
export D3RO_ADMOB_REWARDED_UNIT_ID="$(node -p "require('./release/android-release-identity.json').adMobRewardedUnitId")"
|
||||
npm run release:mobile:config
|
||||
|
||||
- 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 Signed arm64 APK and AAB
|
||||
working-directory: apps/mobile-rn/android
|
||||
env:
|
||||
D3RO_RELEASE_STORE_FILE: ${{ github.workspace }}/apps/mobile-rn/android/app/release.keystore
|
||||
D3RO_RELEASE_STORE_PASSWORD: ${{ secrets.ANDROID_RELEASE_STORE_PASSWORD }}
|
||||
D3RO_RELEASE_KEY_ALIAS: ${{ secrets.ANDROID_RELEASE_KEY_ALIAS }}
|
||||
D3RO_RELEASE_KEY_PASSWORD: ${{ secrets.ANDROID_RELEASE_KEY_PASSWORD }}
|
||||
D3RO_VERSION_NAME: ${{ steps.android-version.outputs.name }}
|
||||
D3RO_VERSION_CODE: ${{ steps.android-version.outputs.code }}
|
||||
run: |
|
||||
export D3RO_ADMOB_APP_ID="$(node -p "require('../../../release/android-release-identity.json').adMobAppId")"
|
||||
export D3RO_ADMOB_BANNER_UNIT_ID="$(node -p "require('../../../release/android-release-identity.json').adMobBannerUnitId")"
|
||||
export D3RO_ADMOB_REWARDED_UNIT_ID="$(node -p "require('../../../release/android-release-identity.json').adMobRewardedUnitId")"
|
||||
./gradlew :app:assembleRelease :app:bundleRelease -PreactNativeArchitectures=arm64-v8a --no-daemon
|
||||
|
||||
- name: Materialize Release Evidence Signing Key
|
||||
shell: bash
|
||||
env:
|
||||
ANDROID_RELEASE_EVIDENCE_PRIVATE_KEY_B64: ${{ secrets.ANDROID_RELEASE_EVIDENCE_PRIVATE_KEY_B64 }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
umask 077
|
||||
test -n "$ANDROID_RELEASE_EVIDENCE_PRIVATE_KEY_B64"
|
||||
printf '%s' "$ANDROID_RELEASE_EVIDENCE_PRIVATE_KEY_B64" | base64 --decode > apps/mobile-rn/android/app/release-evidence-private.pem
|
||||
test -s apps/mobile-rn/android/app/release-evidence-private.pem
|
||||
|
||||
- name: Verify Release BuildConfig, Signature, ABI, Bundle, Ads, and Offline Model
|
||||
shell: bash
|
||||
env:
|
||||
D3RO_VERSION_NAME: ${{ steps.android-version.outputs.name }}
|
||||
D3RO_VERSION_CODE: ${{ steps.android-version.outputs.code }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
D3RO_ADMOB_APP_ID="$(node -p "require('./release/android-release-identity.json').adMobAppId")"
|
||||
D3RO_ADMOB_BANNER_UNIT_ID="$(node -p "require('./release/android-release-identity.json').adMobBannerUnitId")"
|
||||
D3RO_ADMOB_REWARDED_UNIT_ID="$(node -p "require('./release/android-release-identity.json').adMobRewardedUnitId")"
|
||||
ANDROID_UPLOAD_CERT_SHA256="$(node -p "require('./release/android-release-identity.json').uploadCertificateSha256")"
|
||||
PLAY_APP_SIGNING_CERT_SHA256="$(node -p "require('./release/android-release-identity.json').playAppSigningCertificateSha256")"
|
||||
APK=apps/mobile-rn/android/app/build/outputs/apk/release/app-release.apk
|
||||
AAB=apps/mobile-rn/android/app/build/outputs/bundle/release/app-release.aab
|
||||
test -f "$APK"
|
||||
test -f "$AAB"
|
||||
node scripts/ci/verify-mobile-build-config.mjs release \
|
||||
| tee apps/mobile-rn/android/app/build/outputs/release-build-config.json
|
||||
node scripts/ci/create-mobile-release-evidence.mjs \
|
||||
--apk "$APK" \
|
||||
--aab "$AAB" \
|
||||
--bundletool "$RUNNER_TEMP/bundletool-all-1.18.3.jar" \
|
||||
--repository "$GITHUB_REPOSITORY" \
|
||||
--commit-sha "$GITHUB_SHA" \
|
||||
--tree-sha "$(git rev-parse "${GITHUB_SHA}^{tree}")" \
|
||||
--git-ref "$GITHUB_REF" \
|
||||
--workflow-identity "$GITHUB_WORKFLOW_REF" \
|
||||
--run-id "$GITHUB_RUN_ID" \
|
||||
--run-attempt "$GITHUB_RUN_ATTEMPT" \
|
||||
--runner-identity "$RUNNER_NAME:$RUNNER_OS:$RUNNER_ARCH" \
|
||||
--expected-admob-app-id "$D3RO_ADMOB_APP_ID" \
|
||||
--expected-upload-cert-sha256 "$ANDROID_UPLOAD_CERT_SHA256" \
|
||||
--expected-version-name "$D3RO_VERSION_NAME" \
|
||||
--expected-version-code "$D3RO_VERSION_CODE" \
|
||||
--private-key apps/mobile-rn/android/app/release-evidence-private.pem \
|
||||
--snapshot-dir apps/mobile-rn/android/app/build/outputs/release-snapshot
|
||||
VERIFIER_SHA256="$(sha256sum scripts/ci/verify-android-artifact.mjs | awk '{print $1}')"
|
||||
BUNDLETOOL_SHA256="a099cfa1543f55593bc2ed16a70a7c67fe54b1747bb7301f37fdfd6d91028e29"
|
||||
node scripts/ci/prepare-mobile-release-publication.mjs \
|
||||
--source-root apps/mobile-rn/android/app/build/outputs/release-snapshot \
|
||||
--apk apps/mobile-rn/android/app/build/outputs/release-snapshot/app-release.apk \
|
||||
--aab apps/mobile-rn/android/app/build/outputs/release-snapshot/app-release.aab \
|
||||
--evidence apps/mobile-rn/android/app/build/outputs/release-snapshot/release-artifact-evidence.json \
|
||||
--public-key release/mobile-release-evidence-public.pem \
|
||||
--destination-dir apps/mobile-rn/android/app/build/outputs/release-publication \
|
||||
--expected-admob-app-id "$D3RO_ADMOB_APP_ID" \
|
||||
--expected-upload-cert-sha256 "$ANDROID_UPLOAD_CERT_SHA256" \
|
||||
--expected-version-name "$D3RO_VERSION_NAME" \
|
||||
--expected-version-code "$D3RO_VERSION_CODE" \
|
||||
--expected-repository "$GITHUB_REPOSITORY" \
|
||||
--expected-commit-sha "$GITHUB_SHA" \
|
||||
--expected-tree-sha "$(git rev-parse "${GITHUB_SHA}^{tree}")" \
|
||||
--expected-git-ref "$GITHUB_REF" \
|
||||
--expected-workflow-identity "$GITHUB_WORKFLOW_REF" \
|
||||
--expected-run-id "$GITHUB_RUN_ID" \
|
||||
--expected-run-attempt "$GITHUB_RUN_ATTEMPT" \
|
||||
--expected-runner-identity "$RUNNER_NAME:$RUNNER_OS:$RUNNER_ARCH" \
|
||||
--expected-verifier-sha256 "$VERIFIER_SHA256" \
|
||||
--expected-bundletool-sha256 "$BUNDLETOOL_SHA256"
|
||||
node scripts/ci/verify-android-app-links.mjs \
|
||||
--expected-play-app-signing-cert-sha256 "$PLAY_APP_SIGNING_CERT_SHA256" \
|
||||
--forbidden-upload-cert-sha256 "$ANDROID_UPLOAD_CERT_SHA256" \
|
||||
| tee apps/mobile-rn/android/app/build/outputs/release-app-links-evidence.json
|
||||
sha256sum apps/mobile-rn/android/app/build/outputs/release-publication/app-release.apk \
|
||||
apps/mobile-rn/android/app/build/outputs/release-publication/app-release.aab \
|
||||
| tee apps/mobile-rn/android/app/build/outputs/release-publication/SHA256SUMS.txt
|
||||
|
||||
- name: Remove Materialized Release Secrets
|
||||
if: always()
|
||||
shell: bash
|
||||
run: rm -f apps/mobile-rn/android/app/release.keystore apps/mobile-rn/android/app/google-services.json apps/mobile-rn/android/app/release-evidence-private.pem
|
||||
|
||||
- name: Upload Restricted Play Console AAB Handoff
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: android-play-console-handoff
|
||||
path: |
|
||||
apps/mobile-rn/android/app/build/outputs/release-publication/app-release.aab
|
||||
apps/mobile-rn/android/app/build/outputs/release-publication/android-release-evidence.json
|
||||
apps/mobile-rn/android/app/build/outputs/release-publication/android-publication-manifest.json
|
||||
apps/mobile-rn/android/app/build/outputs/release-publication/SHA256SUMS.txt
|
||||
apps/mobile-rn/android/app/build/outputs/release-snapshot/release-artifact-verification.json
|
||||
apps/mobile-rn/android/app/build/outputs/release-build-config.json
|
||||
apps/mobile-rn/android/app/build/outputs/release-app-links-evidence.json
|
||||
retention-days: 7
|
||||
if-no-files-found: error
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────
|
||||
# 4. Build & Containerize Admin Dashboard
|
||||
# ──────────────────────────────────────────────────────────────────
|
||||
package-admin-docker:
|
||||
name: Build & Publish Admin Docker Image
|
||||
needs: release-preflight
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
|
|
@ -137,21 +462,54 @@ jobs:
|
|||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile.admin
|
||||
file: ./apps/admin/Dockerfile
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────
|
||||
# 4. Create GitHub Release & Upload Checksums
|
||||
# 5. Create GitHub Release & Upload Checksums
|
||||
# ──────────────────────────────────────────────────────────────────
|
||||
publish-release:
|
||||
name: Publish Official GitHub Release
|
||||
needs: [package-windows, package-macos, package-admin-docker]
|
||||
needs: [package-windows, package-macos, package-android, package-admin-docker]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Re-establish Trusted Release Identity
|
||||
id: release-identity
|
||||
shell: bash
|
||||
env:
|
||||
DISPATCH_VERSION: ${{ inputs.version }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
git fetch --no-tags origin main
|
||||
if [ "$GITHUB_EVENT_NAME" = "workflow_dispatch" ]; then
|
||||
test "$GITHUB_REF" = "refs/heads/main"
|
||||
test "$GITHUB_SHA" = "$(git rev-parse origin/main)"
|
||||
VERSION_NAME="$DISPATCH_VERSION"
|
||||
RELEASE_TAG="v$VERSION_NAME"
|
||||
else
|
||||
[[ "$GITHUB_REF" == refs/tags/v* ]]
|
||||
VERSION_NAME="${GITHUB_REF_NAME#v}"
|
||||
RELEASE_TAG="$GITHUB_REF_NAME"
|
||||
git merge-base --is-ancestor "$GITHUB_SHA" origin/main
|
||||
fi
|
||||
SSOT_VERSION="$(node -p "require('./release/product-version.json').version")"
|
||||
[[ "$VERSION_NAME" =~ ^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$ ]]
|
||||
test "$VERSION_NAME" = "$SSOT_VERSION"
|
||||
test "$RELEASE_TAG" = "v$VERSION_NAME"
|
||||
printf 'name=%s\n' "$VERSION_NAME" >> "$GITHUB_OUTPUT"
|
||||
printf 'tag=%s\n' "$RELEASE_TAG" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Setup Node.js 24
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
|
||||
- name: Download Windows Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
|
|
@ -167,9 +525,17 @@ jobs:
|
|||
|
||||
- name: Generate SHA-256 Checksums
|
||||
run: |
|
||||
cd release-dist
|
||||
sha256sum * > SHA256SUMS.txt || shasum -a 256 * > SHA256SUMS.txt
|
||||
cat SHA256SUMS.txt
|
||||
set -euo pipefail
|
||||
find release-dist -type f ! -name SHA256SUMS.txt -print0 \
|
||||
| sort -z \
|
||||
| xargs -0 sha256sum > release-dist/SHA256SUMS.txt
|
||||
cat release-dist/SHA256SUMS.txt
|
||||
|
||||
- name: Extract Canonical Release Notes
|
||||
run: >-
|
||||
node scripts/ci/extract-release-notes.mjs
|
||||
--version "${{ steps.release-identity.outputs.name }}"
|
||||
--output release-notes.md
|
||||
|
||||
- name: Create GitHub Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
|
|
@ -178,6 +544,10 @@ jobs:
|
|||
release-dist/*
|
||||
draft: false
|
||||
prerelease: false
|
||||
generate_release_notes: true
|
||||
body_path: release-notes.md
|
||||
generate_release_notes: false
|
||||
tag_name: ${{ steps.release-identity.outputs.tag }}
|
||||
fail_on_unmatched_files: true
|
||||
overwrite_files: false
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue