Skip to content

Commit e7687bf

Browse files
修复 CI 签名构建与发布校验
1 parent 967c2ba commit e7687bf

2 files changed

Lines changed: 90 additions & 16 deletions

File tree

.github/workflows/android-build.yml

Lines changed: 64 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ jobs:
2323
build:
2424
name: Build AAB & APK
2525
runs-on: ubuntu-latest
26+
outputs:
27+
signing_enabled: ${{ steps.signing.outputs.enabled }}
28+
build_mode: ${{ steps.build_mode.outputs.mode }}
2629

2730
steps:
2831
- name: Checkout
@@ -58,22 +61,54 @@ jobs:
5861
run: ./gradlew lint
5962
continue-on-error: true
6063

64+
- name: Check release signing secrets
65+
id: signing
66+
env:
67+
KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }}
68+
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
69+
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
70+
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
71+
run: |
72+
if [[ -n "$KEYSTORE_BASE64" && -n "$KEYSTORE_PASSWORD" && -n "$KEY_ALIAS" && -n "$KEY_PASSWORD" ]]; then
73+
echo "enabled=true" >> "$GITHUB_OUTPUT"
74+
else
75+
echo "enabled=false" >> "$GITHUB_OUTPUT"
76+
fi
77+
78+
- name: Resolve build mode
79+
id: build_mode
80+
run: |
81+
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
82+
echo "mode=debug" >> "$GITHUB_OUTPUT"
83+
elif [[ "${{ github.event_name }}" == "workflow_dispatch" && "${{ github.event.inputs.build_type }}" == "debug" ]]; then
84+
echo "mode=debug" >> "$GITHUB_OUTPUT"
85+
elif [[ "${{ steps.signing.outputs.enabled }}" == "true" ]]; then
86+
echo "mode=release" >> "$GITHUB_OUTPUT"
87+
else
88+
echo "mode=debug" >> "$GITHUB_OUTPUT"
89+
fi
90+
91+
- name: Validate release signing secrets
92+
if: (startsWith(github.ref, 'refs/tags/v') || (github.event_name == 'workflow_dispatch' && github.event.inputs.build_type == 'release')) && steps.signing.outputs.enabled != 'true'
93+
run: |
94+
echo "::error::Release builds require KEYSTORE_BASE64, KEYSTORE_PASSWORD, KEY_ALIAS and KEY_PASSWORD secrets."
95+
exit 1
96+
6197
- name: Build Debug AAB
62-
if: github.event_name == 'pull_request' || (github.event_name == 'workflow_dispatch' && github.event.inputs.build_type == 'debug')
98+
if: steps.build_mode.outputs.mode == 'debug'
6399
run: ./gradlew bundleDebug
64100

65101
- name: Build Debug APK
66-
if: github.event_name == 'pull_request' || (github.event_name == 'workflow_dispatch' && github.event.inputs.build_type == 'debug')
102+
if: steps.build_mode.outputs.mode == 'debug'
67103
run: ./gradlew assembleDebug
68104

69105
- name: Decode Keystore
70-
if: github.event_name != 'pull_request' && (github.event_name != 'workflow_dispatch' || github.event.inputs.build_type == 'release')
106+
if: steps.build_mode.outputs.mode == 'release'
71107
run: |
72108
echo "${{ secrets.KEYSTORE_BASE64 }}" | base64 -d > app/keystore.jks
73-
continue-on-error: true
74109
75110
- name: Build Release AAB
76-
if: github.event_name != 'pull_request' && (github.event_name != 'workflow_dispatch' || github.event.inputs.build_type == 'release')
111+
if: steps.build_mode.outputs.mode == 'release'
77112
run: ./gradlew bundleRelease
78113
env:
79114
KEYSTORE_FILE: ${{ github.workspace }}/app/keystore.jks
@@ -82,7 +117,7 @@ jobs:
82117
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
83118

84119
- name: Build Release APK
85-
if: github.event_name != 'pull_request' && (github.event_name != 'workflow_dispatch' || github.event.inputs.build_type == 'release')
120+
if: steps.build_mode.outputs.mode == 'release'
86121
run: ./gradlew assembleRelease
87122
env:
88123
KEYSTORE_FILE: ${{ github.workspace }}/app/keystore.jks
@@ -93,15 +128,15 @@ jobs:
93128
- name: Upload AAB
94129
uses: actions/upload-artifact@v4
95130
with:
96-
name: app-bundle-${{ github.event_name == 'pull_request' && 'debug' || github.event.inputs.build_type || 'release' }}
131+
name: app-bundle-${{ steps.build_mode.outputs.mode }}
97132
path: app/build/outputs/bundle/**/*.aab
98133
retention-days: 30
99134
if: always()
100135

101136
- name: Upload APK
102137
uses: actions/upload-artifact@v4
103138
with:
104-
name: app-apk-${{ github.event_name == 'pull_request' && 'debug' || github.event.inputs.build_type || 'release' }}
139+
name: app-apk-${{ steps.build_mode.outputs.mode }}
105140
path: app/build/outputs/apk/**/*.apk
106141
retention-days: 14
107142
if: always()
@@ -118,19 +153,38 @@ jobs:
118153
name: Deploy to Play Store
119154
needs: build
120155
runs-on: ubuntu-latest
121-
if: startsWith(github.ref, 'refs/tags/v')
156+
if: startsWith(github.ref, 'refs/tags/v') && needs.build.outputs.signing_enabled == 'true'
122157

123158
steps:
124159
- name: Download AAB
125160
uses: actions/download-artifact@v4
126161
with:
127162
name: app-bundle-release
163+
path: release
164+
165+
- name: Validate Play Store secrets
166+
env:
167+
PLAY_STORE_SERVICE_ACCOUNT: ${{ secrets.PLAY_STORE_SERVICE_ACCOUNT }}
168+
PACKAGE_NAME: ${{ secrets.PACKAGE_NAME }}
169+
run: |
170+
if [[ -z "$PLAY_STORE_SERVICE_ACCOUNT" || -z "$PACKAGE_NAME" ]]; then
171+
echo "::error::Play Store deployment requires PLAY_STORE_SERVICE_ACCOUNT and PACKAGE_NAME secrets."
172+
exit 1
173+
fi
174+
175+
- name: Validate release AAB
176+
run: |
177+
find release -name '*.aab' -print
178+
if ! find release -name '*.aab' -print -quit | grep -q .; then
179+
echo "::error::No release AAB found in downloaded artifact."
180+
exit 1
181+
fi
128182
129183
- name: Deploy to Play Store
130184
uses: r0adkll/upload-google-play@v1
131185
with:
132186
serviceAccountJsonPlainText: ${{ secrets.PLAY_STORE_SERVICE_ACCOUNT }}
133187
packageName: ${{ secrets.PACKAGE_NAME }}
134-
releaseFiles: release/*.aab
188+
releaseFiles: release/**/*.aab
135189
track: internal
136190
status: completed

app/build.gradle.kts

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,19 +33,39 @@ plugins {
3333
alias(libs.plugins.kotlin.compose)
3434
}
3535

36+
// 校验发布签名环境,避免空密钥或缺失密码时误用 release 签名。
37+
fun hasReleaseSigningEnv(): Boolean {
38+
val keystoreFile = System.getenv("KEYSTORE_FILE")
39+
val keystorePassword = System.getenv("KEYSTORE_PASSWORD")
40+
val keyAlias = System.getenv("KEY_ALIAS")
41+
val keyPassword = System.getenv("KEY_PASSWORD")
42+
return !keystoreFile.isNullOrBlank() &&
43+
!keystorePassword.isNullOrBlank() &&
44+
!keyAlias.isNullOrBlank() &&
45+
!keyPassword.isNullOrBlank() &&
46+
file(keystoreFile).isFile
47+
}
48+
3649
android {
3750
namespace = "com.google.services"
3851
compileSdk = 36
3952
ndkVersion = "27.2.12479018"
4053

54+
val releaseSigningEnabled = hasReleaseSigningEnv()
55+
logger.lifecycle(
56+
"KeepLiveService release signing: ${
57+
if (releaseSigningEnabled) "enabled" else "disabled, fallback to debug signing"
58+
}"
59+
)
60+
4161
signingConfigs {
4262
create("release") {
4363
val keystoreFile = System.getenv("KEYSTORE_FILE")
44-
if (keystoreFile != null && file(keystoreFile).exists()) {
64+
if (releaseSigningEnabled && !keystoreFile.isNullOrBlank()) {
4565
storeFile = file(keystoreFile)
46-
storePassword = System.getenv("KEYSTORE_PASSWORD") ?: ""
47-
keyAlias = System.getenv("KEY_ALIAS") ?: ""
48-
keyPassword = System.getenv("KEY_PASSWORD") ?: ""
66+
storePassword = System.getenv("KEYSTORE_PASSWORD")
67+
keyAlias = System.getenv("KEY_ALIAS")
68+
keyPassword = System.getenv("KEY_PASSWORD")
4969
}
5070
}
5171
}
@@ -87,8 +107,8 @@ android {
87107
"proguard-rules.pro"
88108
)
89109

90-
// CI 环境使用环境变量签名,本地开发使用 debug 签名
91-
signingConfig = if (System.getenv("KEYSTORE_FILE") != null) {
110+
// CI 环境签名参数完整时使用 release 签名,本地开发和缺少参数时使用 debug 签名
111+
signingConfig = if (releaseSigningEnabled) {
92112
signingConfigs.getByName("release")
93113
} else {
94114
signingConfigs.getByName("debug")

0 commit comments

Comments
 (0)