GitHub Actions实现RN App自动化构建并推送到蒲公英

GitHub Actions实现RN App自动化构建并推送到蒲公英

背景

XX项目有App终端,技术栈 React Native,本地打包,QA去打扰开发人员占用时间,打包费时,也受开发电脑配置影响,慢得可能要30分钟。电脑配置高也至少10+分钟,一天多次,断断续续打断开发的开发节奏,影响效率。 ​

为什么不用Jenkins? ​

  • 有服务器,但运维一直没帮忙装Android环境,导致App构建迟迟不落实。
  • 服务器一般没有Mac OS的,需要支持iOS App自动化还得采购走申请,了解到以往平台那边的项目也不是自动化打包的。

为什么用 GitHub Actions? ​

  • Gtihub Actions 自2018年上线后,就被社区广泛使用,基本托管在Github的项目都会首选 Action,因为好用
  • 社区共享了很多Action 插件,市场上可以搜到各种符合需求的Action插件,可做到拿来即用,节省时间。

CICD实现思路

由于代码是在公司的 Gitlab,也不会(更不允许)推送到 GitHub,所以采用的策略就是借用一个Github 空项目,在 Runner 执行 job step 流程时,拉取远程公司项目代码,然后再走构建流程。 ​

借用 secrets 来获取配置的环境变量 GITLAB_TOKEN(访问私库Gitlab秘钥) 和 GITLAB_REPO_URL(私库代码Git url), 就可以把私库代码 clone 下来,并做到安全保密。 ​

手动构建触发

手动都可以,想自动更容易

选择支持手动构建触发的原因是,让QA或开发自己决定何时触发,构建什么分支,也避开了无用的自动化构建。 借用 workflow_dispatch 来实现变量控制,如下: ​

name: Android构建
on:
  workflow_dispatch:
    inputs:
      buildBranch:
        description: '输入构建分支(dev/test/master/prod)'
        required: true
        default: 'dev'
      uploadArtifact:
        description: '是否将生成的apk上传到Github Artifact (true/false)'
        required: false
        default: 'true'
      uploadCloud:
        description: '是否将生成的apk上传到蒲公英。(true/false)'
        required: false
        default: 'true'

可以通过 ${{ github.event.inputs.buildBranch}} 获取到构建分支,其他输入框类似

shell 脚本拉取代码

checkout.sh 脚本只负责拉取分支代码即可

#!/bin/bash

set -e

repositoryUrl="${GITLAB_REPO_URL}"
branchName=${1}
devBranch='dev'
testBranch='test'
masterBranch='master'
prodBranch='prod'

function log() {
  echo "$(date)>>>>$@"
}


# 克隆分支代码

if [[ $branchName == $testBranch ]];then
    echo "包含test"
    git clone -b test $repositoryUrl
elif [[ $branchName == $masterBranch ]];then
    echo "包含master"
    git clone $repositoryUrl
elif [[ $branchName == $prodBranch ]];then
    echo "包含[prod]"
    git clone -b prod $repositoryUrl
elif [[ $branchName == $devBranch ]];then
    echo "包含dev"
    git clone -b dev $repositoryUrl
else
    echo "默认执行dev分支代码"
    git clone -b dev $repositoryUrl
fi

cd g-crm-app 

log "$(git branch)"
# 拉取最新代码
git pull

cd ..  
# 将代码放到github runner 执行目录下
cd xxx-app && mv * ../ 

pwd

ls -l

# 此处应该有切换环境服务地址的脚本执行
# node ./scripts/prebuild.js

Android 构建

主要流程描述:

  • 触发构建入参(分支、是否推送到蒲公英等)
  • checkout 代码
  • 安装依赖
  • 执行构建 cd android && chmod +x ./gradlew && ./gradlew assembleRelease
  • 上传apk到蒲公英平台,见api#uploadApp
  • 消息推送
name: Android构建
on:
  workflow_dispatch:
    inputs:
      buildBranch:
        description: '输入构建分支(dev/test/master/prod)'
        required: true
        default: 'dev'
      uploadArtifact:
        description: '是否将生成的apk上传到Github Artifact (true/false)'
        required: false
        default: 'true'
      uploadCloud:
        description: '是否将生成的apk上传到蒲公英。(true/false)'
        required: false
        default: 'true'

env:
  GITLAB_REPO_URL: ${{ secrets.GITLAB_REPO_URL }}
  WECOM_WEBHOOK_KEY: ${{ secrets.WECOM_WEBHOOK_KEY }}
  UPLOAD_TOKEN_URL: ${{ secrets.UPLOAD_TOKEN_URL }}
  UPLOAD_URL: ${{ secrets.UPLOAD_URL }}

jobs:
  build-android:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v2
      - name: Get commit message
        run: |
          GIT_MESSAGE="$(git log --format=%B -n 1)"
          date_str=$(date "+%Y-%m-%d %H:%M:%S")
          seconds=$(date -d "$date_str" +%s)
          seconds_new=$(expr $seconds + 28800) 
          echo "COMMIT_MESSAGE=$GIT_MESSAGE" >> $GITHUB_ENV
          echo "BUILD_TIME=$(date -d @$seconds_new "+%Y-%m-%d_%H_%M_%S")" >> $GITHUB_ENV
      - name: Show commit message
        run: |
          echo "$COMMIT_MESSAGE"
          echo "$BUILD_TIME"
          echo "${{ github.event.inputs.buildBranch }}"
      - name: Checkout code
        run: |
          bash ./checkout2.sh "${{ github.event.inputs.buildBranch }}"
      - name: Install npm dependencies
        run: |
          npm install
      - name: Start Build Apk Message
        run: |
          node ./send-startmsg.js "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" "${{ github.event.inputs.uploadCloud }}"
      - name: Build Android Release
        run: |
          cd android && chmod +x ./gradlew && ./gradlew assembleRelease
      - name: Upload Artifact
        if: ${{ github.event.inputs.uploadArtifact == 'true' }}
        uses: actions/upload-artifact@v1
        with:
          name: app-release.apk
          path: android/app/build/outputs/apk/release/
      - name: Upload Artifact Success
        if: ${{ github.event.inputs.uploadArtifact == 'true' }}
        run: |
          npm i request
          node ./send-success.js "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" "${{ github.event.inputs.uploadCloud }}"
      - name: Push to Fir
        id: PushToFir
        run: |
          curl -F 'file=@android/app/build/outputs/apk/release/app-release.apk' -F '_api_key=${{ secrets.PGYER_API_KEY }}' https://www.pgyer.com/apiv2/app/upload
        continue-on-error: true
      - name: Send fir error notify
        id: firErrorMessage
        if: steps.PushToFir.outcome != 'success'
        run: |
          node ./send-msg.js "Android 附件同步到蒲公英平台失败。请检查错误重新执行或前往Action直接下载apk文件 \n>[Job RunId](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})"
      - name: Send finally notify
        run: |
          node ./send-msg.js "Android 构建成功,并同步到蒲公英平台。\n>[Job Link](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) \n>蒲公英地址:[https://www.pgyer.com/dev-apk](https://www.pgyer.com/dev-apk)"
      - name: On Failure
        if: ${{ failure() }}
        run: |
          npm i request
          node ./send-error.js "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"

image

IOS 构建

IOS 构建流程其实和Android流程一致,只是构建平台的区别,以及IOS需要配置证书对ipa签名,所以借用了Action插件:ios-build-action

name: IOS构建
on:
  # push:
  #   branches: [master*]
  # pull_request:
  #   branches: [master*]

  workflow_dispatch:
    inputs:
      buildBranch:
        description: '输入构建分支(dev/test/master/prod)'
        required: true
        default: 'dev'
      uploadArtifact:
        description: '是否将生成的ipa上传到Github Artifact (true/false)'
        required: false
        default: 'true'
      uploadCloud:
        description: '是否将生成的ipa上传到蒲公英。(true/false)'
        required: false
        default: 'true'

env:
  GITLAB_REPO_URL: ${{ secrets.GITLAB_REPO_URL }}
  WECOM_WEBHOOK_KEY: ${{ secrets.WECOM_WEBHOOK_KEY }}
  UPLOAD_TOKEN_URL: ${{ secrets.UPLOAD_TOKEN_URL }}
  UPLOAD_URL: ${{ secrets.UPLOAD_URL }}

jobs:
  build:
    runs-on: macos-10.15
    timeout-minutes: 60
    strategy:
      matrix:
        node-version: [14.18.x]
    steps:
      - name: Checkout repository
        uses: actions/checkout@v2
      - name: Get commit message
        run: |
          GIT_MESSAGE="$(git log --format=%B -n 1)"
          echo "COMMIT_MESSAGE=$GIT_MESSAGE" >> $GITHUB_ENV
      - name: Show commit message
        run: |
          echo "$COMMIT_MESSAGE"
          echo "$BUILD_TIME"
          echo "${{ github.event.inputs.buildBranch }}"
      - name: Checkout code
        run: |
          bash ./checkout2.sh "${{ github.event.inputs.buildBranch }}"
      - name: Install npm dependencies
        run: |
          npm -v 
          npm install
      - name: pod
        run: |
          cd ios && pod install --repo-update
          cd ..
      - name: Start Build Apk Message
        run: |
          node ./send-startmsg.js "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" "${{ github.event.inputs.uploadCloud }}"  "IOS"
      - uses: yukiarrr/ios-build-action@v1.4.0
        with:
          project-path: ios/g_service.xcodeproj
          workspace-path: ios/g_service.xcworkspace
          p12-path: ios/Certificates.p12
          mobileprovision-path: ios/tieniuniu.mobileprovision
          # p12-base64: ${{ secrets.P12_BASE64 }}
          # p12-cer-base64: ${{ secrets.P12_CER_BASE64 }}
          # mobileprovision-base64: ${{ secrets.MOBILEPROVISION_BASE64 }}
          code-signing-identity: ${{ secrets.CODE_SIGNING_IDENTITY }}
          team-id: ${{ secrets.TEAM_ID }}
          # export-method: 'ad-hoc'
          export-method: 'development'
          configuration: 'Release'
          output-path: /Users/runner/work/outputs/release.ipa
      - name: Upload Artifact
        if: ${{ github.event.inputs.uploadArtifact == 'true' }}
        uses: actions/upload-artifact@v1
        with:
          name: release.ipa
          path: /Users/runner/work/outputs/
      - name: Upload Artifact Success
        if: ${{ github.event.inputs.uploadArtifact == 'true' }}
        run: |
          npm i request
          node ./send-success.js "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" "${{ github.event.inputs.uploadCloud }}" "IOS"
      - name: push to fir
        id: PushToFir
        run: |
          curl -F 'file=@/Users/runner/work/outputs/release.ipa' -F '_api_key=${{ secrets.PGYER_API_KEY }}' https://www.pgyer.com/apiv2/app/upload
        continue-on-error: true
      - name: Send fir error notify
        id: firErrorMessage
        if: steps.PushToFir.outcome != 'success'
        run: |
          node ./send-msg.js "同步到蒲公英平台失败。请检查错误重新执行或前往Action直接下载ipa文件 \n>[Job RunId](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})"
      - name: Send finally notify
        run: |
          node ./send-msg.js "IOS构建成功,并同步到蒲公英平台。\n>[Job Link](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) \n>蒲公英地址:[https://www.pgyer.com/dev-tie](https://www.pgyer.com/dev-tie)"
      - name: On Failure
        if: ${{ failure() }}
        run: |
          npm i request
          node ./send-error.js "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"  "IOS"

本人非IOS开发人员,也是第一次弄这个,折腾了不少时间,对几个参数进行说明 ​

  • runs-on: 指定操作系统,IOS这边如果是系统>=15.0,需要Big Sur才支持,系统类型指定详细见:jobsjob_idruns-on

  • project-path 项目 .xcodeproj 文件路径

  • workspace-path 项目 .xcworkspace文件路径

  • p12-path 证书和秘钥(cert,key两者)p12 文件,通过Mac电脑 KeyChain Access 软件导出

  • mobileprovision-path *.mobileprovision 描述文件,苹果开发者中心签名时下载

  • code-signing-identity 对应Xcode签名时你选择,Build Settings下,并且要对应p12中的cert

  • team-id 登陆开发者账号就可以看到的id,project.pbxproj 文件内的 DEVELOPMENT_TEAM

- uses: yukiarrr/ios-build-action@v1.4.0
        with:
          project-path: ios/g_service.xcodeproj
          workspace-path: ios/g_service.xcworkspace
          p12-path: ios/Certificates.p12
          mobileprovision-path: ios/tieniuniu.mobileprovision
          # p12-base64: ${{ secrets.P12_BASE64 }}
          # p12-cer-base64: ${{ secrets.P12_CER_BASE64 }}
          # mobileprovision-base64: ${{ secrets.MOBILEPROVISION_BASE64 }}
          code-signing-identity: ${{ secrets.CODE_SIGNING_IDENTITY }}
          team-id: ${{ secrets.TEAM_ID }}
          # export-method: 'ad-hoc'
          export-method: 'development'
          configuration: 'Release'
          output-path: /Users/runner/work/outputs/release.ipa

如果在搞IOS自动化构建之前,Xcode 构建打包到真机测试是成功的,自动化需要配置的东西,就基本是对的。 这里对非专业IOS开发人员有点困难的是 p12 和 mobileprovision 内容的获取。我个人是用文件配置,这个文件需要放到项目代码里,其实这里用 base64的方式配置到 Action 的 secrets 可能更方便(上边注释部分),不过要将文件内容转为 base64 。 ​

image

参考资料

对于此块配置推荐阅读插件使用说明和以下苹果签名相关文章

源码

https://github.com/RootLinkFE/devops-crm-app ​

总结

方便好用!


本人自动发布于:https://github.com/giscafer/blog/issues/53

相关文章