-
Notifications
You must be signed in to change notification settings - Fork 20
246 lines (212 loc) · 10.2 KB
/
main.yml
File metadata and controls
246 lines (212 loc) · 10.2 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
name: "Build"
on:
push:
branches:
- main
workflow_dispatch:
jobs:
build:
name: Build & Release
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v3
with:
fetch-depth: 2
fetch-tags: true
- name: Check if version changed
id: check_version_change
run: |
# Get current version
CURRENT_VERSION=$(grep '^version: ' pubspec.yaml | cut -d ' ' -f 2 | tr -d '\r')
echo "Current version: $CURRENT_VERSION"
# Get previous version (from previous commit) with safeguard for shallow history
if git rev-parse --quiet --verify HEAD~1 >/dev/null; then
git checkout HEAD~1 pubspec.yaml
PREVIOUS_VERSION=$(grep '^version: ' pubspec.yaml | cut -d ' ' -f 2 | tr -d '\r')
git checkout -q HEAD pubspec.yaml
echo "Previous version: $PREVIOUS_VERSION"
else
echo "No previous commit - assuming version unchanged"
PREVIOUS_VERSION=$CURRENT_VERSION
fi
# Compare versions and set output
if [ "$CURRENT_VERSION" != "$PREVIOUS_VERSION" ]; then
echo "Version changed from $PREVIOUS_VERSION to $CURRENT_VERSION"
echo "VERSION_CHANGED=true" >> $GITHUB_OUTPUT
else
echo "Version unchanged: $CURRENT_VERSION"
echo "VERSION_CHANGED=false" >> $GITHUB_OUTPUT
fi
# Always store the current version for later steps
echo "VERSION=$CURRENT_VERSION" >> $GITHUB_ENV
- name: Check if build should continue
id: should_build
run: |
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
echo "Building because workflow was manually triggered"
echo "SHOULD_BUILD=true" >> $GITHUB_OUTPUT
elif [[ "${{ steps.check_version_change.outputs.VERSION_CHANGED }}" == "true" ]]; then
echo "Building because version changed"
echo "SHOULD_BUILD=true" >> $GITHUB_OUTPUT
else
echo "Skipping build because version did not change"
echo "SHOULD_BUILD=false" >> $GITHUB_OUTPUT
fi
- name: Set Up Java
if: steps.should_build.outputs.SHOULD_BUILD == 'true'
uses: actions/setup-java@v3.12.0
with:
distribution: 'oracle'
java-version: '17'
- name: Setup Android Keystore
if: steps.should_build.outputs.SHOULD_BUILD == 'true'
run: |
# Create the keystore file from the secret
echo "${{ secrets.ANDROID_KEYSTORE_FILE }}" | base64 --decode > android/app/upload-keystore.jks
# Set environment variables for signing
echo "ANDROID_KEYSTORE_PASSWORD=${{ secrets.ANDROID_KEYSTORE_PASSWORD }}" >> $GITHUB_ENV
echo "ANDROID_KEY_PASSWORD=${{ secrets.ANDROID_KEY_PASSWORD }}" >> $GITHUB_ENV
echo "ANDROID_KEY_ALIAS=${{ secrets.ANDROID_KEY_ALIAS }}" >> $GITHUB_ENV
echo "ANDROID_KEYSTORE_FILE=upload-keystore.jks" >> $GITHUB_ENV
# Write key.properties so Gradle can pick it up without extra config
echo "storePassword=${{ secrets.ANDROID_KEYSTORE_PASSWORD }}" > android/key.properties
echo "keyPassword=${{ secrets.ANDROID_KEY_PASSWORD }}" >> android/key.properties
echo "keyAlias=${{ secrets.ANDROID_KEY_ALIAS }}" >> android/key.properties
echo "storeFile=${{ env.ANDROID_KEYSTORE_FILE }}" >> android/key.properties
- name: Set Up Flutter
if: steps.should_build.outputs.SHOULD_BUILD == 'true'
uses: subosito/flutter-action@v2
with:
flutter-version: '3.32.2'
channel: 'stable'
- name: Configure Flutter minSdkVersion
if: steps.should_build.outputs.SHOULD_BUILD == 'true'
run: |
set -euo pipefail
mkdir -p android
touch android/local.properties
if grep -qE '^flutter\.minSdkVersion=' android/local.properties; then
sed -i 's/^flutter\.minSdkVersion=.*/flutter.minSdkVersion=23/' android/local.properties
else
echo "flutter.minSdkVersion=23" >> android/local.properties
fi
echo "Resulting android/local.properties:"
sed -n '1,120p' android/local.properties
- name: Cache Dart & Pub artifacts
if: steps.should_build.outputs.SHOULD_BUILD == 'true'
uses: actions/cache@v3
with:
path: |
~/.pub-cache
.dart_tool
key: ${{ runner.os }}-dart-${{ hashFiles('**/pubspec.yaml') }}
- name: Install Dependencies
if: steps.should_build.outputs.SHOULD_BUILD == 'true'
run: flutter pub get
- name: Generate localization and other required files
if: steps.should_build.outputs.SHOULD_BUILD == 'true'
run: dart run build_runner build -d
- name: Extract version from pubspec.yaml
if: steps.should_build.outputs.SHOULD_BUILD == 'true'
id: extract_version
run: |
version=$(grep '^version: ' pubspec.yaml | cut -d ' ' -f 2 | tr -d '\r')
echo "VERSION=$version" >> $GITHUB_ENV
- name: Build APK
if: steps.should_build.outputs.SHOULD_BUILD == 'true'
run: flutter build apk --split-per-abi --dart-define=APP_VERSION=${{ env.VERSION }} --dart-define=GIT_COMMIT=${{ github.sha }}
env:
ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
ANDROID_KEYSTORE_FILE: upload-keystore.jks
- name: Build appBundle
if: steps.should_build.outputs.SHOULD_BUILD == 'true'
run: flutter build appbundle --release --dart-define=APP_VERSION=${{ env.VERSION }} --dart-define=GIT_COMMIT=${{ github.sha }}
env:
ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
ANDROID_KEYSTORE_FILE: upload-keystore.jks
- name: Rename Split APKs
if: steps.should_build.outputs.SHOULD_BUILD == 'true'
run: |
set -euo pipefail
# Rename APKs with version and architecture
mv build/app/outputs/flutter-apk/app-armeabi-v7a-release.apk \
build/app/outputs/flutter-apk/mostro-v${{ env.VERSION }}-armeabi-v7a.apk
mv build/app/outputs/flutter-apk/app-arm64-v8a-release.apk \
build/app/outputs/flutter-apk/mostro-v${{ env.VERSION }}-arm64-v8a.apk
# Verify renamed files exist
ls -lh build/app/outputs/flutter-apk/mostro-v${{ env.VERSION }}-*.apk || \
(echo "❌ Split APKs not found after rename" && exit 1)
- name: Verify APK Signing
if: steps.should_build.outputs.SHOULD_BUILD == 'true'
run: |
set -euo pipefail
SDK_ROOT="${ANDROID_HOME:-${ANDROID_SDK_ROOT:-}}"
APKSIGNER=$(find "$SDK_ROOT"/build-tools -name apksigner -type f 2>/dev/null | sort -V | tail -1 || true)
# Function to verify a single APK
verify_apk() {
local APK_PATH=$1
local APK_NAME=$2
if [ -f "$APK_PATH" ]; then
echo "✅ $APK_NAME built successfully"
echo "Verifying $APK_NAME with jarsigner..."
local SAFE_APK_NAME=$(echo "$APK_NAME" | tr ' ' '_')
jarsigner -verify -verbose -certs "$APK_PATH" > "/tmp/jarsigner-${SAFE_APK_NAME}.out"
sed -n '1,120p' "/tmp/jarsigner-${SAFE_APK_NAME}.out"
# Try apksigner if available
if [ -n "$APKSIGNER" ]; then
echo "Verifying $APK_NAME with apksigner..."
"$APKSIGNER" verify --print-certs "$APK_PATH"
# Optional: enforce signer fingerprint if provided
if [ -n "${{ secrets.ANDROID_CERT_SHA256 }}" ]; then
FPR=$("$APKSIGNER" verify --print-certs "$APK_PATH" 2>/dev/null \
| awk -F': ' '/SHA-256 digest/{print $2}' | tr -d ' ')
if [ "$FPR" != "${{ secrets.ANDROID_CERT_SHA256 }}" ]; then
echo "❌ $APK_NAME cert SHA-256 mismatch"
exit 1
fi
fi
fi
echo ""
else
echo "❌ $APK_NAME build failed - file not found: $APK_PATH"
exit 1
fi
}
# Verify both split APKs
verify_apk "build/app/outputs/flutter-apk/mostro-v${{ env.VERSION }}-armeabi-v7a.apk" "armeabi-v7a APK"
verify_apk "build/app/outputs/flutter-apk/mostro-v${{ env.VERSION }}-arm64-v8a.apk" "arm64-v8a APK"
echo "✅ All APKs verified successfully"
- name: Upload Artifacts
if: steps.should_build.outputs.SHOULD_BUILD == 'true'
uses: actions/upload-artifact@v4
with:
name: Releases
path: |
build/app/outputs/flutter-apk/mostro-v${{ env.VERSION }}-armeabi-v7a.apk
build/app/outputs/flutter-apk/mostro-v${{ env.VERSION }}-arm64-v8a.apk
build/app/outputs/bundle/release/app-release.aab
- name: Create Release
if: steps.should_build.outputs.SHOULD_BUILD == 'true'
uses: ncipollo/release-action@v1
with:
artifacts: "build/app/outputs/flutter-apk/mostro-v${{ env.VERSION }}-armeabi-v7a.apk,build/app/outputs/flutter-apk/mostro-v${{ env.VERSION }}-arm64-v8a.apk,build/app/outputs/bundle/release/app-release.aab"
tag: v${{ env.VERSION }}
allowUpdates: true
omitBodyDuringUpdate: true
replacesArtifacts: true
- name: Trigger Desktop Builds
if: steps.should_build.outputs.SHOULD_BUILD == 'true'
uses: peter-evans/repository-dispatch@v3
with:
event-type: build-desktop
client-payload: '{"version": "${{ env.VERSION }}"}'
- name: Cleanup Keystore
if: always() && steps.should_build.outputs.SHOULD_BUILD == 'true'
run: |
# Remove keystore file for security
rm -f android/app/upload-keystore.jks android/key.properties