如何安排 GitHub Actions 每晚构建,但仅在最近代码更改的地方运行它?

bur*_*rsk 9 github-actions

我想使用 GitHub Actions 进行自动夜间构建(来自开发分支的每日快照)。

为了降低计费成本,我希望 GitHub Actions 工作流仅在自上次 GitHub Actions 夜间构建工作流运行以来有新提交时触发(或执行操作)。

如何安排 GitHub Actions 每晚构建,但仅在自上次每晚运行以来代码发生更改的地方运行它?

请注意,还有其他 GitHub 操作工作流程,不会干扰此夜间构建。

Sam*_*ira 6

我有一个可行的解决方案,与您的情况略有不同,但调整起来应该不难。主要目标完全相同 - 如果不需要,不要在日常运行中浪费 CI 时间。

虽然不可能 (AFAIK) 限制计划根本不运行,但您可以通过运行一个小的 shell 脚本作为第一步来降低工作流执行时间,甚至在检出存储库之前。第二部分是如果存储库没有新的提交/没有要运行的东西,则禁用所有其他步骤。

完整示例,稍后逐个讨论,以及如何将其应用于您的用例。

TL;DR - bash, curl, jq.

  - name:  Activity check
    run:   |
           :
           curl -sL https://api.github.com/repos/$GITHUB_REPOSITORY/commits | jq -r '[.[] | select(.author.login != "${{ secrets.ANTALASKAYA_LOGIN }}")][0]' > $HOME/commit.json
           date="$(jq -r '.commit.author.date' $HOME/commit.json)"
           timestamp=$(date --utc -d "$date" +%s)
           author="$(jq -r '.commit.author.name' $HOME/commit.json)"
           url="$(jq -r '.html_url' $HOME/commit.json)"
           days=$(( ( $(date --utc +%s) - $timestamp ) / 86400 ))
           rm -f $HOME/commit.json
           echo "Repository activity : $timestamp $author $url"
           alive=0
           if [ "${{ github.event_name }}" == "repository_dispatch" ]; then
              echo "[WARNING] Ignoring activity limits : workflow triggered manually"
              alive=1
           else
              if [ $days -gt 2 ]; then
                 echo "[WARNING] Repository activity : $days days ago"
              fi
              if [ $days -lt 8 ]; then
                 echo Repository active : $days days
                 alive=1
              else
                 echo "[WARNING] Repository not updated : event<${{ github.event_name }}> not allowed to modify stale repository"
              fi
           fi
           if [ $alive -eq 1 ]; then
              echo ::set-env name=GHA_REPO_ALIVE::true
           fi
    shell: bash
Run Code Online (Sandbox Code Playgroud)

开始时,我使用 GitHub API 获取最后一次非自动提交(并将结果保存到 .json)。在我的情况下,所有“每晚”构建提交结果都通过专用机器人帐户返回到存储库,因此很容易过滤掉。

curl -sL https://api.github.com/repos/$GITHUB_REPOSITORY/commits | jq -r '[.[] | select(.author.login != "${{ secrets.ANTALASKAYA_LOGIN }}")][0]' > $HOME/commit.json
Run Code Online (Sandbox Code Playgroud)

接下来,我正在提取上次提交的时间戳(和其他一些东西)并将其转换为经过的天数。在您的情况下,您很可能希望在这里使用小时数。

date="$(jq -r '.commit.author.date' $HOME/commit.json)"
timestamp=$(date --utc -d "$date" +%s)
author="$(jq -r '.commit.author.name' $HOME/commit.json)"
url="$(jq -r '.html_url' $HOME/commit.json)"
days=$(( ( $(date --utc +%s) - $timestamp ) / 86400 ))
rm -f $HOME/commit.json
echo "Repository activity : $timestamp $author $url"
Run Code Online (Sandbox Code Playgroud)

工作流可以运行时有几种不同的场景(push工作流文件已更改repository_dispatch,,schedule),因此我将最终活动检查结果保留为稍后检查的本地变量。假设存储库默认不需要更新。

alive=0
Run Code Online (Sandbox Code Playgroud)

接下来是repository_dispatch允许手动触发计划的处理;这将强制工作流在忽略任何限制的情况下运行。

if [ "${{ github.event_name }}" == "repository_dispatch" ]; then
   echo "[WARNING] Ignoring activity limits : workflow triggered manually"
   alive=1
else
Run Code Online (Sandbox Code Playgroud)

在没有自动提交的第三天,我在日志中添加条目,只是为了好玩。

if [ $days -gt 2 ]; then
   echo "[WARNING] Repository activity : $days days ago"
fi
Run Code Online (Sandbox Code Playgroud)

如果最后一次提交是在上周内,将存储库标记为活动,否则什么都不做。

if [ $days -lt 8 ]; then
   echo Repository active : $days days
   alive=1
else
    echo "[WARNING] Repository not updated : event<${{ github.event_name }}> not allowed to modify stale repository"
fi
Run Code Online (Sandbox Code Playgroud)

最后,如果有工作要做,将局部变量保存为全局变量。在这里使用::set-env(或::set-output)很重要,因此可以在执行步骤之前检查变量。

if [ $alive -eq 1 ]; then
   echo ::set-env name=GHA_REPO_ALIVE::true
fi
Run Code Online (Sandbox Code Playgroud)

活动检查之后的所有步骤都应该在做任何事情之前检查这个全局变量,以节省时间和/或金钱。

- name: Clone
  if:   env.GHA_REPO_ALIVE == 'true'
  uses: actions/checkout@v2
Run Code Online (Sandbox Code Playgroud)

在野外:


现在关于对你的情况采用这样的解决方案:

如果您不提交返回结果,您可以通过从 API 获取最后一次提交(尽管作者)并检查经过的时间来简化第一部分。如果过去 24 小时内有任何提交,则存储库应标记为活动。

如果您只想运行构建,则可以忽略部件检查repository_dispatchpush场景。但是,我发现使用一些非schedule触发器来运行构建而无需等待非常有用;我强烈建议保留它以备将来调整。

跳过作者/网址提取并禁用日志记录可以节省几毫秒;)

可能有一些操作可以提供相同的功能,但我觉得 shell 脚本 + API 总是会更快。他们也有可能做完全相同的事情,只是“浪费”下载和执行操作所需的额外时间。


小智 5

你可以这样做:

  1. 首先添加这个作业:
  check_date:
    runs-on: ubuntu-latest
    name: Check latest commit
    outputs:
      should_run: ${{ steps.should_run.outputs.should_run }}
    steps:
      - uses: actions/checkout@v2
      - name: print latest_commit
        run: echo ${{ github.sha }}

      - id: should_run
        continue-on-error: true
        name: check latest commit is less than a day
        if: ${{ github.event_name == 'schedule' }}
        run: test -z $(git rev-list  --after="24 hours"  ${{ github.sha }}) && echo "::set-output name=should_run::false"
Run Code Online (Sandbox Code Playgroud)
  1. 将此添加到您工作流程中的所有其他作业中:
    needs: check_date
    if: ${{ needs.check_date.outputs.should_run != 'false' }}
Run Code Online (Sandbox Code Playgroud)

例如:

  do_something:
    needs: check_date
    if: ${{ needs.check_date.outputs.should_run != 'false' }}
    runs-on: windows-latest
    name: do something.
    steps:
      - uses: actions/checkout@v2
Run Code Online (Sandbox Code Playgroud)

来源