Skip to content

Commit

Permalink
feat(android): android internal testing (#8577)
Browse files Browse the repository at this point in the history
  • Loading branch information
Brooooooklyn committed Oct 30, 2024
1 parent 8f95cc7 commit 650ecd0
Show file tree
Hide file tree
Showing 19 changed files with 444 additions and 35 deletions.
6 changes: 4 additions & 2 deletions .github/workflows/build-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -654,12 +654,14 @@ jobs:
path: ./test-results
if-no-files-found: ignore

test-build-ios:
test-build-mobile-app:
uses: ./.github/workflows/release-mobile.yml
with:
build-type: canary
build-target: development
secrets: inherit
permissions:
id-token: 'write'

test-done:
needs:
Expand All @@ -677,7 +679,7 @@ jobs:
- copilot-e2e-test
- server-e2e-test
- desktop-test
- test-build-ios
- test-build-mobile-app
if: always()
runs-on: ubuntu-latest
name: 3, 2, 1 Launch
Expand Down
106 changes: 100 additions & 6 deletions .github/workflows/release-mobile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,10 @@ on:
description: 'Build Target'
type: string
required: true
default: development
build-type:
description: 'Build Type'
type: string
required: true
default: canary
workflow_dispatch:
inputs:
build-target:
Expand All @@ -33,14 +31,15 @@ on:
- beta
- stable
env:
BUILD_TYPE: ${{ github.event.inputs.build-type || inputs.build-type }}
BUILD_TYPE: ${{ inputs.build-type || github.event.inputs.build-type }}
BUILD_TARGET: ${{ inputs.build-target || github.event.inputs.build-target }}
DEBUG: napi:*
KEYCHAIN_NAME: ${{ github.workspace }}/signing_temp

jobs:
build-ios-web:
runs-on: ubuntu-latest
environment: ${{ github.event.inputs.build-type || inputs.build-type }}
environment: ${{ inputs.build-type || github.event.inputs.build-type }}
outputs:
RELEASE_VERSION: ${{ steps.version.outputs.APP_VERSION }}
steps:
Expand Down Expand Up @@ -69,6 +68,39 @@ jobs:
with:
name: ios
path: packages/frontend/apps/ios/dist

build-android-web:
runs-on: ubuntu-latest
environment: ${{ github.event.inputs.build-type || inputs.build-type }}
outputs:
RELEASE_VERSION: ${{ steps.version.outputs.APP_VERSION }}
steps:
- uses: actions/checkout@v4
- name: Setup Version
id: version
uses: ./.github/actions/setup-version
- name: Setup Node.js
uses: ./.github/actions/setup-node
- name: Setup @sentry/cli
uses: ./.github/actions/setup-sentry
- name: Build Mobile
run: yarn nx build @affine/android --skip-nx-cache
env:
PUBLIC_PATH: '/'
MIXPANEL_TOKEN: ${{ secrets.MIXPANEL_TOKEN }}
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: 'affine'
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
SENTRY_RELEASE: ${{ steps.version.outputs.APP_VERSION }}
RELEASE_VERSION: ${{ steps.version.outputs.APP_VERSION }}
SKIP_NX_CACHE: 'true'
- name: Upload android artifact
uses: actions/upload-artifact@v4
with:
name: android
path: packages/frontend/apps/android/dist

ios:
runs-on: macos-latest
needs:
Expand Down Expand Up @@ -101,17 +133,79 @@ jobs:
with:
xcode-version: latest-stable
- name: Testflight
if: ${{ github.event.inputs.build-type || inputs.build-type }} != 'stable'
if: ${{ env.BUILD_TYPE != 'stable' }}
working-directory: packages/frontend/apps/ios/App
run: |
echo -n "${{ env.BUILD_PROVISION_PROFILE }}" | base64 --decode -o $PP_PATH
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
cp $PP_PATH ~/Library/MobileDevice/Provisioning\ Profiles
fastlane beta
env:
BUILD_TARGET: ${{ github.event.inputs.build-target || inputs.build-target }}
BUILD_PROVISION_PROFILE: ${{ secrets.BUILD_PROVISION_PROFILE }}
PP_PATH: ${{ runner.temp }}/build_pp.mobileprovision
APPLE_STORE_CONNECT_API_KEY_ID: ${{ secrets.APPLE_STORE_CONNECT_API_KEY_ID }}
APPLE_STORE_CONNECT_API_ISSUER_ID: ${{ secrets.APPLE_STORE_CONNECT_API_ISSUER_ID }}
APPLE_STORE_CONNECT_API_KEY: ${{ secrets.APPLE_STORE_CONNECT_API_KEY }}

android:
runs-on: ubuntu-latest
permissions:
id-token: 'write'
needs:
- build-android-web
steps:
- uses: actions/checkout@v4
- name: Download mobile artifact
uses: actions/download-artifact@v4
with:
name: android
path: packages/frontend/apps/android/dist
- name: Setup Node.js
uses: ./.github/actions/setup-node
timeout-minutes: 10
with:
extra-flags: workspaces focus @affine/android @affine/playstore-auto-bump
playwright-install: false
electron-install: false
hard-link-nm: false
enableScripts: false
- name: Cap sync
run: yarn workspace @affine/android cap sync
- name: Auth gcloud
id: auth
uses: google-github-actions/auth@v2
if: ${{ env.BUILD_TARGET == 'distribution' }}
with:
workload_identity_provider: 'projects/${{ secrets.GCP_PROJECT_NUMBER }}/locations/global/workloadIdentityPools/github-actions/providers/github-actions-helm-deploy'
service_account: '${{ secrets.GCP_HELM_DEPLOY_SERVICE_ACCOUNT }}'
token_format: 'access_token'
project_id: '${{ secrets.GCP_PROJECT_ID }}'
access_token_scopes: 'https://www.googleapis.com/auth/androidpublisher'
- uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '17'
- name: Auto increment version code
id: bump
if: ${{ env.BUILD_TARGET == 'distribution' }}
run: yarn workspace @affine/playstore-auto-bump bump
env:
GOOGLE_APPLICATION_CREDENTIALS: ${{ steps.auth.outputs.credentials_file_path }}
- name: Build
run: |
echo -n "${{ env.AFFINE_ANDROID_SIGN_KEYSTORE }}" | base64 --decode > packages/frontend/apps/android/affine.keystore
yarn workspace @affine/android cap build android
env:
AFFINE_ANDROID_KEYSTORE_PASSWORD: ${{ secrets.AFFINE_ANDROID_KEYSTORE_PASSWORD }}
AFFINE_ANDROID_KEYSTORE_ALIAS_PASSWORD: ${{ secrets.AFFINE_ANDROID_KEYSTORE_ALIAS_PASSWORD }}
AFFINE_ANDROID_SIGN_KEYSTORE: ${{ secrets.AFFINE_ANDROID_SIGN_KEYSTORE }}
- name: Upload to Google Play
uses: r0adkll/upload-google-play@v1
if: ${{ env.BUILD_TARGET == 'distribution' }}
with:
serviceAccountJson: ${{ steps.auth.outputs.credentials_file_path }}
packageName: app.affine.pro
releaseFiles: packages/frontend/apps/android/App/app/build/outputs/bundle/release/app-release-signed.aab
track: internal
status: draft
existingEditId: ${{ steps.bump.outputs.EDIT_ID }}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@
"prettier": "^3.3.3",
"semver": "^7.6.0",
"serve": "^14.2.1",
"typescript": "^5.4.5",
"typescript": "^5.6.3",
"unplugin-swc": "^1.4.5",
"vite": "^5.2.8",
"vitest": "2.1.1"
Expand Down
2 changes: 1 addition & 1 deletion packages/backend/server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@
"socket.io": "^4.7.5",
"stripe": "^17.0.0",
"ts-node": "^10.9.2",
"typescript": "^5.4.5",
"typescript": "^5.6.3",
"yjs": "patch:yjs@npm%3A13.6.18#~/.yarn/patches/yjs-npm-13.6.18-ad0d5f7c43.patch",
"zod": "^3.22.4"
},
Expand Down
1 change: 1 addition & 0 deletions packages/frontend/apps/android/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ App/**/*.p8
*.zip
*.cer
App/fastlane/report.xml
affine.keystore
4 changes: 2 additions & 2 deletions packages/frontend/apps/android/App/build.gradle
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {

repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:8.2.1'
classpath 'com.android.tools.build:gradle:8.7.1'
classpath 'com.google.gms:google-services:4.4.0'

// NOTE: Do not place your application dependencies here; they belong
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-all.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Expand Down
9 changes: 9 additions & 0 deletions packages/frontend/apps/android/capacitor.config.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { join } from 'node:path';

import type { CapacitorConfig } from '@capacitor/cli';

const config: CapacitorConfig = {
Expand All @@ -6,6 +8,13 @@ const config: CapacitorConfig = {
webDir: 'dist',
android: {
path: 'App',
buildOptions: {
keystorePath: join(__dirname, 'affine.keystore'),
keystorePassword: process.env.AFFINE_ANDROID_KEYSTORE_PASSWORD,
keystoreAlias: 'key0',
keystoreAliasPassword: process.env.AFFINE_ANDROID_KEYSTORE_ALIAS_PASSWORD,
releaseType: 'AAB',
},
},
};

Expand Down
2 changes: 1 addition & 1 deletion packages/frontend/apps/android/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,6 @@
"@types/react": "^18.2.75",
"@types/react-dom": "^18.2.24",
"cross-env": "^7.0.3",
"typescript": "^5.4.5"
"typescript": "^5.6.3"
}
}
2 changes: 1 addition & 1 deletion packages/frontend/apps/ios/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,6 @@
"@types/react": "^18.2.75",
"@types/react-dom": "^18.2.24",
"cross-env": "^7.0.3",
"typescript": "^5.4.5"
"typescript": "^5.6.3"
}
}
2 changes: 1 addition & 1 deletion packages/frontend/apps/mobile/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,6 @@
"@types/react": "^18.2.75",
"@types/react-dom": "^18.2.24",
"cross-env": "^7.0.3",
"typescript": "^5.4.5"
"typescript": "^5.6.3"
}
}
2 changes: 1 addition & 1 deletion packages/frontend/apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,6 @@
"@types/react": "^18.2.75",
"@types/react-dom": "^18.2.24",
"cross-env": "^7.0.3",
"typescript": "^5.4.5"
"typescript": "^5.6.3"
}
}
2 changes: 1 addition & 1 deletion packages/frontend/component/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@
"@types/react-dom": "^18.2.24",
"@vanilla-extract/css": "^1.14.2",
"storybook": "^8.2.9",
"typescript": "^5.4.5",
"typescript": "^5.6.3",
"unplugin-swc": "^1.5.1",
"vite": "^5.2.8",
"vitest": "2.1.1"
Expand Down
2 changes: 1 addition & 1 deletion packages/frontend/native/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
"ava": "^6.1.2",
"nx": "^20.0.3",
"ts-node": "^10.9.2",
"typescript": "^5.4.5"
"typescript": "^5.6.3"
},
"engines": {
"node": ">= 10"
Expand Down
80 changes: 80 additions & 0 deletions tools/playstore-auto-bump/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { execSync } from 'node:child_process';
import fs from 'node:fs';
import { join } from 'node:path';
import { fileURLToPath } from 'node:url';

import {
androidpublisher_v3,
auth as google_auth,
} from '@googleapis/androidpublisher';

export async function fetchVersionCode(applicationId: string): Promise<number> {
const auth = new google_auth.GoogleAuth({
scopes: ['https://www.googleapis.com/auth/androidpublisher'],
});
const androidPublisher = new androidpublisher_v3.Androidpublisher({
auth,
});
const appEdit = await androidPublisher.edits.insert({
packageName: applicationId,
requestBody: {
// 20min
expiryTimeSeconds: (Math.floor(Date.now() / 1000) + 12000).toString(),
},
});

if (!appEdit.data.id) {
throw new Error('Failed to create edit');
}

const lists = await androidPublisher.edits.bundles.list({
packageName: applicationId,
editId: appEdit.data.id,
});

let versionCode: number = 1;
try {
versionCode =
lists.data.bundles?.[lists.data.bundles.length - 1].versionCode || 1;
} catch {}

console.info(`Remote version code: ${versionCode}`);

console.info(`Writing edit ID to ${process.env.GITHUB_OUTPUT}`);

if (process.env.GITHUB_OUTPUT) {
execSync(
`echo "EDIT_ID=${appEdit.data.id}" >> ${process.env.GITHUB_OUTPUT}`,
{
stdio: 'inherit',
}
);
}

return versionCode;
}

const versionCodeRegexPattern = /(versionCode(?:\s|=)*)(.*)/;
const gradlePath = join(
fileURLToPath(import.meta.url),
'..',
'..',
'..',
'packages/frontend/apps/android/App/app/build.gradle'
);

let gradleVersionCode = 0;

const gradleFile = fs.readFileSync(gradlePath, 'utf8');
const matched = gradleFile.match(versionCodeRegexPattern);

const remoteVersion = await fetchVersionCode('app.affine.pro');

gradleVersionCode = parseInt(matched?.[2] || '0');
gradleVersionCode = isNaN(gradleVersionCode) ? 0 : gradleVersionCode;
const versionCode = Math.max(gradleVersionCode, remoteVersion) + 1;

fs.writeFileSync(
gradlePath,
gradleFile.replace(versionCodeRegexPattern, `$1 ${versionCode}`)
);
19 changes: 19 additions & 0 deletions tools/playstore-auto-bump/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "@affine/playstore-auto-bump",
"version": "0.17.0",
"private": true,
"type": "module",
"description": "Automatically bump the versionCode and versionName of an Android app from the Google Play Store versions",
"main": "index.ts",
"scripts": {
"bump": "node --import @oxc-node/core/register index.ts"
},
"dependencies": {
"@googleapis/androidpublisher": "^22.0.0",
"@oxc-node/core": "^0.0.15"
},
"devDependencies": {
"@types/node": "^20.14.12",
"typescript": "^5.6.3"
}
}
Loading

0 comments on commit 650ecd0

Please sign in to comment.