-
Notifications
You must be signed in to change notification settings - Fork 320
326 lines (288 loc) · 12.1 KB
/
Copy pathandroid-release.yml
File metadata and controls
326 lines (288 loc) · 12.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
name: Android Release
on:
workflow_call:
inputs:
version:
description: 'Version to build (e.g., 1.2.3 without v prefix)'
required: true
type: string
nightly:
description: 'Is this a nightly build?'
required: false
default: false
type: boolean
notify_testers:
description: 'Notify external testers'
required: false
default: false
type: boolean
workflow_dispatch:
inputs:
version:
description: 'Version to build (e.g., 1.2.3 without v prefix)'
required: true
type: string
version_type:
description: 'Type of version bump (auto, patch, minor, major)'
required: false
type: choice
default: 'auto'
options:
- auto
- patch
- minor
- major
notify_testers:
description: 'Notify external testers'
required: false
default: false
type: boolean
concurrency:
group: android-release-${{ inputs.version }}
cancel-in-progress: true
permissions:
contents: write
jobs:
build_and_deploy_android:
runs-on: ubuntu-latest
environment: thunderbolt_release
outputs:
build_version: ${{ steps.determine_version.outputs.build_version }}
steps:
- name: Free disk space
run: |
sudo rm -rf /usr/share/dotnet
sudo rm -rf /usr/local/lib/android/sdk/ndk/27*
sudo rm -rf /opt/ghc
sudo rm -rf /opt/hostedtoolcache/CodeQL
sudo rm -rf /usr/share/swift
sudo rm -rf /usr/local/share/boost
sudo docker system prune -af || true
df -h
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
ref: v${{ inputs.version }}
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: 'lts/*'
- name: Setup Bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
- name: Display version information
env:
INPUT_NIGHTLY: ${{ inputs.nightly }}
INPUT_VERSION: ${{ inputs.version }}
run: |
if [ "$INPUT_NIGHTLY" == "true" ]; then
echo "🌙 Nightly build"
fi
if [ -n "$INPUT_VERSION" ]; then
echo "🏷️ Using specified version: $INPUT_VERSION"
else
CURRENT_VERSION=$(node -p "require('./package.json').version")
echo "📦 Current version: $CURRENT_VERSION"
fi
- name: Determine build version
id: determine_version
uses: ./.github/actions/determine-version
with:
version: ${{ inputs.version }}
version_type: ${{ inputs.version_type }}
- name: Export build version env
env:
BUILD_VERSION: ${{ steps.determine_version.outputs.build_version }}
run: echo "BUILD_VERSION=$BUILD_VERSION" >> $GITHUB_ENV
- name: Calculate versionCode from git history
id: version_code
run: |
# versionCode must be strictly monotonic across uploads: Play permanently
# burns any code it has accepted, so neither a re-dispatch nor a "Re-run"
# of the same workflow can reuse a code. RUN_NUMBER increments per new
# dispatch but not on re-runs, RUN_ATTEMPT increments on every re-run —
# combining them (RUN_NUMBER * 100 + RUN_ATTEMPT) covers both. See THU-643.
COMMIT_COUNT=$(git rev-list --count HEAD)
BASE_OFFSET=2000
VERSION_CODE=$((BASE_OFFSET + COMMIT_COUNT + GITHUB_RUN_NUMBER * 100 + GITHUB_RUN_ATTEMPT))
echo "versionCode=$VERSION_CODE" >> $GITHUB_OUTPUT
echo "📱 Calculated versionCode: $VERSION_CODE (base $BASE_OFFSET + $COMMIT_COUNT commits + run #$GITHUB_RUN_NUMBER attempt $GITHUB_RUN_ATTEMPT)"
- name: Set build version and versionCode locally for Android
run: |
VERSION="$BUILD_VERSION"
VERSION_CODE="${{ steps.version_code.outputs.versionCode }}"
echo "🔧 Applying version $VERSION and versionCode $VERSION_CODE locally for the build"
# Update package.json version
node -e "
const fs = require('fs');
const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
pkg.version = process.env.VERSION;
fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2) + '\n');
"
# Update Cargo.toml version
sed -i "s/^version = \".*\"/version = \"$VERSION\"/" src-tauri/Cargo.toml
# Update tauri.conf.json with versionCode
node -e "
const fs = require('fs');
const path = 'src-tauri/tauri.conf.json';
const data = JSON.parse(fs.readFileSync(path, 'utf8'));
data.bundle = data.bundle || {};
data.bundle.android = data.bundle.android || {};
data.bundle.android.versionCode = Number(process.env.VERSION_CODE);
fs.writeFileSync(path, JSON.stringify(data, null, 2) + '\n');
console.log('✅ Set versionCode to', data.bundle.android.versionCode);
"
env:
VERSION_CODE: ${{ steps.version_code.outputs.versionCode }}
- name: Update tauri.properties with correct version
run: |
VERSION="$BUILD_VERSION"
VERSION_CODE="${{ steps.version_code.outputs.versionCode }}"
PROPS_FILE="src-tauri/gen/android/app/tauri.properties"
echo "// THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY." > "$PROPS_FILE"
echo "tauri.android.versionName=$VERSION" >> "$PROPS_FILE"
echo "tauri.android.versionCode=$VERSION_CODE" >> "$PROPS_FILE"
echo "✅ Updated tauri.properties: versionName=$VERSION, versionCode=$VERSION_CODE"
cat "$PROPS_FILE"
- name: Setup Java
uses: actions/setup-java@03ad4de0992f5dab5e18fcb136590ce7c4a0ac95 # v5
with:
distribution: 'temurin'
java-version: '17'
- name: Setup Android SDK
uses: android-actions/setup-android@9fc6c4e9069bf8d3d10b2204b1fb8f6ef7065407 # v3.2.2
with:
api-level: 36
build-tools: 36.0.0
ndk-version: 26.1.10909125
- name: Export Android SDK/NDK environment
run: |
echo "ANDROID_SDK_ROOT=$ANDROID_SDK_ROOT" >> $GITHUB_ENV
echo "ANDROID_HOME=$ANDROID_SDK_ROOT" >> $GITHUB_ENV
NDK_PATH="$ANDROID_SDK_ROOT/ndk/26.1.10909125"
if [ ! -d "$NDK_PATH" ]; then
NDK_PATH=$(ls -d "$ANDROID_SDK_ROOT/ndk/"* | head -1)
fi
echo "NDK_HOME=$NDK_PATH" >> $GITHUB_ENV
echo "ANDROID_NDK_HOME=$NDK_PATH" >> $GITHUB_ENV
echo "Using ANDROID_SDK_ROOT=$ANDROID_SDK_ROOT"
echo "Using NDK at $NDK_PATH"
- name: Install Rust stable toolchain
uses: actions-rust-lang/setup-rust-toolchain@166cdcfd11aee3cb47222f9ddb555ce30ddb9659 # v1
with:
toolchain: stable
cache: false
rustflags: ''
- name: Cache Cargo registry and build
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: |
~/.cargo/registry
~/.cargo/git
src-tauri/target
key: ${{ runner.os }}-cargo-android-${{ hashFiles('src-tauri/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-android-
${{ runner.os }}-cargo-
- name: Install Android targets
run: |
rustup target add aarch64-linux-android
rustup target add armv7-linux-androideabi
rustup target add i686-linux-android
rustup target add x86_64-linux-android
- name: Setup Android signing
env:
ANDROID_UPLOAD_KEY: ${{ secrets.ANDROID_UPLOAD_KEY }}
ANDROID_UPLOAD_KEY_ALIAS: ${{ secrets.ANDROID_UPLOAD_KEY_ALIAS }}
ANDROID_UPLOAD_KEY_PASSWORD: ${{ secrets.ANDROID_UPLOAD_KEY_PASSWORD }}
run: |
# Validate required secrets
if [ -z "$ANDROID_UPLOAD_KEY" ] || [ -z "$ANDROID_UPLOAD_KEY_ALIAS" ] || [ -z "$ANDROID_UPLOAD_KEY_PASSWORD" ]; then
echo "❌ Required Android secrets are not set"
exit 1
fi
# Create keystore directory
mkdir -p ~/.android
# Decode and install keystore
CLEAN_KEY=$(echo "$ANDROID_UPLOAD_KEY" | tr -d '\n\r\t ' | tr -d '[:space:]')
echo "$CLEAN_KEY" | base64 --decode > ~/.android/upload-keystore.jks
if [ ! -s ~/.android/upload-keystore.jks ]; then
echo "❌ Failed to decode Android keystore"
exit 1
fi
# Set permissions
chmod 600 ~/.android/upload-keystore.jks
# Export environment variables for Tauri build
echo "ANDROID_KEYSTORE_PATH=~/.android/upload-keystore.jks" >> $GITHUB_ENV
echo "ANDROID_KEY_ALIAS=$ANDROID_UPLOAD_KEY_ALIAS" >> $GITHUB_ENV
echo "ANDROID_KEY_PASSWORD=$ANDROID_UPLOAD_KEY_PASSWORD" >> $GITHUB_ENV
- name: Install frontend dependencies
run: bun install --frozen-lockfile
- name: Build frontend
run: |
echo "VITE_THUNDERBOLT_CLOUD_URL: $VITE_THUNDERBOLT_CLOUD_URL"
bun run build
env:
VITE_THUNDERBOLT_CLOUD_URL: ${{ vars.VITE_THUNDERBOLT_CLOUD_URL }}
NODE_OPTIONS: '--max-old-space-size=8192'
- name: Setup Android Signing Configuration
env:
ANDROID_UPLOAD_KEY: ${{ secrets.ANDROID_UPLOAD_KEY }}
ANDROID_UPLOAD_KEY_ALIAS: ${{ secrets.ANDROID_UPLOAD_KEY_ALIAS }}
ANDROID_UPLOAD_KEY_PASSWORD: ${{ secrets.ANDROID_UPLOAD_KEY_PASSWORD }}
run: |
# Create signing.properties file for Android build
cat > src-tauri/gen/android/signing.properties << EOF
storeFile=../upload-keystore.jks
storePassword=$ANDROID_UPLOAD_KEY_PASSWORD
keyAlias=$ANDROID_UPLOAD_KEY_ALIAS
keyPassword=$ANDROID_UPLOAD_KEY_PASSWORD
EOF
# Copy keystore to Android project directory
cp ~/.android/upload-keystore.jks src-tauri/gen/android/upload-keystore.jks
- name: Build Tauri Android App
env:
ANDROID_KEYSTORE_PATH: ${{ env.ANDROID_KEYSTORE_PATH }}
ANDROID_KEY_ALIAS: ${{ env.ANDROID_KEY_ALIAS }}
ANDROID_KEY_PASSWORD: ${{ env.ANDROID_KEY_PASSWORD }}
RUSTC_WRAPPER: ''
SCCACHE_DISABLE: '1'
VITE_THUNDERBOLT_CLOUD_URL: ${{ vars.VITE_THUNDERBOLT_CLOUD_URL }}
NODE_OPTIONS: '--max-old-space-size=8192'
run: |
bun install --frozen-lockfile
bun tauri android build
- name: Find AAB file
id: find_aab
run: |
AAB_PATH=$(find src-tauri/gen/android/app/build/outputs -name "*.aab" -type f | head -1)
if [ -f "$AAB_PATH" ]; then
echo "📱 Found AAB file: $AAB_PATH"
echo "aab_path=$AAB_PATH" >> $GITHUB_OUTPUT
else
echo "❌ No AAB file found in Android build outputs directory"
echo "Available AAB files:"
find src-tauri/gen/android -name "*.aab" -type f
exit 1
fi
- name: Upload to Google Play Store
uses: ./.github/actions/upload-to-play
with:
serviceAccountJson: ${{ secrets.GOOGLE_PLAY_SERVICE_ACCOUNT_JSON }}
packageName: 'net.thunderbird.thunderbolt'
aabPath: ${{ steps.find_aab.outputs.aab_path }}
track: 'internal'
- name: Upload build artifacts (if build fails)
if: failure()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: android-build-logs-${{ github.run_id }}
path: |
src-tauri/gen/android/app/build/
src-tauri/gen/android/*.log
retention-days: 7
- name: Clean up sensitive files
if: always()
run: |
rm -f ~/.android/upload-keystore.jks
rm -f ~/google-play-service-account.json