完整解析使用 Github Action 构建和发布 Flutter 应用

Github Actions 是 Github 提供的免费自动化构建实现,特别适用于持续集成和持续交付的场景,它具备自动化完成许多不同任务的能力,例如构建、测试和部署等等。

一、简单介绍

用户只需要在自己 Github 的开源项目下创建 .github/workflows 脚本就可以完成接入,另外针对 Github Actions 官方还提供了 marketplace 用于开发者提交或者引用别人写好的 aciton ,所以很多时候开发者在使用 Github Actions 时,其实会变成了在 marketplace 里挑选和组合 action 的场景。当然,这样各有利弊,后面我们会讲到

image.png

要在 Github 存储库中使用 Github Actions,首先需要创建目录.github/workflows/,然后在 workflows 文件夹里创建不同的 .yml 文件用于响应或者执行不同的事件,比如 git pushpull request 等,例如:

name: GitHub Actions Demo
on: [push]
jobs:
  Explore-GitHub-Actions:
    runs-on: ubuntu-latest
    steps:
      - run: echo "🎉 The job was automatically triggered by a ${{ github.event_name }} event."
      - run: echo "🐧 This job is now running on a ${{ runner.os }} server hosted by GitHub!"
      - run: echo "🔎 The name of your branch is ${{ github.ref }} and your repository is ${{ github.repository }}."
      - name: Check out repository code
        uses: actions/checkout@v2
      - run: echo "💡 The ${{ github.repository }} repository has been cloned to the runner."
      - run: echo "🖥️ The workflow is now ready to test your code on the runner."
      - name: List files in the repository
        run: |
          ls ${{ github.workspace }}
      - run: echo "🍏 This job's status is ${{ job.status }}."

上面是 Github doc 里关于 Action 的一个基本的工作流 yml 文件,具体参数含义 :

  • name:这表示该工作流文件的名称,将在 Github 的 actions 选项卡作为名称显示 ;
  • on:这将触发该工作流的事件名称,它可以包含事件列表,例如这里监听的事 push
  • jobs:每个工作流会包含一个或多个 jobs ,在这里只有一个,主要是用于表示不同工作任务;
  • Explore-GitHub-Actions :这是工作 ID,你也可以根据自己的需要命名,会在 action 的执行过程中显示;
  • runs-on:jobs 需要运行在虚拟机上,在这里中使用了 ubuntu-latest,当然你也可以使用windows-latest 或者 macos-latest
  • steps:每个 jobs 可以将需要执行的内容划分为不同步骤;
  • run:用于提供执行命令,例如这里使用了echo 打印日志;
  • name: steps 里的 name 是可选项,主要是在日志中用来做标记的;
  • uses :使用一些官方或者第三方的 actions 来执行,例如这里使用官方的 actions/checkout@v2,它会check-out 我们的 repo ,之后工作流可以直接访问 repo 里的文件;

在 GitHub 仓库添加完对应的 .github/workflows/ci.yml 文件之后,以后每次 push 都可以触发 action 的自动执行,以此来完成可持续的自动集成和构建能力。

二、构建 Flutter 和发布到 Github Release

简单介绍完 Github Action ,接着我们介绍如何利用 Github Action 构建 Flutter 和发布 apk 到 Github Release,如下代码所示是 gsy_github_app_flutter 项目里使用到的 github action 脚本:

name: CI

on:
  push:
    branches:
      - master
    tags:
      - '*'
  pull_request:
    paths-ignore:
      - '**/*.md'
      - '**/*.txt'
      - '**/*.png'
      - '**/*.jpg'

jobs:
  build:
    name: Build
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-java@v2
        with:
          distribution: 'zulu'
          java-version: 11
      - uses: subosito/flutter-action@v1
        with:
          flutter-version: '2.8.1'
      - uses: finnp/create-file-action@master
        env:
          FILE_NAME: lib/common/config/ignoreConfig.dart
          FILE_DATA: class NetConfig { static const CLIENT_ID = "${{ secrets.CLIENT_ID }}"; static const CLIENT_SECRET = "${{ secrets.CLIENT_SECRET }}";}
      - run: flutter pub get
      - run: flutter build apk --release --target-platform=android-arm64 --no-shrink

  apk:
    name: Generate APK
    if: startsWith(github.ref, 'refs/tags/')
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v2
      - name: Setup JDK
        uses: actions/setup-java@v2
        with:
          distribution: 'zulu'
          java-version: 8
      - uses: subosito/flutter-action@v1
        with:
          flutter-version: '2.5.3'
      - uses: finnp/create-file-action@master
        env:
          FILE_NAME: lib/common/config/ignoreConfig.dart
          FILE_DATA: class NetConfig { static const CLIENT_ID = "${{ secrets.CLIENT_ID }}"; static const CLIENT_SECRET = "${{ secrets.CLIENT_SECRET }}";}
      - run: flutter pub get
      - run: flutter build apk --release --target-platform=android-arm64 --no-shrink
      - name: Upload APK
        uses: actions/upload-artifact@v2
        with:
          name: apk
          path: build/app/outputs/apk/release/app-release.apk
  release:
    name: Release APK
    needs: apk
    if: startsWith(github.ref, 'refs/tags/')
    runs-on: ubuntu-latest
    steps:
      - name: Download APK from build
        uses: actions/download-artifact@v2
        with:
          name: apk
      - name: Display structure of downloaded files
        run: ls -R

      - name: Create Release
        id: create_release
        uses: actions/create-release@v1.1.4
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          tag_name: ${{ github.ref }}
          release_name: ${{ github.ref }}
      - name: Upload Release APK
        id: upload_release_asset
        uses: actions/upload-release-asset@v1.0.1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          upload_url: ${{ steps.create_release.outputs.upload_url }}
          asset_path: ./app-release.apk
          asset_name: app-release.apk
          asset_content_type: application/zip

根据上述脚本,首先可以看到:

  • push 事件里我们指定了只监听 master 分支和 tags 相关的提交;

  • 然后在 pull_request 事件里忽略了关于 .md、 .text 和图片相关的内容,也就是这部分内容提交不触发 action ,具体可以看你自己的需求;

  • 接着进入到 jobs 里,首先不管是 push 还是 pull_request 都会执行到 Build 事件,运行在 ubuntu-latest 虚拟机上,之后利用 actions/checkout@v2 checkout 代码;

  • 接着使用 actions/setup-java@v2 配置 java 环境,这里使用的是 Zulu OpenJDK 版本 11 ,下面表格是 setup-java 支持的可选 java 类型;

    Keyword Distribution Official site License
    temurin Eclipse Temurin Link Link
    zulu Zulu OpenJDK Link Link
    adopt or adopt-hotspot Adopt OpenJDK Hotspot Link Link
    adopt-openj9 Adopt OpenJDK OpenJ9 Link Link
    liberica Liberica JDK Link Link
    microsoft Microsoft Build of OpenJDK Link Link
  • 接着就是使用第三方的 subosito/flutter-action@v1 配置 flutter 环境,直接通过 flutter-version: '2.8.1' 指定了 Flutter 版本;

  • 接着是使用第三方的 finnp/create-file-action@master 创建文件,因为 gsy_github_app_flutter 项目有一个配置文件是需要用户根据自己的 ID 和 SECRET 手动创建,所以这里通过 create-file-action 创建文件并输入内容;

  • 在上述输入内容部分,有一个 secrets.xxx 的参数,因为构建时需要将自己的一些密钥信息配置到 action 里,所以如下图所示,可以在 SettingsSecrets 里添加对应的内容,就可以在 action 里通过 secrets.xxx 读取;

  • 接着配置好环境之后,就可以执行 flutter pub getflutter build apk 执行构建;

完成 Build 任务的逻辑介绍之后,可以看到在 Build 任务下面还有一个 apk 任务,该任务基本和 Build 任务一直,不同之处在于:

  • 多了一个 if: startsWith(github.ref, 'refs/tags/') ,也就是存在 tag 的时候才会触发该任务执行;
  • 多了一个 actions/upload-artifact@v2 用于将构建出来的 build/app/outputs/apk/release/app-release.apk上传,并等到 release 任务内使用;

完成 apk 任务之后,会进入到 release 任务,该任务同样通过 if 指定了只在 tag 提交时运行:

  • 任务首先会通过 actions/download-artifact@v2 下载刚刚上传的 apk;
  • 然后就通过 actions/create-release@v1.1.4 创建一个 release 版本,这里使用的 secrets.GITHUB_TOKEN 是官方内置的 secrets ,我们直接使用就可以了;
  • 最后通过 actions/upload-release-asset@v1.0.1 将 apk 上传到刚刚创建的 release 版本里,自此就完成了 action 的发布流程;

可以看到整个过程其实都是在组合不同的 action ,可以很灵活方便地配置构建逻辑,例如如果你的项目是单纯的 android sdk 项目,那同样可以通过如下脚本进行发布管理:

name: CI

on:
  push:
    branches:
      - master
    paths-ignore:
      - '.idea/**'
      - '.gitattributes'
      - '.github/**.json'
      - '.gitignore'
      - '.gitmodules'
      - '**.md'
      - '**/*.txt'
      - '**/*.png'
      - '**/*.jpg'
      - 'LICENSE'
      - 'NOTICE'
  pull_request:
    paths-ignore:
      - '.idea/**'
      - '.gitattributes'
      - '.github/**.json'
      - '.gitignore'
      - '.gitmodules'
      - '**.md'
      - '**/*.txt'
      - '**/*.png'
      - '**/*.jpg'
      - 'LICENSE'
      - 'NOTICE'

jobs:
  publish:
    name: Publish to MavenLocal
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-java@v3
        with:
          distribution: 'zulu'
          java-version: 17
      - uses: gradle/gradle-build-action@v2
        with:
          arguments: publishToMavenLocal

  build:
    name: Build
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-java@v3
        with:
          distribution: 'zulu'
          java-version: 17
      - uses: gradle/gradle-build-action@v2
        with:
          arguments: app:assembleDebug

当然,如果你需要打包的是 iOS ,那么你就需要使用 macos-latest 的环境,另外还需要配置相关的开发者证书,这个过程可能会比较难受,相关可以参考 《Flutter 搭建 iOS 命令行服务打包发布全保姆式流程》

三、隐私安全问题

最后,关于 Github Actions 之前存在过出现泄露敏感数据的问题,比如 Github 的 Token 等 ,举个例子,如上面的脚本,它在执行任务时都会需要秘钥 ,如果你使用的第三方 action 在执行过程中获取了你的密钥并干了一些“非法” 的事情,就可能出现异常泄漏问题。

所以一般情况下建议大家都要去看下非官方的脚本实现里是否安全,但是由于 tag 和 branch 是可以修改,所以建议不要@分支或tag,而是应该 checkout 对应的提交哈希,这样有利于你审查使用时的脚本是否安全。

另外,例如还有人提到可以通过 pull_request 来恶意攻击获取对应隐私:

  • 1、fork 一个正在使用 GitHub Actions 的公开代码库;

  • 2、创建一个基于该项目的 pull 请求;

  • 3、使用 pull_request_target 事件创建一个恶意 Actions 工作流,然后单独向该 fork 库 commit;

  • 4、将第二步基分支的 pull 请求更新为第三步的 commit 哈希;

之后恶意 Actions 工作流就会运行,并从目标 repos 里获取到执行过程的敏感数据,此时攻击者将拥有对目标存储库的写访问权限,除此之外他们还可以通过 GitHub 访问与仓库之成的任何服务。

所以虽然 GitHub Action 很便捷,但是如果出于商业考虑的话,还需要谨慎抉择安全问题

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,230评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,261评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,089评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,542评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,542评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,544评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,922评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,578评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,816评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,576评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,658评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,359评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,937评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,920评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,859评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,381评论 2 342

推荐阅读更多精彩内容