You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于上一次成功运行的差异确定变更文件并触发对应测试?

问题背景与需求

我当前使用actions/checkout@v3配合tj-actions/changed-files@v37搭建GitHub Action,用来检测PR中的变更文件,以此决定需要运行的测试用例。由于所有测试都是必填项,无需运行的测试会被自动标记为“通过”(绿色对勾)。

目前遇到的问题是:tj-actions/changed-files@v37检测的是整个PR的全部变更文件,而非自上一次Action成功运行以来的变更文件。我希望基于上一次成功测试的记录来确定变更范围,只触发对应需要的测试动作,请问有没有可行的实现方式?


现有主Workflow示例

name: "Example tests"
on:
    push:
        branches:
            - main
            - dev
    pull_request:
        types: [opened, reopened, synchronize]
        branches-ignore: [releases]

jobs:
    changed-files:
        name: Determine changed files
        runs-on: [self-hosted, external]
        outputs:
            cpp: ${{ steps.changed-files.outputs.cpp_any_modified }}
        steps:
            - name: Checkout project
              uses: actions/checkout@v3
              with:
                  fetch-depth: 0
                  submodules: recursive
                  token: ${{ secrets.GITHUBTOKEN }}
            - name: Check which files have changed
              id: changed-files
              uses: tj-actions/changed-files@v37
              with:
                  files_yaml: |
                      cpp:
                        - cpp/**
    cpp_test:
        needs: changed-files
        uses: My-Companys-Name/github_workflows/.github/workflows/test.yml@main
        with:
            branch_name: ${{ github.head_ref || github.ref_name }}
            test_file: cpp/test_suite.yml
            skip: ${{ needs.changed-files.outputs.cpp == 'false' }}

可复用工作流test.yml示例

name: "Trigger Tests to External Machine"
on:
  workflow_call:
    inputs:
      branch_name:
        required: true
        type: string
      test_file:
        required: true
        type: string
      skip:
        required: false
        type : boolean
        default: false

run-name: ${{ github.actor }} is running the ${{ inputs.test_file }} test

jobs:
  testing:
    name: Tests
    runs-on: [self-hosted, external]
    steps:
      - name: Dispatch test to external machine
        if: ${{ !inputs.skip }}
        run: | 
          # Send the test file to the external machine using http requests
      - name: Display test results
        if: ${{ !inputs.skip }}
        run: |
          # Display test results from the external machine
      - name: Notify supervisor of test cancellation
      - name: Download test result
        if: ${{ always() && !inputs.skip }}
        run: |
         # Download test results for artifact use
      - name: Upload test results
        if: ${{ always() && !inputs.skip }}
        uses: actions/upload-artifact@v3
        with:
          name: test-results
          path: ${{ env.test_folder }}
          retention-days: 14
      - name: Notify skipped
        if:  ${{ inputs.skip }}
        run: echo 'Test ${{ inputs.test_file }} requested to be skipped'

解决方案

核心思路是记录上一次Action成功运行时的提交哈希,将该哈希与当前提交做对比,从而获取两次成功运行之间的变更文件。以下是两种可行实现方式:

方式一:用GitHub Actions缓存存储上次成功提交哈希

通过缓存持久化上次成功运行的提交哈希,仅在测试成功时更新缓存,确保对比基准的准确性。

修改后的主Workflow

name: "Example tests"
on:
    push:
        branches:
            - main
            - dev
    pull_request:
        types: [opened, reopened, synchronize]
        branches-ignore: [releases]

jobs:
    changed-files:
        name: Determine changed files
        runs-on: [self-hosted, external]
        outputs:
            cpp: ${{ steps.changed-files.outputs.cpp_any_modified }}
            current_sha: ${{ github.sha }}
        steps:
            - name: Checkout project
              uses: actions/checkout@v3
              with:
                  fetch-depth: 0
                  submodules: recursive
                  token: ${{ secrets.GITHUBTOKEN }}
            - name: Get last successful run SHA
              id: get-last-sha
              uses: actions/cache@v3
              with:
                  path: last_success_sha.txt
                  key: ${{ github.ref }}-last-success-sha
            - name: Set comparison SHA
              id: set-compare-sha
              run: |
                  if [ -f last_success_sha.txt ]; then
                      echo "compare_sha=$(cat last_success_sha.txt)" >> "$GITHUB_OUTPUT"
                  else
                      # 首次运行或无成功记录时,用基准分支作为对比起点
                      if [ "${{ github.event_name }}" = "pull_request" ]; then
                          echo "compare_sha=${{ github.base_ref }}" >> "$GITHUB_OUTPUT"
                      else
                          echo "compare_sha=${{ github.ref_name }}" >> "$GITHUB_OUTPUT"
                      fi
                  fi
            - name: Check changed files since last success
              id: changed-files
              uses: tj-actions/changed-files@v37
              with:
                  base_sha: ${{ steps.set-compare-sha.outputs.compare_sha }}
                  files_yaml: |
                      cpp:
                        - cpp/**
    cpp_test:
        needs: changed-files
        uses: My-Companys-Name/github_workflows/.github/workflows/test.yml@main
        with:
            branch_name: ${{ github.head_ref || github.ref_name }}
            test_file: cpp/test_suite.yml
            skip: ${{ needs.changed-files.outputs.cpp == 'false' }}
        outputs:
            test_success: ${{ jobs.testing.outputs.success }}
    update-last-sha:
        needs: cpp_test
        if: ${{ needs.cpp_test.outputs.test_success == 'true' }}
        runs-on: [self-hosted, external]
        steps:
            - name: Save current SHA as last success
              run: echo "${{ needs.changed-files.outputs.current_sha }}" > last_success_sha.txt
            - name: Update cache with new SHA
              uses: actions/cache@v3
              with:
                  path: last_success_sha.txt
                  key: ${{ github.ref }}-last-success-sha

修改后的test.yml(添加测试成功状态输出)

name: "Trigger Tests to External Machine"
on:
  workflow_call:
    inputs:
      branch_name:
        required: true
        type: string
      test_file:
        required: true
        type: string
      skip:
        required: false
        type : boolean
        default: false
    outputs:
      success:
        description: "Whether the test succeeded"
        value: ${{ jobs.testing.outputs.success }}

run-name: ${{ github.actor }} is running the ${{ inputs.test_file }} test

jobs:
  testing:
    name: Tests
    runs-on: [self-hosted, external]
    outputs:
      success: ${{ steps.check-result.outputs.success }}
    steps:
      - name: Dispatch test to external machine
        if: ${{ !inputs.skip }}
        id: dispatch-test
        run: | 
          # Send the test file to the external machine using http requests
      - name: Display test results
        if: ${{ !inputs.skip }}
        run: |
          # Display test results from the external machine
      - name: Download test result
        if: ${{ always() && !inputs.skip }}
        run: |
         # Download test results for artifact use
      - name: Upload test results
        if: ${{ always() && !inputs.skip }}
        uses: actions/upload-artifact@v3
        with:
          name: test-results
          path: ${{ env.test_folder }}
          retention-days: 14
      - name: Notify skipped
        if:  ${{ inputs.skip }}
        run: |
          echo 'Test ${{ inputs.test_file }} requested to be skipped'
          echo "success=true" >> "$GITHUB_OUTPUT"
      - name: Check test result
        id: check-result
        if: ${{ !inputs.skip }}
        run: |
          if [ ${{ steps.dispatch-test.outcome }} == 'success' ]; then
              echo "success=true" >> "$GITHUB_OUTPUT"
          else
              echo "success=false" >> "$GITHUB_OUTPUT"
          fi

方式二:通过GitHub API查询上一次成功运行记录

直接调用GitHub API获取当前分支/PR的上一次成功Workflow运行记录,提取对应的提交哈希作为对比基准。

在changed-files job中添加以下步骤替换缓存逻辑:

- name: Fetch last successful run SHA via API
  id: get-last-sha
  run: |
      # 区分PR和普通分支场景
      if [ "${{ github.event_name }}" = "pull_request" ]; then
          REF="pull/${{ github.event.pull_request.number }}/head"
      else
          REF="${{ github.ref }}"
      fi

      # 调用API获取最近一次成功运行的提交哈希
      LAST_SUCCESS_SHA=$(curl -s -H "Authorization: token ${{ secrets.GITHUBTOKEN }}" \
          "https://api.github.com/repos/${{ github.repository }}/actions/runs?branch=${REF#refs/heads/}&status=success" \
          | jq -r '.workflow_runs[0].head_sha')

      # 无成功记录时回退到基准分支
      if [ "$LAST_SUCCESS_SHA" = "null" ]; then
          if [ "${{ github.event_name }}" = "pull_request" ]; then
              LAST_SUCCESS_SHA="${{ github.base_ref }}"
          else
              LAST_SUCCESS_SHA="${{ github.ref_name }}"
          fi
      fi

      echo "compare_sha=$LAST_SUCCESS_SHA" >> "$GITHUB_OUTPUT"

之后在tj-actions/changed-files步骤中设置base_sha: ${{ steps.get-last-sha.outputs.compare_sha }}即可。

注意:此方式需要Runner环境安装jq工具,且GITHUBTOKEN需具备repo权限。


关键注意事项
  • 缓存键需包含github.ref,确保不同分支/PR的缓存相互独立,避免基准混乱
  • 首次运行或无成功记录时,必须设置合理的默认基准分支(如PR的base分支)
  • 仅在测试成功时更新上次成功提交哈希,避免失败提交被作为对比基准
  • PR场景下需区分head分支和base分支的对比逻辑,确保变更检测准确

内容的提问来源于stack exchange,提问作者cwbusacker

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 10:14:54