记一次对 GitHub Actions 的优化

上回已经谈到在使用 GitHub+jsDelivr 作为 OSS 的时候,还可以配合 GitHub Actions 实现缓存的自动刷新,详情参考:

但这个解决方案并不够好。不难发现,每次我们 push 上去新的文件,Action 将全量刷新缓存。这有什么问题呢?

最直观的一点就是随着文件数量的增加,单次 Action 的执行时间将不断变长,最终达到一个你无法接受且 GitHub 也不支持的超长运行时间;除此之外,它做的无用功非常多,它的大部分时间都用在本不需要处理的已有文件上,而只有很少的时间在处理真正需要处理的新增文件。

这里的优化空间是非常大的,而且思路也很简单:将全量刷新改为增量刷新即可。预先处理出本次 commit 涉及的文件,在请求时只请求这一部分就可以了。

实际实现起来还需要一定技巧。幸运的是,GitHub Actions 市场中有相关的“获取 commit 涉及文件”的现成轮子,这里采用 lots0logs/gh-action-get-changed-files。从输出中可以获取的共有五种类型,考虑到我们的需求,只关注 addedrenamedmodified 即可。依次请求这三类文件中的所有文件。

# Auto release and force cache

name: Auto release and force cache

# Controls when the action will run. Triggers the workflow only on push
# events but only for the master branch
on:
  push:
    branches: [ master ]

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
  build:
    # The type of runner that the job will run on
    runs-on: ubuntu-18.04

    # Steps represent a sequence of tasks that will be executed as part of the job
    steps:
    # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
    - uses: actions/checkout@v2
    
    # Get changed files
    - id: files
      name: Get changed files
      uses: lots0logs/[email protected]
      with:
        token: ${{ secrets.GITHUB_TOKEN }}
    
    # Check files
    - id: check
      name: Check files
      run: |
        # Check files
        readarray -t added_files <<<"$(jq -r '.[]' <<<'${{ steps.files.outputs.added }}')"
        readarray -t renamed_files <<<"$(jq -r '.[]' <<<'${{ steps.files.outputs.renamed }}')"
        readarray -t modified_files <<<"$(jq -r '.[]' <<<'${{ steps.files.outputs.modified }}')"
        for file in ${added_files[@]}; do
          if [[ $file != .github* ]]; then
            echo "::set-output name=continue::1"
            exit
          fi
        done
        for file in ${renamed_files[@]}; do
          if [[ $file != .github* ]]; then
            echo "::set-output name=continue::1"
            exit
          fi
        done
        for file in ${modified_files[@]}; do
          if [[ $file != .github* ]]; then
            echo "::set-output name=continue::1"
            exit
          fi
        done
        echo "::set-output name=continue::0"
    # Auto release
    - name: Auto release
      if: steps.check.outputs.continue == 1
      uses: marvinpinto/action-automatic-releases@latest
      with:
        repo_token: ${{ secrets.GITHUB_TOKEN }}
        automatic_release_tag: ${{ github.run_id }}
        prerelease: false
        title: ${{ github.run_id }}

    # Force cache
    - name: Force cache
      if: steps.check.outputs.continue == 1
      run: |
        # Force cache
        function uriencode {
          uri=`jq -nr --arg v "$1" '$v|@uri'`
          echo ${uri//%2[Ff]//}
        }
        function force_cache() {
          curl -s -o /dev/null --retry 5 "https://cdn.jsdelivr.net/gh/wuxianucw/ucw-desu@master/"$1
          url="https://cdn.jsdelivr.net/gh/wuxianucw/ucw-desu@${{ github.run_id }}/"$1
          code=`curl -s -o /dev/null --retry 5 -w %{http_code} $url`
          echo "Force cache "$url" [ "$code" ]"
          if [ $code != "200" ]
          then
              url="https://cdn.jsdelivr.net/gh/wuxianucw/ucw-desu@master/"$1
              echo " - Try master "$url" [ `curl -s -o /dev/null --retry 5 -w %{http_code} $url` ]"
              url="https://cdn.jsdelivr.net/gh/wuxianucw/ucw-desu@${{ github.run_id }}/"$1
              echo " - Retry "$url" [ `curl -s -o /dev/null --retry 5 -w %{http_code} $url` ]"
          fi
          url="https://cdn.jsdelivr.net/gh/wuxianucw/ucw-desu/"$1
          echo " -> "$url" [ `curl -s -o /dev/null --retry 5 -w %{http_code} $url` ]"
        }
        readarray -t added_files <<<"$(jq -r '.[]' <<<'${{ steps.files.outputs.added }}')"
        readarray -t renamed_files <<<"$(jq -r '.[]' <<<'${{ steps.files.outputs.renamed }}')"
        readarray -t modified_files <<<"$(jq -r '.[]' <<<'${{ steps.files.outputs.modified }}')"
        for file in ${added_files[@]}; do
          if [[ $file != .github* ]]; then
            force_cache `uriencode $file`
          fi
        done
        for file in ${renamed_files[@]}; do
          if [[ $file != .github* ]]; then
            force_cache `uriencode $file`
          fi
        done
        for file in ${modified_files[@]}; do
          if [[ $file != .github* ]]; then
            force_cache `uriencode $file`
          fi
        done

代码长了不少,但至少实现了增量更新,节约了我们的时间和 GitHub Actions 的运行资源。

最后修改:2021 年 02 月 17 日 06 : 06 PM
欢迎投食喵 ~

发表评论