From 6c99d4fe221c1e3a17e7de9e55146f76e86320a6 Mon Sep 17 00:00:00 2001 From: Zhengchao An Date: Sat, 1 Aug 2026 11:28:14 +0800 Subject: [PATCH] ci: stop the weekly flake.lock bot from running the whole pipeline (#5539) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit nix-flake-update opens a PR every Sunday that changes flake.lock and nothing else. flake.lock is consumed only by Nix packaging — cargo never reads it — yet it was in no paths filter, so each of those PRs ran the full Continuous Integration pipeline and the merge then ran Build and Release too. Added to all four lists that have to agree: ci.yml's push and pull_request paths-ignore, build.yml's push paths-ignore, and ci-docs-only.yml's paths. The cron stays. flake.lock still has consumers — anyone running `nix build` or `nix develop` — so stopping the updates would remove the workflow's output, not just its CI cost. Those four lists drifting is a silent failure, so scripts/check_ci_paths_sync.sh now asserts the pair that matters: ci.yml's pull_request paths-ignore must equal ci-docs-only.yml's paths. An entry present only in the first means a PR touching those files triggers neither workflow, nobody reports "Test and Lint" or "Quick Checks", and the PR waits on a required check forever. It also asserts both companion job names still exist, since renaming one produces the same hang. The push list is not compared: no required check is reported for push events. Also in this cleanup: - nix-flake-update's GITHUB_TOKEN drops to contents: read. The branch push and the pull request are both made by update-flake-lock with the FLAKE_UPDATE_TOKEN PAT, so the write scopes were an unused repo-write credential on an unattended weekly job. - build.yml's build_docker dispatch input is documented as advisory. docker.yml triggers on workflow_run and requires the triggering event to be a tag push, so a manual dispatch never reaches it whatever this input says. - The nine disabled workflow files get a banner saying so. Their disabled_manually state lives in GitHub's UI and is invisible when reading the file, which has already misled one audit into treating dead workflows as live. Refs: rustfs/backlog#1598, rustfs/backlog#1603 --- .github/workflows/build.yml | 8 +- .github/workflows/ci-docs-only.yml | 3 + .github/workflows/ci.yml | 4 + .github/workflows/e2e-s3tests.yml | 7 ++ .github/workflows/fuzz.yml | 7 ++ .github/workflows/issue-translator.yml | 7 ++ .github/workflows/minio-interop.yml | 7 ++ .github/workflows/mint.yml | 7 ++ .github/workflows/nix-flake-update.yml | 7 +- .github/workflows/nix.yml | 7 ++ .github/workflows/performance-ab.yml | 7 ++ .../schedule-failure-alert-drill.yml | 7 ++ .github/workflows/stale.yml | 7 ++ scripts/check_ci_paths_sync.sh | 83 +++++++++++++++++++ 14 files changed, 165 insertions(+), 3 deletions(-) create mode 100755 scripts/check_ci_paths_sync.sh diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 63c555f63..5f39fe992 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -50,12 +50,18 @@ on: - "**/*.svg" - ".gitignore" - ".dockerignore" + - "flake.lock" schedule: - cron: "0 1 * * 0" # Weekly on Sunday 01:00 UTC (staggered after the ci.yml midnight cron) workflow_dispatch: inputs: build_docker: - description: "Build and push Docker images after binary build" + # Advisory only. docker.yml triggers on workflow_run and its job-level + # condition requires the triggering event to be a tag push, so a manual + # dispatch of this workflow never produces images regardless of this + # value. Kept because the summary step reports it; wiring it up would + # mean teaching docker.yml's version parser a second event shape. + description: "Build and push Docker images after binary build (ignored: dispatch runs never reach docker.yml)" required: false default: true type: boolean diff --git a/.github/workflows/ci-docs-only.yml b/.github/workflows/ci-docs-only.yml index 01e0db124..cdb9de7e0 100644 --- a/.github/workflows/ci-docs-only.yml +++ b/.github/workflows/ci-docs-only.yml @@ -53,6 +53,7 @@ on: - ".github/workflows/build.yml" - ".github/workflows/docker.yml" - ".github/workflows/audit.yml" + - "flake.lock" permissions: contents: read @@ -110,6 +111,8 @@ jobs: - name: Check no planning docs committed run: ./scripts/check_no_planning_docs.sh + - name: Check CI paths stay in sync + run: ./scripts/check_ci_paths_sync.sh test-and-lint: name: Test and Lint diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e2a346cd0..085ed83a4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,6 +33,7 @@ on: - ".github/workflows/build.yml" - ".github/workflows/docker.yml" - ".github/workflows/audit.yml" + - "flake.lock" pull_request: types: [ opened, synchronize, reopened, closed ] branches: [ main ] @@ -54,6 +55,7 @@ on: - ".github/workflows/build.yml" - ".github/workflows/docker.yml" - ".github/workflows/audit.yml" + - "flake.lock" merge_group: types: [ checks_requested ] schedule: @@ -142,6 +144,8 @@ jobs: - name: Check no planning docs committed run: ./scripts/check_no_planning_docs.sh + - name: Check CI paths stay in sync + run: ./scripts/check_ci_paths_sync.sh test-and-lint: name: Test and Lint diff --git a/.github/workflows/e2e-s3tests.yml b/.github/workflows/e2e-s3tests.yml index 23598e803..ce26fc235 100644 --- a/.github/workflows/e2e-s3tests.yml +++ b/.github/workflows/e2e-s3tests.yml @@ -45,6 +45,13 @@ # The PR gate (ci.yml s3-implemented-tests) is unaffected: it avoids Docker # via DEPLOY_MODE=binary and defers all pip setup to run.sh's self-bootstrap. +# DISABLED. This workflow is switched off in the repository's Actions settings +# (state: disabled_manually) and does not run on any trigger, including its cron +# and workflow_dispatch. That state lives in GitHub's UI and is invisible when +# reading this file, which has already misled at least one audit — hence this +# banner. Re-enabling is a UI action; anyone doing so should first check that the +# workflow still matches the current CI layout. See rustfs/backlog#1603. +# name: e2e-s3tests on: diff --git a/.github/workflows/fuzz.yml b/.github/workflows/fuzz.yml index 3fe5ab0de..1082da1de 100644 --- a/.github/workflows/fuzz.yml +++ b/.github/workflows/fuzz.yml @@ -12,6 +12,13 @@ # See the License for the specific language governing permissions and # limitations under the License. +# DISABLED. This workflow is switched off in the repository's Actions settings +# (state: disabled_manually) and does not run on any trigger, including its cron +# and workflow_dispatch. That state lives in GitHub's UI and is invisible when +# reading this file, which has already misled at least one audit — hence this +# banner. Re-enabling is a UI action; anyone doing so should first check that the +# workflow still matches the current CI layout. See rustfs/backlog#1603. +# name: Fuzz on: diff --git a/.github/workflows/issue-translator.yml b/.github/workflows/issue-translator.yml index 1bb98a956..04a57563d 100644 --- a/.github/workflows/issue-translator.yml +++ b/.github/workflows/issue-translator.yml @@ -12,6 +12,13 @@ # See the License for the specific language governing permissions and # limitations under the License. +# DISABLED. This workflow is switched off in the repository's Actions settings +# (state: disabled_manually) and does not run on any trigger, including its cron +# and workflow_dispatch. That state lives in GitHub's UI and is invisible when +# reading this file, which has already misled at least one audit — hence this +# banner. Re-enabling is a UI action; anyone doing so should first check that the +# workflow still matches the current CI layout. See rustfs/backlog#1603. +# name: "issue-translator" on: issue_comment: diff --git a/.github/workflows/minio-interop.yml b/.github/workflows/minio-interop.yml index 585dca543..b043b84a1 100644 --- a/.github/workflows/minio-interop.yml +++ b/.github/workflows/minio-interop.yml @@ -23,6 +23,13 @@ # Runner: GitHub-hosted `ubuntu-latest`. It reliably ships Docker + Python, # unlike the self-hosted fleet, whose pods drift in Docker/pip availability # (see the infra note in e2e-s3tests.yml). Nightly + manual only. +# DISABLED. This workflow is switched off in the repository's Actions settings +# (state: disabled_manually) and does not run on any trigger, including its cron +# and workflow_dispatch. That state lives in GitHub's UI and is invisible when +# reading this file, which has already misled at least one audit — hence this +# banner. Re-enabling is a UI action; anyone doing so should first check that the +# workflow still matches the current CI layout. See rustfs/backlog#1603. +# name: minio-interop on: diff --git a/.github/workflows/mint.yml b/.github/workflows/mint.yml index a7ca90658..20f449310 100644 --- a/.github/workflows/mint.yml +++ b/.github/workflows/mint.yml @@ -45,6 +45,13 @@ # docker-capable self-hosted `dind-sm-standard-2` label was the alternative but # has fewer cores and reintroduces fleet-state risk for no reliability gain. +# DISABLED. This workflow is switched off in the repository's Actions settings +# (state: disabled_manually) and does not run on any trigger, including its cron +# and workflow_dispatch. That state lives in GitHub's UI and is invisible when +# reading this file, which has already misled at least one audit — hence this +# banner. Re-enabling is a UI action; anyone doing so should first check that the +# workflow still matches the current CI layout. See rustfs/backlog#1603. +# name: mint on: diff --git a/.github/workflows/nix-flake-update.yml b/.github/workflows/nix-flake-update.yml index 9857d0b55..8a5ec5ba6 100644 --- a/.github/workflows/nix-flake-update.yml +++ b/.github/workflows/nix-flake-update.yml @@ -19,9 +19,12 @@ on: schedule: - cron: '0 5 * * 0' # Weekly on Sunday 05:00 UTC (staggered after the midnight ci/build crons) +# GITHUB_TOKEN only needs to read the repository here: the branch push and the +# pull request are both created by update-flake-lock using the +# FLAKE_UPDATE_TOKEN PAT below, not by this token. Leaving write on it hands a +# repo-write credential to an unattended weekly job that does not use it. permissions: - contents: write - pull-requests: write + contents: read concurrency: group: ${{ github.workflow }}-${{ github.ref }} diff --git a/.github/workflows/nix.yml b/.github/workflows/nix.yml index 85829ef47..6dc80ef3d 100644 --- a/.github/workflows/nix.yml +++ b/.github/workflows/nix.yml @@ -12,6 +12,13 @@ # See the License for the specific language governing permissions and # limitations under the License. +# DISABLED. This workflow is switched off in the repository's Actions settings +# (state: disabled_manually) and does not run on any trigger, including its cron +# and workflow_dispatch. That state lives in GitHub's UI and is invisible when +# reading this file, which has already misled at least one audit — hence this +# banner. Re-enabling is a UI action; anyone doing so should first check that the +# workflow still matches the current CI layout. See rustfs/backlog#1603. +# name: Nix CI on: diff --git a/.github/workflows/performance-ab.yml b/.github/workflows/performance-ab.yml index 7a831303c..ad3e6f1c6 100644 --- a/.github/workflows/performance-ab.yml +++ b/.github/workflows/performance-ab.yml @@ -22,6 +22,13 @@ # a deliberate correctness cost (e.g. the #4221 fsync durability fix) is # recorded but does not block (rustfs/backlog#935 correction 1). +# DISABLED. This workflow is switched off in the repository's Actions settings +# (state: disabled_manually) and does not run on any trigger, including its cron +# and workflow_dispatch. That state lives in GitHub's UI and is invisible when +# reading this file, which has already misled at least one audit — hence this +# banner. Re-enabling is a UI action; anyone doing so should first check that the +# workflow still matches the current CI layout. See rustfs/backlog#1603. +# name: Performance A/B on: diff --git a/.github/workflows/schedule-failure-alert-drill.yml b/.github/workflows/schedule-failure-alert-drill.yml index 8154e1c79..648cfb94c 100644 --- a/.github/workflows/schedule-failure-alert-drill.yml +++ b/.github/workflows/schedule-failure-alert-drill.yml @@ -24,6 +24,13 @@ # The run itself is expected to end red (the forced failure); only the # alert-on-failure job result matters. +# DISABLED. This workflow is switched off in the repository's Actions settings +# (state: disabled_manually) and does not run on any trigger, including its cron +# and workflow_dispatch. That state lives in GitHub's UI and is invisible when +# reading this file, which has already misled at least one audit — hence this +# banner. Re-enabling is a UI action; anyone doing so should first check that the +# workflow still matches the current CI layout. See rustfs/backlog#1603. +# name: Schedule Failure Alert Drill on: diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 3152e720d..6eb903db8 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -12,6 +12,13 @@ # See the License for the specific language governing permissions and # limitations under the License. +# DISABLED. This workflow is switched off in the repository's Actions settings +# (state: disabled_manually) and does not run on any trigger, including its cron +# and workflow_dispatch. That state lives in GitHub's UI and is invisible when +# reading this file, which has already misled at least one audit — hence this +# banner. Re-enabling is a UI action; anyone doing so should first check that the +# workflow still matches the current CI layout. See rustfs/backlog#1603. +# name: "Mark stale issues" on: schedule: diff --git a/scripts/check_ci_paths_sync.sh b/scripts/check_ci_paths_sync.sh new file mode 100755 index 000000000..152129fd4 --- /dev/null +++ b/scripts/check_ci_paths_sync.sh @@ -0,0 +1,83 @@ +#!/usr/bin/env bash +# ci.yml's pull_request paths-ignore and ci-docs-only.yml's paths must be equal. +# +# ci-docs-only.yml exists to report the required checks for pull requests that +# ci.yml skips. The two lists are the complement of each other, so any drift +# breaks one of two ways, both silent: +# +# - an entry only in ci.yml's paths-ignore: a PR touching only those files +# triggers neither workflow, nobody reports "Test and Lint" or "Quick +# Checks", and the PR waits on a required check forever; +# - an entry only in ci-docs-only.yml's paths: both workflows run, which is +# merely wasteful — but it also means the lists no longer describe the same +# intent, and the next edit is made against a wrong assumption. +# +# The push paths-ignore in ci.yml is deliberately NOT compared: no required +# check is reported for push events, so it does not have to pair with anything. +# +# Also asserts ci-docs-only.yml still declares both companion job names, since a +# rename there produces exactly the permanent-pending failure above. +# +# Usage: scripts/check_ci_paths_sync.sh +set -euo pipefail + +cd "$(dirname "$0")/.." + +CI=".github/workflows/ci.yml" +DOCS=".github/workflows/ci-docs-only.yml" + +# Print the quoted list items that follow $2 within the block introduced by $1. +# Both files keep these as a flat list of quoted scalars, so no YAML parser is +# needed and the script stays dependency-free like its check_* siblings. +extract() { + local file="$1" event="$2" key="$3" + awk -v event="$event" -v key="$key" ' + $0 ~ "^ " event ":[[:space:]]*$" { in_event = 1; next } + in_event && /^ [a-z_]+:[[:space:]]*$/ { in_event = 0 } + in_event && $0 ~ "^ " key ":[[:space:]]*$" { in_list = 1; next } + in_list { + if ($0 ~ /^ - /) { + item = $0 + sub(/^ - /, "", item) + gsub(/^"|"$/, "", item) + print item + next + } + if ($0 !~ /^[[:space:]]*#/ && $0 !~ /^[[:space:]]*$/) in_list = 0 + } + ' "$file" | sort +} + +ci_list="$(extract "$CI" "pull_request" "paths-ignore")" +docs_list="$(extract "$DOCS" "pull_request" "paths")" + +if [ -z "$ci_list" ] || [ -z "$docs_list" ]; then + echo "ERROR: could not read one of the path lists — did the file structure change?" >&2 + echo " $CI pull_request.paths-ignore: $(printf '%s' "$ci_list" | grep -c . || true) entries" >&2 + echo " $DOCS pull_request.paths: $(printf '%s' "$docs_list" | grep -c . || true) entries" >&2 + exit 1 +fi + +status=0 + +if ! diff_out="$(diff <(printf '%s\n' "$ci_list") <(printf '%s\n' "$docs_list"))"; then + echo "ERROR: $CI pull_request paths-ignore and $DOCS paths have drifted." >&2 + echo " '<' is only in $CI, '>' is only in $DOCS:" >&2 + printf '%s\n' "$diff_out" | sed 's/^/ /' >&2 + status=1 +fi + +for job_name in "Test and Lint" "Quick Checks"; do + if ! grep -q "name: ${job_name}\$" "$DOCS"; then + echo "ERROR: $DOCS no longer declares a job named '${job_name}'." >&2 + echo " It is a required status check; without a companion job here, a" >&2 + echo " docs-only PR waits on it forever." >&2 + status=1 + fi +done + +if [ "$status" -ne 0 ]; then + exit 1 +fi + +echo "OK: ci.yml and ci-docs-only.yml path lists agree ($(printf '%s\n' "$ci_list" | wc -l | tr -d ' ') entries)"