在这个Flutter项目 https://github.com/xmaihh/FlutterHub 中使用Github Actions自动化构建(Android、iOS、Web、Linux、Windows、macOS)应用并发布到Release。
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
| ┌─────────────────┐ ┌─────────────────┐ │ 推送 v* 标签 │ │ 手动触发 │ └────────┬────────┘ └────────┬────────┘ │ │ └──────────┬──────────┘ ▼ ┌───────────────────┐ │ build_env │ │ ───────────────── │ │ 生成版本号 │ │ 更新 pubspec.yaml │ │ 上传 pubspec.yaml │ └──────────┬────────┘ │ ▼ ┌───────────────────────────────────────────────────────┐ │ build_app │ │ ┌─────────┐ ┌─────────┐ ┌───┐ ┌─────┐ ┌───┐ ┌───┐ ┌───┐│ │ │ Android │ │Android │ │Web│ │Linux│ │Win│ │iOS│ │Mac││ │ │ │ │ AAB │ │ │ │ │ │ │ │ │ │ ││ │ │1.下载 │ │1.下载 │ │ 1 │ │ 1 │ │ 1 │ │ 1 │ │ 1 ││ │ │2.设置 │ │2.设置 │ │ 2 │ │ 2 │ │ 2 │ │ 2 │ │ 2 ││ │ │3.构建 │ │3.构建 │ │ 3 │ │ 3 │ │ 3 │ │ 3 │ │ 3 ││ │ │4.压缩 │ │4.压缩 │ │ 4 │ │ 4 │ │ 4 │ │ 4 │ │ 4 ││ │ │5.上传 │ │5.上传 │ │ 5 │ │ 5 │ │ 5 │ │ 5 │ │ 5 ││ │ └─────────┘ └─────────┘ └───┘ └─────┘ └───┘ └───┘ └───┘│ └───────────────────────────┬───────────────────────────┘ │ ▼ ┌───────────────────┐ │ release │ │ ───────────────── │ │ 下载所有 artifacts │ │ 创建 GitHub Release│ │ 上传 Release 资产 │ └───────────────────┘
图例: 1 = 下载 pubspec.yaml 2 = 设置 Flutter 3 = 构建 4 = 压缩 5 = 上传 artifact
|
我的PlantUML图片:
工作流程的主要步骤:
触发条件:
工作流程包含三个主要任务:
- build_env:更新版本号
- build_app:构建多平台应用
- release:创建发布和上传构建产物
- build_env 任务:
- 生成版本号
- 更新 pubspec.yaml 中的版本号
- 上传更新后的 pubspec.yaml 文件
- build_app 任务:
- 使用矩阵策略为不同平台构建应用(Android、iOS、Web、Linux、Windows、macOS)
- 下载更新后的 pubspec.yaml
- 设置 Flutter 环境
- 安装依赖
- 根据平台执行相应的构建命令
- 压缩构建产物
- 上传构建产物作为 artifact
- release 任务:
- 下载所有构建产物
- 创建 GitHub Release
- 将构建产物上传为 Release 资产
在你的Flutter项目仓库根目录下创建一个 .github/workflows 文件夹,在该文件夹内创建一个新的 YAML 文件(例如 deploy.yml)用于定义 GitHub Actions 工作流。
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
| name: Flutter Release Build CI/CD
on: push: tags: - 'v*' workflow_dispatch:
jobs: build_env: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0
- name: Generate Version Number id: generate_version run: | # 标签触发 (e.g. v1.0或1.0) 移除 refs/tags/ 前缀和可选的 'v' 前缀 if [[ ${{ github.ref }} == refs/tags/* ]]; then TAG_VERSION=$(echo ${{ github.ref }} | sed -E 's/^refs\/tags\/(v)?//') echo "This is a tag release: $TAG_VERSION" elif [[ ${{ github.ref }} == refs/heads/* ]]; then TAG_VERSION=$(echo ${{ github.ref }} | sed -E 's/^refs\/heads\///' | sed 's/\//-/g') echo "This is a branch push: $TAG_VERSION" elif [[ ${{ github.ref }} == refs/pull/* ]]; then PR_NUMBER=$(echo ${{ github.ref }} | sed -E 's/^refs\/pull\/([0-9]+)\/merge$/\1/') TAG_VERSION="pr-$PR_NUMBER" echo "This is a Pull Request: $TAG_VERSION" else TAG_VERSION=$(echo ${{ github.ref }} | sed 's/\//-/g') echo "This is another trigger: $TAG_VERSION" fi echo "TAG_VERSION=$TAG_VERSION" COMMIT_COUNT=$(git rev-list --count HEAD) SHORT_HASH=$(git rev-parse --short HEAD) BUILD_VERSION="${COMMIT_COUNT}" echo "BUILD_VERSION=${BUILD_VERSION}" >> $GITHUB_OUTPUT echo "Generated BUILD_VERSION: ${BUILD_VERSION}" shell: bash
- name: Update version in pubspec.yaml id: update_version_in_pubspec run: | # 从pubspec.yaml中提取主版本号,(e.g. version: 1.0.0+1,提取1.0.0) MAIN_VERSION=$(grep "^version:" pubspec.yaml | sed -E 's/version: ([0-9]+\.[0-9]+\.[0-9]+).*/\1/') echo "MAIN_VERSION=${MAIN_VERSION}" FULL_VERSION="${MAIN_VERSION}+${{ steps.generate_version.outputs.BUILD_VERSION }}" echo "FULL_VERSION=${FULL_VERSION}" sed -i "s/^version: .*/version: ${FULL_VERSION}/" pubspec.yaml echo "Updated pubspec.yaml content:" cat pubspec.yaml if grep -q "^version: ${FULL_VERSION}" pubspec.yaml; then echo "Version updated successfully to ${FULL_VERSION}" else echo "Failed to update version" echo "Current version line:" grep "^version:" pubspec.yaml exit 1 fi echo "FULL_VERSION=${FULL_VERSION}" > _build_env.config shell: bash
- name: Upload updated pubspec.yaml uses: actions/upload-artifact@v4 with: name: build_env_files path: | pubspec.yaml _build_env.config
build_app: needs: build_env runs-on: ${{ matrix.os }} strategy: matrix: include: - name: android os: ubuntu-latest - name: android-aab os: ubuntu-latest - name: web os: ubuntu-latest - name: linux os: ubuntu-latest - name: windows os: windows-latest - name: ios os: macos-latest - name: macos os: macos-latest
steps: - uses: actions/checkout@v4
- name: Download updated pubspec.yaml uses: actions/download-artifact@v4 with: name: build_env_files
- name: Replace pubspec.yaml run: | ls cat pubspec.yaml pwd cat _build_env.config shell: bash
- name: Read FULL_VERSION from _build_env.config id: read_build_env run: | echo "Reading version from _build_env.config..." FULL_VERSION=$(grep 'FULL_VERSION=' _build_env.config | cut -d '=' -f2) echo "FULL_VERSION=${FULL_VERSION}" >> $GITHUB_OUTPUT # 设置输出变量 shell: bash
- name: Set up Flutter uses: subosito/flutter-action@v2 with: channel: stable
- name: Check Flutter version run: flutter --version - name: Install dependencies run: flutter pub get
- name: Build Android APK if: matrix.name == 'android' run: | flutter build apk flutter build apk --split-per-abi
- name: Build Android App Bundle if: matrix.name == 'android-aab' run: flutter build appbundle
- name: Build Web if: matrix.name == 'web' run: flutter build web --base-href=/FlutterHub/
- name: Build Linux if: matrix.name == 'linux' run: | sudo apt-get update -y sudo apt-get install -y ninja-build libgtk-3-dev flutter build linux
- name: Build Windows if: matrix.name == 'windows' run: flutter build windows
- name: Build iOS if: matrix.name == 'ios' run: flutter build ios --release --no-codesign
- name: Build macOS if: matrix.name == 'macos' run: flutter build macos
- name: Deploy to GitHub Pages if: matrix.name == 'web' uses: peaceiris/actions-gh-pages@v4 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: ./build/web publish_branch: gh-pages user_name: 'github-actions[bot]' user_email: 'github-actions[bot]@users.noreply.github.com' commit_message: 'Deploy to GitHub Pages: ${{ steps.read_build_env.outputs.FULL_VERSION }}'
- name: Compress Build run: | if [ "${{ matrix.name }}" = "android" ]; then mv build/app/outputs/flutter-apk/app-release.apk ./app-release-all-${{ steps.read_build_env.outputs.FULL_VERSION }}.apk mv build/app/outputs/flutter-apk/app-armeabi-v7a-release.apk ./app-release-armeabi-v7a-${{ steps.read_build_env.outputs.FULL_VERSION }}.apk mv build/app/outputs/flutter-apk/app-arm64-v8a-release.apk ./app-release-arm64-v8a-${{ steps.read_build_env.outputs.FULL_VERSION }}.apk mv build/app/outputs/flutter-apk/app-x86_64-release.apk ./app-release-x86_64-${{ steps.read_build_env.outputs.FULL_VERSION }}.apk elif [ "${{ matrix.name }}" = "android-aab" ]; then mv build/app/outputs/bundle/release/app-release.aab ./app-release-${{ steps.read_build_env.outputs.FULL_VERSION }}.aab elif [ "${{ matrix.name }}" = "web" ]; then zip -r web-release-${{ steps.read_build_env.outputs.FULL_VERSION }}.zip build/web elif [ "${{ matrix.name }}" = "linux" ]; then zip -r linux-release-${{ steps.read_build_env.outputs.FULL_VERSION }}.zip build/linux/x64/release/bundle elif [ "${{ matrix.name }}" = "windows" ]; then powershell Compress-Archive build/windows/x64/runner/Release windows-release-${{ steps.read_build_env.outputs.FULL_VERSION }}.zip elif [ "${{ matrix.name }}" = "ios" ]; then mkdir Payload cp -r build/ios/iphoneos/Runner.app Payload/ zip -r ios-release-${{ steps.read_build_env.outputs.FULL_VERSION }}.ipa Payload/ zip -r ios-release-${{ steps.read_build_env.outputs.FULL_VERSION }}.zip build/ios/iphoneos elif [ "${{ matrix.name }}" = "macos" ]; then zip -r macos-release-${{ steps.read_build_env.outputs.FULL_VERSION }}.zip build/macos/Build/Products/Release fi shell: bash
- name: Upload Build Artifact uses: actions/upload-artifact@v4 with: name: ${{ matrix.name }}-artifact path: | *.apk *.aab *.ipa *.zip if-no-files-found: error
release: needs: build_app runs-on: ubuntu-latest steps: - name: Download all artifacts uses: actions/download-artifact@v4 with: path: artifacts - name: Display structure of downloaded files run: ls -R artifacts - name: Read FULL_VERSION from _build_env.config id: read_build_env run: | echo "Reading version from _build_env.config..." FULL_VERSION=$(grep 'FULL_VERSION=' artifacts/build_env_files/_build_env.config | cut -d '=' -f2) echo "FULL_VERSION=${FULL_VERSION}" >> $GITHUB_OUTPUT - name: Create Release id: create_release uses: actions/create-release@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: tag_name: ${{ steps.read_build_env.outputs.FULL_VERSION }} release_name: Release ${{ steps.read_build_env.outputs.FULL_VERSION }} draft: false prerelease: false
- name: Upload Release Assets env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | for artifact in artifacts/*/*.{apk,aab,ipa,zip} do if [ -f "$artifact" ]; then asset_name=$(basename "$artifact") echo "Uploading $asset_name" curl --fail -X POST \ -H "Authorization: token $GITHUB_TOKEN" \ -H "Content-Type: $(file -b --mime-type $artifact)" \ --data-binary @"$artifact" \ "https://uploads.github.com/repos/${{ github.repository }}/releases/${{ steps.create_release.outputs.id }}/assets?name=$asset_name" fi done
|