引子

前不久在关于 CI/CD 文章《实战 CI/CD:微软加持的 GitHub Actions,怎么用才香?》中简略介绍了一下 GitHub 的官方 CI/CD 工作流服务 GitHub Actions,用一个实践的 Python 项目比如做了演示。不过,这部分内容相对较初级,而对于一些大型项目来说,咱们或许需要更杂乱的工作流。而这篇文章的首要意图是协助开发者们了解软件开发中的工程化部分。

本篇文章以实践的大型项目为例,介绍一下笔者的开源项目 Crawlab 在 GitHub Actions 中的 CI/CD 应用。对 Crawlab 不了解的读者,能够参考一下官网或官方文档,简略来说,Crawlab 便是能协助提高数据收集效率的爬虫办理渠道。

整体 CI/CD 架构

Crawlab 新版本 v0.6 将不少通用的模块进行了拆分,因而整个项目由多个子项目依靠组成,例如主项目 crawlab 依靠于前端 crawlab-ui 和后端 crawlab-core。这样切分红多个子项目之后,各个模块耦合性降低可维护性添加

以下是 Crawlab 的整体 CI/CD 架构示意图。

实战 CI/CD:利用 GitHub Actions 管理大型开源项目的自动化构建

能够看到,整个 Crawlab 项意图构建流程仍是有些繁琐。给用户拉取布置的终究镜像 crawlabteam/crawlab 依靠于主库房,而主库房依靠于前后端、根底镜像、插件镜像,它们来自于各自的代码库房,也依靠了更上游的中心代码库房。这里简化了中心前后端代码背后所依靠的模块。

前端构建

咱们先从前端部分开端。

前端代码库房 crawlab-ui 是以 NPM 库来分发的。先看一下其 CI/CD 工作流装备。

name: Publish to NPM registry

on:
  pull_request:
   branches: [ main ]
  push:
   branches: [ main ]
  release:
   types: [ created ]

jobs:
  publish:
   runs-on: ubuntu-latest
   steps:
    - uses: actions/checkout@v2
    - uses: actions/setup-node@v2
     with:
      node-version: '12.22.7'
      registry-url: https://registry.npmjs.com/
    - name: Get version
     run: echo "TAG_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
    - name: Install dependencies
     run: yarn install
    - name: Build
     run: yarn run build
     env:
      TAG_VERSION: ${{env.TAG_VERSION}}
    - if: ${{ github.event_name == 'release' }}
     name: Publish npm
     run: npm publish --registry ${REGISTRY}
     env:
      NODE_AUTH_TOKEN: ${{secrets.NPM_PUBLISH_TOKEN}}
      TAG_VERSION: ${{env.TAG_VERSION}}
      REGISTRY: https://registry.npmjs.com/

在这个工作流中有几个重要部分。

  1. 建立 Node.js 环境 uses: actions/setup-node@v2,设置了 Node.js 版本 node-version: '12.22.7'
  2. 安装依靠 run: yarn install
  3. 构建包 yarn run build
  4. 发布库到 NPM 公共空间 npm publish --registry ${REGISTRY}

关于发布到 NPM 公共空间所用到的令牌为 ${{secrets.NPM_PUBLISH_TOKEN}}。这是一个 GitHub 密钥(Secret),安全起见是不公开的,由代码库房办理者设置的。

这个设置好之后,每当在 crawlab-ui 中提交并推送代码之后,就会主动触发 GitHub Actions 构建了,如下图。

实战 CI/CD:利用 GitHub Actions 管理大型开源项目的自动化构建

咱们几乎不必再办理 NPM 发布的工作了,一切都是主动化完结的,完美!

根底镜像构建

下面咱们再看看另一个相对特别的工作流,根底镜像构建。这个镜像的库房在 docker-base-images 里。

因为每一次新的根底镜像发布都需要被集成到终究的镜像中,咱们需要让其构建完之后让 crawlab 再构建一次。下面咱们看看是如何装备的。

name: Docker crawlab-base
​
on:
  push:
   branches: [ main ]
  release:
   types: [ published ]
  workflow_dispatch:
  repository_dispatch:
   types: [ crawlab-base ]
​
env:
  IMAGE_PATH: crawlab-base
  IMAGE_NAME: crawlabteam/crawlab-base
​
jobs:
​
  build:
   name: Build Image
   runs-on: ubuntu-latest
   steps:
    - uses: actions/checkout@v2
​
    - name: Get changed files
     id: changed-files
     uses: tj-actions/changed-files@v18.7
​
    - name: Check matched
     run: |
      # check changed files
      for file in ${{ steps.changed-files.outputs.all_changed_files }}; do
       if [[ $file =~ ^.github/workflows/.* ]]; then
        echo "file ${file} is matched"
        echo "is_matched=1" >> $GITHUB_ENV
        exit 0
       fi
       if [[ $file =~ ^${IMAGE_PATH}/.* ]]; then
        echo "file ${file} is matched"
        echo "is_matched=1" >> $GITHUB_ENV
        exit 0
       fi
      done
     
      # force trigger
      if [[ ${{ inputs.forceTrigger }} == true ]]; then
        echo "is_matched=1" >> $GITHUB_ENV
        exit 0
      fi
​
    - name: Build image
     if: ${{ env.is_matched == '1' }}
     run: |
      cd $IMAGE_PATH
      docker build . --file Dockerfile --tag image
​
    - name: Log into registry
     if: ${{ env.is_matched == '1' }}
     run: echo ${{ secrets.DOCKER_PASSWORD}} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin
​
    - name: Push image
     if: ${{ env.is_matched == '1' }}
     run: |
      IMAGE_ID=$IMAGE_NAME
     
      # Strip git ref prefix from version
      VERSION=$(echo "${{ github.ref }}" | sed -e 's,.*/(.*),\1,')
     
      # Strip "v" prefix from tag name
      [[ "${{ github.ref }}" == "refs/tags/"* ]] && VERSION=$(echo $VERSION | sed -e 's/^v//')
     
      # Use Docker `latest` tag convention
      [ "$VERSION" == "main" ] && VERSION=latest
     
      echo IMAGE_ID=$IMAGE_ID
      echo VERSION=$VERSION
     
      docker tag image $IMAGE_ID:$VERSION
      docker push $IMAGE_ID:$VERSION
     
      if [[ $VERSION == "latest" ]]; then
       docker tag image $IMAGE_ID:main
       docker push $IMAGE_ID:main
      fi
​
    - name: Trigger other workflows
     if: ${{ env.is_matched == '1' }}
     uses: peter-evans/repository-dispatch@v2
     with:
      token: ${{ secrets.WORKFLOW_ACCESS_TOKEN }}
      repository: crawlab-team/crawlab
      event-type: docker-crawlab

能够看到在装备文件中最后一步 name: Trigger other workflows,经过 peter-evans/repository-dispatch@v2 这个 Action 触发了另一个库房 crawlab-team/crawlab 的 Action。也便是说,假如咱们修改了根底镜像代码并提交代码到 GitHub 库房之后,根底镜像会主动构建,并在构建完结之后主动触发终究库房构建完整镜像。

这太棒了!咱们无需再人工做什么操作,只需要坐下来喝杯咖啡等候构建完结。

总结

今天咱们介绍了大型开源项目 Crawlab 在 GitHub Actions 上面的主动化构建流程和整体架构,能够看出 GitHub Actions 对大型项意图支持仍是相对比较全面的。用到的技能如下:

  1. 主动触发构建
  2. 发布到 NPM 公共空间
  3. 库房密钥设置
  4. 触发其他库房的工作流

整个代码库房都在 Crawlab 的 GitHub 库房中,并且是公开可见的。

社区

假如您对笔者的文章感兴趣,能够加笔者微信 tikazyq1 并注明 “码之道”,笔者会将你拉入 “码之道” 交流群。

本篇文章英文版同步发布在 dev.to,技能分享无国界,欢迎大佬们点拨。