Skip to content

Commit 185d179

Browse files
authored
更新 android-apk.yml
1 parent 495a58b commit 185d179

1 file changed

Lines changed: 110 additions & 111 deletions

File tree

.github/workflows/android-apk.yml

Lines changed: 110 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Build Android APK
1+
Name: Build Android APK
22

33
on:
44
workflow_dispatch:
@@ -33,45 +33,45 @@ jobs:
3333

3434
steps:
3535
- name: Checkout
36-
uses: actions/checkout@v5
36+
uses: actions/checkout@v4
37+
38+
- name: Setup Java
39+
uses: actions/setup-java@v4
40+
with:
41+
distribution: 'zulu'
42+
java-version: '17'
3743

3844
- name: Setup Flutter
3945
uses: subosito/flutter-action@v2
4046
with:
4147
channel: stable
4248
cache: true
4349

44-
- name: Create Flutter skeleton (Android only)
50+
- name: Create Flutter skeleton
4551
run: |
52+
# 每次全新生成项目结构
4653
flutter create --platforms=android --org com.linkweb linkweb_build
4754
4855
- name: Copy sources into skeleton
4956
run: |
57+
# 将你的代码覆盖进去
5058
rsync -a app/ linkweb_build/
5159
52-
- name: Debug - Check file structure
53-
run: |
54-
echo "📂 Checking Android build files:"
55-
find linkweb_build/android -name "build.gradle*" -type f
56-
echo ""
57-
echo "📂 App directory structure:"
58-
ls -la linkweb_build/android/app/ || true
59-
6060
- name: Force Android minSdk = 24
6161
run: |
6262
set -euo pipefail
6363
GROOVY="linkweb_build/android/app/build.gradle"
6464
KTS="linkweb_build/android/app/build.gradle.kts"
6565
6666
if [ -f "$GROOVY" ]; then
67-
echo "✅ Found build.gradle"
67+
echo "✅ Found build.gradle (Groovy)"
6868
sed -i -E 's/minSdkVersion[[:space:]]+[^[:space:]]+/minSdkVersion 24/' "$GROOVY"
6969
elif [ -f "$KTS" ]; then
70-
echo "✅ Found build.gradle.kts"
70+
echo "✅ Found build.gradle.kts (Kotlin)"
71+
# Kotlin DSL 语法稍有不同
7172
sed -i -E 's/minSdk[[:space:]]*=[[:space:]]*[^[:space:]]+/minSdk = 24/' "$KTS"
7273
else
73-
echo "❌ No Gradle file found, checking all locations:"
74-
find linkweb_build/android -maxdepth 5 -type f -name "build.gradle*"
74+
echo "❌ No Gradle file found"
7575
exit 1
7676
fi
7777
@@ -94,6 +94,7 @@ jobs:
9494
with open(manifest_path, 'r', encoding='utf-8') as f:
9595
content = f.read()
9696
97+
# 替换 label
9798
content = re.sub(
9899
r'android:label="[^"]*"',
99100
f'android:label="{app_name}"',
@@ -102,105 +103,127 @@ jobs:
102103
103104
with open(manifest_path, 'w', encoding='utf-8') as f:
104105
f.write(content)
105-
106106
print(f"✅ App name set to: {app_name}")
107107
except Exception as e:
108-
print(f"❌ Error: {e}")
108+
print(f"❌ Error setting app name: {e}")
109109
exit(1)
110110
PYTHON_SCRIPT
111111
112-
- name: Verify Manifest
113-
run: |
114-
echo "📄 Checking AndroidManifest.xml:"
115-
grep "android:label" linkweb_build/android/app/src/main/AndroidManifest.xml || true
116-
117112
- name: Prepare Keystore
118113
run: |
119114
set -euo pipefail
120115
121-
[ -n "${{ secrets.ANDROID_KEYSTORE_BASE64 }}" ] || { echo "❌ Missing ANDROID_KEYSTORE_BASE64"; exit 1; }
122-
[ -n "${{ secrets.ANDROID_KEYSTORE_PASSWORD }}" ] || { echo "❌ Missing ANDROID_KEYSTORE_PASSWORD"; exit 1; }
123-
[ -n "${{ secrets.ANDROID_KEY_ALIAS }}" ] || { echo "❌ Missing ANDROID_KEY_ALIAS"; exit 1; }
124-
[ -n "${{ secrets.ANDROID_KEY_PASSWORD }}" ] || { echo "❌ Missing ANDROID_KEY_PASSWORD"; exit 1; }
125-
126-
echo "${{ secrets.ANDROID_KEYSTORE_BASE64 }}" | base64 -d > keystore.jks
127-
KEYSTORE_PATH="$(realpath keystore.jks)"
128-
129-
if [ ! -f "$KEYSTORE_PATH" ] || [ ! -s "$KEYSTORE_PATH" ]; then
130-
echo "❌ Keystore file is empty or invalid"
131-
ls -lh keystore.jks || true
116+
# 检查 Secrets 是否存在
117+
if [ -z "${{ secrets.ANDROID_KEYSTORE_BASE64 }}" ]; then
118+
echo "❌ Error: ANDROID_KEYSTORE_BASE64 secret is missing."
132119
exit 1
133120
fi
134121
135-
KEYSTORE_SIZE=$(stat -c%s "$KEYSTORE_PATH" 2>/dev/null || stat -f%z "$KEYSTORE_PATH" 2>/dev/null || echo "unknown")
136-
echo "✅ Keystore size: ${KEYSTORE_SIZE} bytes"
122+
# 解码 Keystore
123+
echo "${{ secrets.ANDROID_KEYSTORE_BASE64 }}" | base64 -d > keystore.jks
124+
KEYSTORE_PATH="$(realpath keystore.jks)"
137125
126+
# 创建 key.properties 供 Gradle 读取
138127
cat > linkweb_build/android/key.properties << EOF
139128
storePassword=${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
140129
keyPassword=${{ secrets.ANDROID_KEY_PASSWORD }}
141130
keyAlias=${{ secrets.ANDROID_KEY_ALIAS }}
142131
storeFile=$KEYSTORE_PATH
143132
EOF
144133
145-
echo "✅ Keystore prepared at: $KEYSTORE_PATH"
146-
echo "🔑 Key alias: ${{ secrets.ANDROID_KEY_ALIAS }}"
134+
echo "✅ Keystore prepared."
147135
148-
- name: Configure Gradle for signing
136+
- name: Configure Gradle for signing (Fixed for Kotlin DSL)
149137
run: |
150138
set -euo pipefail
151139
152-
# 查找 build.gradle 文件
153140
GROOVY="linkweb_build/android/app/build.gradle"
154141
KTS="linkweb_build/android/app/build.gradle.kts"
155142
156-
if [ -f "$GROOVY" ]; then
157-
BUILD_FILE="$GROOVY"
158-
echo "✅ Using build.gradle"
159-
elif [ -f "$KTS" ]; then
160-
BUILD_FILE="$KTS"
161-
echo "✅ Using build.gradle.kts"
162-
else
163-
echo "❌ No build.gradle file found!"
164-
echo "Searching for gradle files..."
165-
find linkweb_build/android -name "build.gradle*" -type f
166-
exit 1
167-
fi
143+
# === 分支 1: 处理 Kotlin DSL (新版 Flutter 默认) ===
144+
if [ -f "$KTS" ]; then
145+
echo "✅ Detected Kotlin DSL (build.gradle.kts)"
146+
147+
# 1. 在文件头部添加 Keystore 加载逻辑
148+
cat > /tmp/header.txt << 'EOF'
149+
import java.io.FileInputStream
150+
import java.util.Properties
151+
152+
val keystoreProperties = Properties()
153+
val keystorePropertiesFile = rootProject.file("key.properties")
154+
if (keystorePropertiesFile.exists()) {
155+
keystoreProperties.load(FileInputStream(keystorePropertiesFile))
156+
}
157+
158+
EOF
159+
cat "$KTS" >> /tmp/header.txt
160+
mv /tmp/header.txt "$KTS"
161+
162+
# 2. 使用 Python 注入 signingConfigs
163+
python3 << 'PYTHON_SCRIPT'
164+
import re
168165
169-
# 检查是否已配置签名
170-
if grep -q "signingConfigs" "$BUILD_FILE"; then
171-
echo "⚠️ Signing config already exists, skipping..."
172-
exit 0
173-
fi
166+
gradle_file = 'linkweb_build/android/app/build.gradle.kts'
174167
175-
# 为 build.gradle 添加签名配置
176-
if [ -f "$GROOVY" ]; then
177-
# 创建备份
178-
cp "$GROOVY" "${GROOVY}.backup"
168+
with open(gradle_file, 'r') as f:
169+
content = f.read()
170+
171+
# Kotlin 格式的签名配置
172+
signing_config_block = '''
173+
signingConfigs {
174+
create("release") {
175+
keyAlias = keystoreProperties["keyAlias"] as String
176+
keyPassword = keystoreProperties["keyPassword"] as String
177+
storeFile = file(keystoreProperties["storeFile"] as String)
178+
storePassword = keystoreProperties["storePassword"] as String
179+
}
180+
}
181+
'''
182+
183+
# 插入 signingConfigs (在 buildTypes 之前)
184+
if "signingConfigs {" not in content:
185+
content = re.sub(
186+
r'(\s+buildTypes\s*\{)',
187+
signing_config_block + r'\1',
188+
content
189+
)
190+
191+
# 应用签名配置到 release 构建类型
192+
# 查找 buildTypes { release { ... } } 结构并注入
193+
content = re.sub(
194+
r'(buildTypes\s*\{[\s\S]*?release\s*\{)',
195+
r'\1\n signingConfig = signingConfigs.getByName("release")',
196+
content
197+
)
198+
199+
with open(gradle_file, 'w') as f:
200+
f.write(content)
201+
print("✅ Injected signing config into build.gradle.kts")
202+
PYTHON_SCRIPT
203+
204+
# === 分支 2: 处理 Groovy DSL (旧版 Flutter) ===
205+
elif [ -f "$GROOVY" ]; then
206+
echo "✅ Detected Groovy DSL (build.gradle)"
179207
180-
# 在文件开头添加 keystore 属性加载代码
208+
# 加载 Properties
181209
cat > /tmp/prepend.txt << 'GRADLE_HEADER'
182210
def keystoreProperties = new Properties()
183211
def keystorePropertiesFile = rootProject.file("key.properties")
184212
if (keystorePropertiesFile.exists()) {
185213
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
186214
}
187-
188215
GRADLE_HEADER
189216
190-
# 合并文件
191217
cat /tmp/prepend.txt "$GROOVY" > /tmp/build.gradle.new
192218
mv /tmp/build.gradle.new "$GROOVY"
193219
194-
# 在 android { 后添加 signingConfigs
220+
# 注入配置
195221
python3 << 'PYTHON_SCRIPT'
196222
import re
197-
198223
gradle_file = 'linkweb_build/android/app/build.gradle'
199-
200224
with open(gradle_file, 'r') as f:
201225
content = f.read()
202226
203-
# 在 android { 块内添加 signingConfigs
204227
signing_config = '''
205228
signingConfigs {
206229
release {
@@ -211,31 +234,16 @@ jobs:
211234
}
212235
}
213236
'''
214-
215-
# 在 buildTypes 之前插入 signingConfigs
216-
content = re.sub(
217-
r'(\s+buildTypes\s*\{)',
218-
signing_config + r'\1',
219-
content
220-
)
221-
222-
# 在 release buildType 中添加 signingConfig
223-
content = re.sub(
224-
r'(release\s*\{)',
225-
r'\1\n signingConfig signingConfigs.release',
226-
content
227-
)
228-
237+
content = re.sub(r'(\s+buildTypes\s*\{)', signing_config + r'\1', content)
238+
content = re.sub(r'(release\s*\{)', r'\1\n signingConfig signingConfigs.release', content)
229239
with open(gradle_file, 'w') as f:
230240
f.write(content)
231-
232-
print("✅ Gradle signing configuration added")
233241
PYTHON_SCRIPT
234242
243+
else
244+
echo "❌ No Gradle file found!"
245+
exit 1
235246
fi
236-
237-
echo "📄 Checking signing config:"
238-
grep -A 8 "signingConfigs" "$BUILD_FILE" || echo "⚠️ Could not verify signingConfigs"
239247
240248
- name: Get dependencies
241249
working-directory: linkweb_build
@@ -253,13 +261,13 @@ jobs:
253261
- name: Verify APK signature
254262
run: |
255263
echo "🔍 Verifying APK signatures..."
256-
for apk in linkweb_build/build/app/outputs/flutter-apk/app-*-release.apk; do
257-
if [ -f "$apk" ]; then
258-
echo "Checking: $(basename "$apk")"
259-
jarsigner -verify -verbose -certs "$apk" 2>&1 | head -20 || true
260-
echo "---"
261-
fi
262-
done
264+
# 只要验证一个就能确认签名是否生效
265+
APK_FILE=$(find linkweb_build/build/app/outputs/flutter-apk/ -name "app-*-release.apk" | head -n 1)
266+
if [ -f "$APK_FILE" ]; then
267+
echo "Checking: $APK_FILE"
268+
# 检查是否包含你的 Alias 名称 (grep 搜索)
269+
jarsigner -verify -verbose -certs "$APK_FILE" | grep "X.509" -A 2 || true
270+
fi
263271
264272
- name: Rename and collect APKs
265273
run: |
@@ -268,16 +276,14 @@ jobs:
268276
269277
VERSION="${{ github.event.inputs.version_name }}"
270278
APP_NAME="${{ github.event.inputs.app_name }}"
271-
# 移除特殊字符,保留字母数字、下划线和连字符
279+
# 清理文件名
272280
SAFE_APP_NAME=$(echo "$APP_NAME" | sed 's/[^[:alnum:]_-]//g')
273281
[ -z "$SAFE_APP_NAME" ] && SAFE_APP_NAME="LinkWeb"
274282
275-
echo "📦 Collecting APKs with name: ${SAFE_APP_NAME}"
283+
echo "📦 Collecting APKs..."
276284
277285
for apk in linkweb_build/build/app/outputs/flutter-apk/app-*-release.apk; do
278-
if [ ! -f "$apk" ]; then
279-
continue
280-
fi
286+
[ -f "$apk" ] || continue
281287
282288
basename=$(basename "$apk")
283289
if [[ $basename == *"arm64-v8a"* ]]; then
@@ -291,37 +297,30 @@ jobs:
291297
fi
292298
done
293299
294-
echo "✅ APKs ready:"
295300
ls -lh dist/
296301
297302
- name: Upload APKs as artifact
298303
uses: actions/upload-artifact@v4
299304
with:
300305
name: APKs-${{ github.run_number }}-v${{ github.event.inputs.version_name }}
301306
path: dist/*.apk
302-
if-no-files-found: error
303307

304308
- name: Create GitHub Release
305309
if: github.event.inputs.upload_to_release == 'true'
306310
env:
307311
GH_TOKEN: ${{ github.token }}
308312
run: |
309313
set -euo pipefail
310-
311314
TAG="v${{ github.event.inputs.version_name }}"
312315
316+
# 检查 Tag 是否存在
313317
if gh release view "$TAG" >/dev/null 2>&1; then
314-
echo "Release $TAG already exists, updating..."
315-
gh release edit "$TAG" \
316-
--title "$TAG - ${{ github.event.inputs.app_name }}" \
317-
--notes "${{ github.event.inputs.release_notes }}"
318+
echo "Updating existing release $TAG"
319+
gh release upload "$TAG" dist/*.apk --clobber
318320
else
319321
echo "Creating new release $TAG"
320322
gh release create "$TAG" \
321323
--title "$TAG - ${{ github.event.inputs.app_name }}" \
322-
--notes "${{ github.event.inputs.release_notes }}"
324+
--notes "${{ github.event.inputs.release_notes }}" \
325+
dist/*.apk
323326
fi
324-
325-
gh release upload "$TAG" dist/*.apk --clobber
326-
327-
echo "✅ Release created/updated: https://github.com/${{ github.repository }}/releases/tag/${TAG}"

0 commit comments

Comments
 (0)