前两天在折腾 Flutter 工作流,记录一下踩坑过程。
或许可以写一下flutter配置安卓编译全家桶? 挖个坑先
apk签名
在发布 Android 应用时,必须对 APK 进行签名,但是签名密钥(keystore)是敏感信息,不能直接放在代码库中。常见的做法是将 keystore 文件进行 Base64 编码,然后作为 GitHub Secrets 存储在仓库中。在工作流中,可以通过解码该 Secret 并写入到文件系统来使用。
存储keystore 信息到 GitHub Secrets
需要在 GitHub 仓库的 Settings -> Secrets and variables -> Actions 中添加以下 Secrets
KEYSTORE_BASE64:Base64 编码后的 keystore 文件内容。KEY_STORE_PASSWORD:keystore 的密码。KEY_ALIAS:密钥别名。KEY_PASSWORD:密钥的密码。
在工作流中使用keystore 信息进行签名
并将密钥库信息写入android/key.properties 文件,工作流中的代码如下所示:
1
2
3
4
5
6
7
8
9
| - name: Create key.properties
run: |
# 创建 key.properties 文件
echo "storePassword=${{ secrets.KEY_STORE_PASSWORD }}" >> android/key.properties
echo "keyPassword=${{ secrets.KEY_PASSWORD }}" >> android/key.properties
echo "keyAlias=${{ secrets.KEY_ALIAS }}" >> android/key.properties
echo "storeFile=ghaction-keystore.jks" >> android/key.properties
# 解码 keystore 并保存到指定路径
echo "${{ secrets.KEYSTORE_BASE64 }}" | base64 --decode > android/app/ghaction-keystore.jks
|
配置 Android 项目使用签名
在 android/app/build.gradle(.kts) 文件中引用 key.properties 文件,以便在构建时使用正确的签名配置。
1
2
3
4
5
6
7
8
9
10
11
12
13
| val keystorePropertiesFile = rootProject.file("key.properties")
android {
...
signingConfigs {
create("release") {
keyAlias = keystoreProperties["keyAlias"] as String
keyPassword = keystoreProperties["keyPassword"] as String
storeFile = keystoreProperties["storeFile"]?.let { file(it) }
storePassword = keystoreProperties["storePassword"] as String
}
}
...
}
|
自动创建pre-release
在使用 actions/create-release@v1 和 actions/upload-artifact@v4 时遇到了问题,
上传被拒绝,提示Error: Resource not accessible by integration github action 。
因为配置了精细权限(permissions),所以需要确保工作流有足够的权限来创建发布和上传资产,但依旧失败。
解决方法是使用 softprops/action-gh-release@v1,它可以更好地处理权限问题。
编译缓存优化
Flutter 项目的编译时间较长,尤其是首次构建时。每次workflow运行时都需要重新下载和配置Flutter SDK、Android SDK等,导致整体构建时间过长。因此,需要对编译cache进行优化。
可以使用 actions/cache@v4 来缓存 Gradle 依赖和 Android 构建输出,从而加快后续构建速度。
最后效果非常好,Build APK的时间从11分钟(首次编译)缩短到了1分钟左右(使用缓存)。
完整工作流配置
点击展开查看完整工作流配置
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
| name: Build and Release
permissions:
contents: write
pull-requests: read
on:
push:
tags:
- "*"
workflow_dispatch:
jobs:
build-android:
runs-on: ubuntu-latest
steps:
# 检出代码
- uses: actions/checkout@v3
# 设置 Java 环境
- name: Setup Java
uses: actions/setup-java@v3
with:
distribution: "zulu"
java-version: "17"
# 设置 Flutter 环境
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
channel: "stable"
cache: true
# 缓存 Gradle 依赖
- name: Cache Gradle dependencies
uses: actions/cache@v4
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-
# 缓存 Android 构建输出(包括 NDK、CMake 编译产物等)
- name: Cache Android build
uses: actions/cache@v4
with:
path: |
build
android/.gradle
android/app/build
~/.android/build-cache
key: ${{ runner.os }}-android-build-${{ hashFiles('**/*.dart', 'pubspec.yaml', 'android/**/*.gradle*') }}
restore-keys: |
${{ runner.os }}-android-build-
# 设置 Android SDK 环境并预安装组件
- name: Setup Android SDK components
run: |
export ANDROID_SDK_ROOT=$ANDROID_HOME
export PATH=$PATH:$ANDROID_SDK_ROOT/cmdline-tools/latest/bin:$ANDROID_SDK_ROOT/platform-tools
yes | sdkmanager --licenses || true
sdkmanager --install "platforms;android-33" "build-tools;33.0.2" "cmake;3.22.1" "ndk;26.1.10909125" || true
# 获取依赖
- name: Get dependencies
run: flutter pub get
# 构建 APK
- name: Create key.properties
run: |
echo "storePassword=${{ secrets.KEY_STORE_PASSWORD }}" >> android/key.properties
echo "keyPassword=${{ secrets.KEY_PASSWORD }}" >> android/key.properties
echo "keyAlias=${{ secrets.KEY_ALIAS }}" >> android/key.properties
echo "storeFile=ghaction-keystore.jks" >> android/key.properties
echo "${{ secrets.KEYSTORE_BASE64 }}" | base64 --decode > android/app/ghaction-keystore.jks
- name: Build APK
run: flutter build apk --split-per-abi --release
- name: Build Universal APK
run: flutter build apk --release
- name: Upload Android artifacts
uses: actions/upload-artifact@v4
with:
name: android-build
path: |
build/app/outputs/flutter-apk/app-release.apk
build/app/outputs/flutter-apk/app-armeabi-v7a-release.apk
build/app/outputs/flutter-apk/app-arm64-v8a-release.apk
build/app/outputs/flutter-apk/app-x86_64-release.apk
create-release:
needs: build-android
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
with:
fetch-depth: 0
# 下载构建产物
- name: Download artifacts
uses: actions/download-artifact@v4
with:
path: ./dist/
# 提取 tag 名称
- name: Extract tag name
id: extract_tag
run: |
TAG_NAME=${GITHUB_REF#refs/tags/}
echo "tag_name=$TAG_NAME" >> $GITHUB_ENV
echo "Release tag: $TAG_NAME"
# 生成 Changelog
- name: Generate Changelog
id: changelog
run: |
# 获取当前 tag
CURRENT_TAG="${{ env.tag_name }}"
# 获取上一个 tag
PREVIOUS_TAG=$(git tag --sort=-v:refname | grep -A1 "^${CURRENT_TAG}$" | tail -n1)
echo "Current tag: $CURRENT_TAG"
echo "Previous tag: $PREVIOUS_TAG"
# 如果没有上一个 tag,则从第一个 commit 开始
if [ -z "$PREVIOUS_TAG" ] || [ "$PREVIOUS_TAG" == "$CURRENT_TAG" ]; then
echo "No previous tag found, generating changelog from first commit"
CHANGELOG=$(git log --pretty=format:"- %s (%h)" --no-merges)
else
# 生成两个 tag 之间的 changelog
CHANGELOG=$(git log ${PREVIOUS_TAG}..${CURRENT_TAG} --pretty=format:"- %s (%h)" --no-merges)
fi
# 如果 changelog 为空
if [ -z "$CHANGELOG" ]; then
CHANGELOG="No changes"
fi
# 保存到文件
echo "## What's Changed" > changelog.md
echo "" >> changelog.md
echo "$CHANGELOG" >> changelog.md
echo "" >> changelog.md
echo "**Full Changelog**: https://github.com/${{ github.repository }}/compare/${PREVIOUS_TAG}...${CURRENT_TAG}" >> changelog.md
# 输出 changelog 用于 release
cat changelog.md
# 将 changelog 设置为输出(处理多行)
echo "changelog<<EOF" >> $GITHUB_OUTPUT
cat changelog.md >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
# 创建 GitHub Release 并上传 APK 文件
- name: Create Release and Upload Assets
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ env.tag_name }}
name: ${{ env.tag_name }}
body: ${{ steps.changelog.outputs.changelog }}
draft: false
prerelease: false
files: |
./dist/android-build/app-release.apk
./dist/android-build/app-armeabi-v7a-release.apk
./dist/android-build/app-arm64-v8a-release.apk
./dist/android-build/app-x86_64-release.apk
|
IIIA
IIIA 2
AI参与制作
人类主导,AI用于提升效果
?