diff --git a/.github/actions/edit-already-passed/action.yml b/.github/actions/edit-already-passed/action.yml new file mode 100644 index 00000000000..10ac7cc3144 --- /dev/null +++ b/.github/actions/edit-already-passed/action.yml @@ -0,0 +1,36 @@ +name: Edit already passed +description: | + Reports whether this run comes from a pull request title or body edit on a + head commit whose previous run of the same workflow passed. Such an edit + cannot change the result, so callers skip their checks and the skipped + required checks still pass. A base change, a first run, or a previous run + that failed, was cancelled or is unfinished returns false and runs them again. + The calling job needs `actions: read`. + +outputs: + passed: + description: "`true` when the previous run on this head commit passed." + value: ${{ steps.previous.outputs.passed }} + +runs: + using: composite + steps: + - id: previous + if: github.event.action == 'edited' && github.event.changes.base == null + shell: bash + env: + GH_TOKEN: ${{ github.token }} + HEAD_SHA: ${{ github.event.pull_request.head.sha }} + HEAD_REF: ${{ github.event.pull_request.head.ref }} + HEAD_REPO: ${{ github.event.pull_request.head.repo.full_name }} + run: | + workflow="$(basename "${GITHUB_WORKFLOW_REF%%@*}")" + conclusion="$(gh api "repos/$GITHUB_REPOSITORY/actions/workflows/$workflow/runs?event=pull_request&head_sha=$HEAD_SHA&per_page=100" --jq ' + [.workflow_runs[] + | select(.id < (env.GITHUB_RUN_ID | tonumber) + and .head_branch == env.HEAD_REF + and .head_repository.full_name == env.HEAD_REPO)] + | max_by(.id) | .conclusion // "none"')" || conclusion="lookup failed" + echo "Previous $workflow run on $HEAD_SHA: $conclusion" + if [ "$conclusion" = success ]; then passed=true; else passed=false; fi + echo "passed=$passed" >> "$GITHUB_OUTPUT" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 86b8f6fc959..616bc1512cb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -65,6 +65,10 @@ jobs: name: Detect changes runs-on: ubuntu-latest timeout-minutes: 2 + permissions: + actions: read + contents: read + pull-requests: read outputs: code: ${{ (github.event_name == 'workflow_dispatch' && inputs.catalog_publish) && 'true' || steps.filter.outputs.code }} docs_catalog: ${{ steps.filter.outputs.docs_catalog }} @@ -83,6 +87,9 @@ jobs: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 with: fetch-depth: 0 + - name: Check whether this commit already passed before a title or body edit + id: edit + uses: ./.github/actions/edit-already-passed - name: Reject accidental file deletions if: github.event_name == 'pull_request' run: node scripts/check-no-main-deletions.mjs --base origin/main @@ -102,6 +109,7 @@ jobs: run: sh scripts/check-large-files.sh --range "$BASE_SHA" HEAD - uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4 id: filter + if: steps.edit.outputs.passed != 'true' with: token: "" base: ${{ steps.base.outputs.sha }} diff --git a/.github/workflows/regression.yml b/.github/workflows/regression.yml index 8374cee9e5d..7401a42c58f 100644 --- a/.github/workflows/regression.yml +++ b/.github/workflows/regression.yml @@ -40,8 +40,11 @@ jobs: name: Detect changes runs-on: ubuntu-latest timeout-minutes: 2 + permissions: + actions: read + contents: read outputs: - code: ${{ (github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && inputs.catalog_publish)) && 'true' || steps.filter.outputs.code }} + code: ${{ (github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && inputs.catalog_publish)) && 'true' || steps.edit.outputs.passed == 'true' && 'false' || steps.filter.outputs.code }} matrix: ${{ steps.shards.outputs.matrix }} steps: # Force git-based change detection instead of the pull_request REST API. @@ -50,6 +53,9 @@ jobs: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 with: fetch-depth: 0 + - name: Check whether this commit already passed before a title or body edit + id: edit + uses: ./.github/actions/edit-already-passed # HEAD is the PR merge ref; its first parent is the current base tip, unlike the payload's # base.sha, which goes stale and would count the base's newer commits as the PR's changes. - name: Find the pull request's current base @@ -61,7 +67,7 @@ jobs: echo "sha=$sha" >> "$GITHUB_OUTPUT" - uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4 id: filter - if: github.event_name != 'schedule' + if: github.event_name != 'schedule' && steps.edit.outputs.passed != 'true' with: token: "" base: ${{ steps.base.outputs.sha }} diff --git a/.github/workflows/windows-render.yml b/.github/workflows/windows-render.yml index 7c717fab650..1bdbcec71de 100644 --- a/.github/workflows/windows-render.yml +++ b/.github/workflows/windows-render.yml @@ -44,6 +44,9 @@ jobs: name: Detect changes runs-on: ubuntu-latest timeout-minutes: 2 + permissions: + actions: read + contents: read outputs: code: ${{ steps.filter.outputs.code }} steps: @@ -53,6 +56,9 @@ jobs: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 with: fetch-depth: 0 + - name: Check whether this commit already passed before a title or body edit + id: edit + uses: ./.github/actions/edit-already-passed # HEAD is the PR merge ref; its first parent is the current base tip, unlike the payload's # base.sha, which goes stale and would count the base's newer commits as the PR's changes. - name: Find the pull request's current base @@ -64,6 +70,7 @@ jobs: echo "sha=$sha" >> "$GITHUB_OUTPUT" - uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4 id: filter + if: steps.edit.outputs.passed != 'true' with: token: "" base: ${{ steps.base.outputs.sha }}