Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
giscafer opened this issue Nov 11, 2021 · 4 comments
Open

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

giscafer opened this issue Nov 11, 2021 · 4 comments

Comments

@giscafer
Copy link
Owner

giscafer commented Nov 11, 2021

背景

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/[email protected]
        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/[email protected]
        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

总结

方便好用!

@phobal
Copy link

phobal commented Nov 17, 2021

最近也在折腾这块儿,你这种方案应该是最好的,奈何公司的 gitlab 是部署在内网的,GitHub Actions 访问不了,最终还是弄了台 mac 电脑专门来做 CI/CD

@giscafer
Copy link
Owner Author

最近也在折腾这块儿,你这种方案应该是最好的,奈何公司的 gitlab 是部署在内网的,GitHub Actions 访问不了,最终还是弄了台 mac 电脑专门来做 CI/CD

有Mac服务器资源也挺好。类似的GItlab也一样的,只不过要自己配置Mac服务器资源跑runner。Github Action 官方默认提供了资源,搞起来速度是比较快。

@phobal
Copy link

phobal commented Nov 18, 2021

最近也在折腾这块儿,你这种方案应该是最好的,奈何公司的 gitlab 是部署在内网的,GitHub Actions 访问不了,最终还是弄了台 mac 电脑专门来做 CI/CD

有Mac服务器资源也挺好。类似的GItlab也一样的,只不过要自己配置Mac服务器资源跑runner。Github Action 官方默认提供了资源,搞起来速度是比较快。

我看你的构建记录,构建一次大概需要 25 分钟,感觉时间有点儿长呢。是 app 比较大构建慢还是本身这种方式会比较慢呢?
我现在在 mac 上 构建 + 上传 差不多 5 分钟。

P.S 之前尝试在 docker 里面构建 Android,也差不多需要 20 分钟,感觉时间有点儿长,把这个方案也给否了

@giscafer
Copy link
Owner Author

giscafer commented Nov 18, 2021

最近也在折腾这块儿,你这种方案应该是最好的,奈何公司的 gitlab 是部署在内网的,GitHub Actions 访问不了,最终还是弄了台 mac 电脑专门来做 CI/CD

有Mac服务器资源也挺好。类似的GItlab也一样的,只不过要自己配置Mac服务器资源跑runner。Github Action 官方默认提供了资源,搞起来速度是比较快。

我看你的构建记录,构建一次大概需要 25 分钟,感觉时间有点儿长呢。是 app 比较大构建慢还是本身这种方式会比较慢呢? 我现在在 mac 上 构建 + 上传 差不多 5 分钟。

P.S 之前尝试在 docker 里面构建 Android,也差不多需要 20 分钟,感觉时间有点儿长,把这个方案也给否了

  • 目前是免费版的,资源肯定不会很好。
  • Action 服务器是国外的,代码checkout和推送pgy都花多点时间,两者加起来花费的时间搞不好就超过5分钟了。
  • npm依赖,ios pod install ,都没走缓存,每次都是重新来。每次都花费3~5分钟,Android可能更慢。(有优化空间)
  • 上传到 Artifact,花费半分钟(可去掉的step)
  • Android 构建 6~8分钟,IOS构建约20分钟(IOS 这个确实比本机慢很多,本地mbp就几分钟,这个是macos内存和核数配置是基础影响的,并且runner估计都是走docker的策略,不会占用整个虚拟机的内存或者其他资源)

付费的肯定快很多,毕竟免费的给全球用。如果性能特别好,那成本很高了,目前解决的问题是,QA不再问我们要包而已,节省开发时间和干扰次数。需要优化到更好就要付费,不管是Github付费版的还是自己购买服务器。

Github Action Supported runners and hardware resources

Hardware specification for Windows and Linux virtual machines:

  • 2-core CPU
  • 7 GB of RAM memory
  • 14 GB of SSD disk space

Hardware specification for macOS virtual machines:

  • 3-core CPU
  • 14 GB of RAM memory
  • 14 GB of SSD disk space

https://github.com/actions/virtual-environments/blob/main/images/macos/macos-11-Readme.md

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants