硅基俊宁.
Skills · 技能体系 ↗
← 返回文章

叮,一份 Deno GitHub Action 源码解析请查收

俊宁 · 2023-04-19
迁自历史博客,保留原文、署名、引用与原始日期;文中的版本、成果与性能结论不代表重新完成了当前验收。
查看原始出处 ↗ · 查看迁移清单
叮,一份 Deno GitHub Action 源码解析请查收封面

GitHub Action 是 GitHub 官方的 CI/CD 工具,相较于 Travis CI 和 Circle CI,更轻量和易于扩展,marketplace 中有大量社区贡献的插件。各大开源项目都纷纷转向使用 GitHub Action 作为持续集成的工具,比如本文的主角 Deno。

GitHub 的文档中有很多概念写的十分晦涩,有些翻译很僵硬影响理解。截止发稿时,Deno 的 ci.yml 文件有 323 行,是一个很好的学习范本。想要对 GitHub Action 有个了解或对 Deno 的持续集成部分感兴趣的同学都可以一起来探究下。

Action 结构

注意:GitHub Action 的 ci 脚本需要放在 .github/workflows 文件夹下

name: ci

on: [push, pull_request]

jobs:

build job

jobs:
  build:
    name: ${{ matrix.kind }} ${{ matrix.os }}
    if: |
      github.event_name == 'push' ||
      !startsWith(github.event.pull_request.head.label, 'denoland:')
    runs-on: ${{ matrix.os }}
    timeout-minutes: 60
    strategy:
      # ...
    env:
      # ...
    steps:
      # ...

strategy 策略

strategy:
  matrix:
    include:
      - os: macos-10.15
        kind: test_release
      - os: windows-2019
        kind: test_release
      - os: ${{ github.repository == 'denoland/deno' && 'ubuntu-latest' || 'ubuntu-18.04' }}
        kind: test_release
      - os: ${{ github.repository == 'denoland/deno' && 'ubuntu-latest' || 'ubuntu-18.04' }}
        kind: test_debug
      - os: ${{ github.repository == 'denoland/deno' && 'ubuntu-latest' || 'ubuntu-18.04' }}
        kind: bench
      - os: ${{ github.repository == 'denoland/deno' && 'ubuntu-latest' || 'ubuntu-18.04' }}
        kind: lint

  # Always run master branch builds to completion. This allows the cache to
  # stay mostly up-to-date in situations where a single job fails due to
  # e.g. a flaky test.
  # Don't fast-fail on tag build because publishing binaries shouldn't be
  # prevented if 'cargo publish' fails (which can be a false negative).
  fail-fast: ${{ github.event_name == 'pull_request' || (github.ref !=
    'refs/heads/master' && !startsWith(github.ref, 'refs/tags/')) }}

插曲

${{ github.repository == 'denoland/deno' && 'ubuntu-latest' || 'ubuntu-18.04' }} 这种写法我看到时很疑惑,直接取 'ubuntu-latest' 不行吗?如果不行是为什么呢?抱着求真的态度,我翻阅到是在这个 commit 加入的这个判断,在一番询问后得到了下面的答案:

紫升

在 justjavac 大佬那里也得到了印证:

紫升

然后我还有个疑问是为什么不使用三元表达式,是 YAML 语法不支持吗?

紫升

env 环境变量

env:
  CARGO_INCREMENTAL: 0
  RUST_BACKTRACE: full
  CARGO_TERM_COLOR: always

steps 步骤

Deno 的 CI 中截止写下这段话时有 35 个步骤,相当考验我继续解读的勇气。。。

作业包含一系列任务,称为 steps。步骤可以运行命令、运行设置任务,或者运行您的仓库、公共仓库中的操作或 Docker 注册表中发布的操作。并非所有步骤都会运行操作,但所有操作都会作为步骤运行。每个步骤在运行器环境中以其自己的进程运行,且可以访问工作区和文件系统。因为步骤以自己的进程运行,所以步骤之间不会保留环境变量的更改。 GitHub 提供内置的步骤来设置和完成作业。—— 文档

1、Configure git

- name: Configure git
  run: git config --global core.symlinks true

2、Clone repository

- name: Clone repository
  uses: actions/checkout@v2
  with:
    # 使用 `depth> 1`,因为有时我们需要重新构建 master 分支,如果 checkout 太浅,其他 commits push 之后将不可能重新构建。
    fetch-depth: 5
    submodules: true

3、Create source tarballs (release, linux)

创建源文件压缩包 (release, linux)

- name: Create source tarballs (release, linux)
  if: |
    startsWith(matrix.os, 'ubuntu') &&
    matrix.kind == 'test_release' &&
    github.repository == 'denoland/deno' &&
    startsWith(github.ref, 'refs/tags/')
  run: |
    mkdir -p target/release
    tar --exclude=.cargo_home --exclude=".git*" --exclude=target --exclude=third_party/prebuilt -czvf target/release/deno_src.tar.gz -C .. deno

4、Install rust

安装 rust

- name: Install rust
  uses: hecrj/setup-rust-action@v1
  with:
    rust-version: 1.49.0

5、Install clippy and rustfmt

- name: Install clippy and rustfmt
  if: matrix.kind == 'lint'
  run: |
    rustup component add clippy
    rustup component add rustfmt

6、Install Deno(非 Windows)

- name: Install Deno
  if: |
    !startsWith(matrix.os, 'windows')
  run: |-
    curl -fsSL https://deno.land/x/install/install.sh | sh -s v1.5.1
    echo "$HOME/.deno/bin" >> $GITHUB_PATH

7、Install Deno (Windows)

- name: Install Deno (Windows)
  if: startsWith(matrix.os, 'windows')
  run: |-
    curl -fsSL https://deno.land/x/install/install.sh | sh -s v1.5.1
    echo "$HOME/.deno/bin" >> $env:GITHUB_PATH

8、Install Python

安装 python

- name: Install Python
  uses: actions/setup-python@v1
  with:
    python-version: '3.8'
    architecture: x64

9、Install Node

安装 Node

- name: Install Node
  uses: actions/setup-node@v2
  with:
    node-version: '14'
    check-latest: true

10、Remove unused versions of Python

删除没有用到的 Python 版本

- name: Remove unused versions of Python
  if: startsWith(matrix.os, 'windows')
  run: |-
    $env:PATH -split ";" |
      Where-Object { Test-Path "$_\python.exe" } |
      Select-Object -Skip 1 |
      ForEach-Object { Move-Item "$_" "$_.disabled" }

11、Setup gcloud (unix)

安装 Google Cloud SDK(unix)

- name: Setup gcloud (unix)
  if: |
    runner.os != 'Windows' &&
    matrix.kind == 'test_release' &&
    github.repository == 'denoland/deno' &&
    (github.ref == 'refs/heads/master' ||
    startsWith(github.ref, 'refs/tags/'))
  uses: google-github-actions/setup-gcloud@master
  with:
    project_id: denoland
    service_account_key: ${{ secrets.GCP_SA_KEY }}
    export_default_credentials: true

12、Setup gcloud (windows)

同 11 作用一样,不过是为 windows 设置 gcloud

- name: Setup gcloud (windows)
  if: |
    runner.os == 'Windows' &&
    matrix.kind == 'test_release' &&
    github.repository == 'denoland/deno' &&
    (github.ref == 'refs/heads/master' ||
    startsWith(github.ref, 'refs/tags/'))
  uses: google-github-actions/setup-gcloud@master
  env:
    CLOUDSDK_PYTHON: ${{env.pythonLocation}}\python.exe
  with:
    project_id: denoland
    service_account_key: ${{ secrets.GCP_SA_KEY }}
    export_default_credentials: true

13、Configure canary build

配置 canary 渠道的 build(Deno 发布氛围 canary 和 release 渠道)

- name: Configure canary build
  if: |
    matrix.kind == 'test_release' &&
    github.repository == 'denoland/deno' &&
    github.ref == 'refs/heads/master'
  shell: bash
  run: |
    echo "DENO_CANARY=true" >> $GITHUB_ENV

您也可以使用 GITHUB_ENV environment file 设置工作流程中的以下步骤可以使用的环境变量。 —— 文档

14、Log versions

检查前面安装的程序的版本

- name: Log versions
  run: |
    node -v
    python --version
    rustc --version
    cargo --version
    deno --version

15、lint.js

执行 lint

- name: lint.js
  if: matrix.kind == 'lint'
  run: deno run --unstable --allow-write --allow-read --allow-run ./tools/lint.js

16、test_format.js

测试格式化程序

- name: test_format.js
  if: matrix.kind == 'lint'
  run: deno run --unstable --allow-write --allow-read --allow-run ./tools/format.js --check

17、Build release

- name: Build release
  if: |
    matrix.kind == 'test_release' ||
    matrix.kind == 'bench'
  run: cargo build --release --locked --all-targets -vv

18、Build debug

- name: Build debug
  if: matrix.kind == 'test_debug'
  run: cargo build --locked --all-targets

19、Pre-release (linux)

发布之前的准备工作(linux)

- name: Pre-release (linux)
  if: |
    startsWith(matrix.os, 'ubuntu') &&
    matrix.kind == 'test_release'
  run: |
    cd target/release
    zip -r deno-x86_64-unknown-linux-gnu.zip deno
    zip -r denort-x86_64-unknown-linux-gnu.zip denort
    ./deno types > lib.deno.d.ts

20、Pre-release (mac)

和 19 作用一样,这里没有 ./deno types > lib.deno.d.ts,私以为漏了。

- name: Pre-release (mac)
  if: |
    startsWith(matrix.os, 'macOS') &&
    matrix.kind == 'test_release'
  run: |
    cd target/release
    zip -r deno-x86_64-apple-darwin.zip deno
    zip -r denort-x86_64-apple-darwin.zip denort

21、Pre-release (windows)

和 19、20 一样,just for windows,可以学习一下 Windows 下的压缩命令怎么写!

- name: Pre-release (windows)
  if: |
    startsWith(matrix.os, 'windows') &&
    matrix.kind == 'test_release'
  run: |
    Compress-Archive -CompressionLevel Optimal -Force -Path target/release/deno.exe -DestinationPath target/release/deno-x86_64-pc-windows-msvc.zip
    Compress-Archive -CompressionLevel Optimal -Force -Path target/release/denort.exe -DestinationPath target/release/denort-x86_64-pc-windows-msvc.zip

22、Upload canary to dl.deno.land (unix)

上传 canary 包到 dl.deno.land(unix)(我们只要知道托管在 Google 云就好了)

- name: Upload canary to dl.deno.land (unix)
  if: |
    runner.os != 'Windows' &&
    matrix.kind == 'test_release' &&
    github.repository == 'denoland/deno' &&
    github.ref == 'refs/heads/master'
  run: |
    gsutil cp ./target/release/*.zip gs://dl.deno.land/canary/$(git rev-parse HEAD)/
    echo $(git rev-parse HEAD) > canary-latest.txt
    gsutil cp canary-latest.txt gs://dl.deno.land/canary-latest.txt

23、Upload canary to dl.deno.land (windows)

和 22 作用一样,just for windows

- name: Upload canary to dl.deno.land (windows)
  if: |
    runner.os == 'Windows' &&
    matrix.kind == 'test_release' &&
    github.repository == 'denoland/deno' &&
    github.ref == 'refs/heads/master'
  env:
    CLOUDSDK_PYTHON: ${{env.pythonLocation}}\python.exe
  shell: bash
  run: |
    gsutil cp ./target/release/*.zip gs://dl.deno.land/canary/$(git rev-parse HEAD)/
    echo $(git rev-parse HEAD) > canary-latest.txt
    gsutil cp canary-latest.txt gs://dl.deno.land/canary-latest.txt

24、Test release

测试发布

- name: Test release
  if: matrix.kind == 'test_release'
  run: cargo test --release --locked --all-targets

25、Test debug

测试 debug

- name: Test debug
  if: matrix.kind == 'test_debug'
  run: |
    cargo test --locked --doc
    cargo test --locked --all-targets

26、Configure hosts file for WPT (unix)

26、Configure hosts file for WPT (unix)

WPT 是 Web Platform Test 的意思,对应仓库 denoland/wpt

 - name: Configure hosts file for WPT (unix)
  if: runner.os != 'Windows'
  run: ./wpt make-hosts-file | sudo tee -a /etc/hosts
  working-directory: test_util/wpt/

27、Configure hosts file for WPT (windows)

和 26 作用一样,just for windows

- name: Configure hosts file for WPT (windows)
  if: runner.os == 'Windows'
  working-directory: test_util/wpt/
  run: python wpt make-hosts-file | Out-File $env:SystemRoot\System32\drivers\etc\hosts -Encoding ascii -Append

28、Run web platform tests (release)

在 web 平台下运行测试(release)

- name: Run web platform tests (release)
  if: matrix.kind == 'test_release'
  run: |
    deno run --unstable --allow-write --allow-read --allow-net --allow-env --allow-run ./tools/wpt.ts setup
    deno run --unstable --allow-write --allow-read --allow-net --allow-env --allow-run ./tools/wpt.ts run --quiet --release

29、Run web platform tests (debug)

在 web 平台下运行测试(debug)

 - name: Run web platform tests (debug)
  if: matrix.kind == 'test_debug'
  run: |
    deno run --unstable --allow-write --allow-read --allow-net --allow-env --allow-run ./tools/wpt.ts setup
    deno run --unstable --allow-write --allow-read --allow-net --allow-env --allow-run ./tools/wpt.ts run --quiet

30、Run Benchmarks

压力测试

- name: Run Benchmarks
  if: matrix.kind == 'bench'
  run: cargo bench

31、Post Benchmarks

看起来是把压测数据存到 denoland/benchmark_data

- name: Post Benchmarks
  if: |
    matrix.kind == 'bench' &&
    github.repository == 'denoland/deno' &&
    github.ref == 'refs/heads/master'
  env:
    DENOBOT_PAT: ${{ secrets.DENOBOT_PAT }}
  run: |
    git clone --depth 1 -b gh-pages https://${DENOBOT_PAT}@github.com/denoland/benchmark_data.git gh-pages
    deno run --unstable -A ./tools/build_benchmark_jsons.js --release
    cd gh-pages
    git config user.email "propelml@gmail.com"
    git config user.name "denobot"
    git add .
    git commit --message "Update benchmarks"
    git push origin gh-pages

32、Worker info

- name: Worker info
  if: matrix.kind == 'bench'
  run: |
    cat /proc/cpuinfo
    cat /proc/meminfo

33、Upload release to dl.deno.land (unix)

上传 unix release 到 dl.deno.land

- name: Upload release to dl.deno.land (unix)
  if: |
    runner.os != 'Windows' &&
    matrix.kind == 'test_release' &&
    github.repository == 'denoland/deno' &&
    startsWith(github.ref, 'refs/tags/')
  run: |
    gsutil cp ./target/release/*.zip gs://dl.deno.land/release/${GITHUB_REF#refs/*/}/
    echo ${GITHUB_REF#refs/*/} > release-latest.txt
    gsutil cp release-latest.txt gs://dl.deno.land/release-latest.txt

34、Upload release to dl.deno.land (windows)

上传 windows release 到 dl.deno.land

- name: Upload release to dl.deno.land (windows)
  if: |
    runner.os == 'Windows' &&
    matrix.kind == 'test_release' &&
    github.repository == 'denoland/deno' &&
    startsWith(github.ref, 'refs/tags/')
  env:
    CLOUDSDK_PYTHON: ${{env.pythonLocation}}\python.exe
  shell: bash
  run: |
    gsutil cp ./target/release/*.zip gs://dl.deno.land/release/${GITHUB_REF#refs/*/}/
    echo ${GITHUB_REF#refs/*/} > release-latest.txt
    gsutil cp release-latest.txt gs://dl.deno.land/release-latest.txt

35、Upload release to GitHub

上传 release 到 GitHub

- name: Upload release to GitHub
  uses: softprops/action-gh-release@v1
  if: |
    matrix.kind == 'test_release' &&
    github.repository == 'denoland/deno' &&
    startsWith(github.ref, 'refs/tags/')
  env:
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  with:
    files: |
      target/release/deno-x86_64-pc-windows-msvc.zip
      target/release/deno-x86_64-unknown-linux-gnu.zip
      target/release/deno-x86_64-apple-darwin.zip
      target/release/deno_src.tar.gz
      target/release/lib.deno.d.ts
    draft: true