mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-25 21:46:50 +00:00
fix(ci): block PR-controlled execution in performance A/B workflow (#5705)
fix(ci): block PR execution in performance workflow
This commit is contained in:
@@ -24,6 +24,7 @@ on:
|
|||||||
- '.github/actions/**'
|
- '.github/actions/**'
|
||||||
- '.github/workflows/**'
|
- '.github/workflows/**'
|
||||||
- 'scripts/release/create_or_update_release.sh'
|
- 'scripts/release/create_or_update_release.sh'
|
||||||
|
- 'scripts/security/check_performance_ab_workflow.sh'
|
||||||
- 'scripts/security/check_preview_release_workflow.sh'
|
- 'scripts/security/check_preview_release_workflow.sh'
|
||||||
- 'scripts/security/check_workflow_pins.sh'
|
- 'scripts/security/check_workflow_pins.sh'
|
||||||
pull_request:
|
pull_request:
|
||||||
@@ -36,6 +37,7 @@ on:
|
|||||||
- '.github/actions/**'
|
- '.github/actions/**'
|
||||||
- '.github/workflows/**'
|
- '.github/workflows/**'
|
||||||
- 'scripts/release/create_or_update_release.sh'
|
- 'scripts/release/create_or_update_release.sh'
|
||||||
|
- 'scripts/security/check_performance_ab_workflow.sh'
|
||||||
- 'scripts/security/check_preview_release_workflow.sh'
|
- 'scripts/security/check_preview_release_workflow.sh'
|
||||||
- 'scripts/security/check_workflow_pins.sh'
|
- 'scripts/security/check_workflow_pins.sh'
|
||||||
schedule:
|
schedule:
|
||||||
@@ -141,6 +143,9 @@ jobs:
|
|||||||
- name: Check preview release workflow policy
|
- name: Check preview release workflow policy
|
||||||
run: ./scripts/security/check_preview_release_workflow.sh
|
run: ./scripts/security/check_preview_release_workflow.sh
|
||||||
|
|
||||||
|
- name: Check performance A/B workflow trust boundary
|
||||||
|
run: ./scripts/security/check_performance_ab_workflow.sh
|
||||||
|
|
||||||
dependency-review:
|
dependency-review:
|
||||||
name: Dependency Review
|
name: Dependency Review
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|||||||
@@ -17,10 +17,10 @@
|
|||||||
# Two entry points, honestly scoped:
|
# Two entry points, honestly scoped:
|
||||||
# * schedule (nightly, on main): post-merge detection — catches a regression
|
# * schedule (nightly, on main): post-merge detection — catches a regression
|
||||||
# within 24h of landing, not before merge.
|
# within 24h of landing, not before merge.
|
||||||
# * pull_request labeled `perf-ab`: opt-in pre-merge gate for a specific PR.
|
# * workflow_dispatch: an explicitly selected trusted ref.
|
||||||
# The `perf-deliberate-tradeoff` label runs the gate with --allow-regression so
|
# The dispatch input can run the gate with --allow-regression so a deliberate
|
||||||
# a deliberate correctness cost (e.g. the #4221 fsync durability fix) is
|
# correctness cost (e.g. the #4221 fsync durability fix) is recorded, not
|
||||||
# recorded but does not block (rustfs/backlog#935 correction 1).
|
# blocked (rustfs/backlog#935 correction 1).
|
||||||
|
|
||||||
# DISABLED. This workflow is switched off in the repository's Actions settings
|
# 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
|
# (state: disabled_manually) and does not run on any trigger, including its cron
|
||||||
@@ -46,8 +46,6 @@ on:
|
|||||||
required: false
|
required: false
|
||||||
default: false
|
default: false
|
||||||
type: boolean
|
type: boolean
|
||||||
pull_request:
|
|
||||||
types: [labeled, synchronize, reopened]
|
|
||||||
push:
|
push:
|
||||||
# Every main commit pre-builds and caches its release binary (perf-3) so the
|
# Every main commit pre-builds and caches its release binary (perf-3) so the
|
||||||
# nightly A/B restores a ready baseline instead of paying the double build.
|
# nightly A/B restores a ready baseline instead of paying the double build.
|
||||||
@@ -55,14 +53,6 @@ on:
|
|||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
pull-requests: write
|
|
||||||
|
|
||||||
# Per-PR: a new push cancels the previous (up to 90-minute) A/B run instead of
|
|
||||||
# stacking them. Nightly schedule and manual dispatch get a unique group and
|
|
||||||
# always run to completion.
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
|
|
||||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
|
||||||
|
|
||||||
env:
|
env:
|
||||||
CARGO_TERM_COLOR: always
|
CARGO_TERM_COLOR: always
|
||||||
@@ -70,8 +60,8 @@ env:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
# perf-3: on every push to main, build the release binary once and cache it
|
# perf-3: on every push to main, build the release binary once and cache it
|
||||||
# keyed by commit SHA (rustfs-baseline-<sha>). The nightly A/B (and, later, the
|
# keyed by commit SHA (rustfs-baseline-<sha>). The warp-ab measurements
|
||||||
# perf-7 PR gate) restore this instead of paying the ~32min-per-side source
|
# restore this instead of paying the ~32min-per-side source
|
||||||
# build. That double build is what pushed the expanded 24-cell nightly past its
|
# build. That double build is what pushed the expanded 24-cell nightly past its
|
||||||
# ceiling — 2026-07-11..07-14 all cancelled on the 120min timeout. Incremental
|
# ceiling — 2026-07-11..07-14 all cancelled on the 120min timeout. Incremental
|
||||||
# builds off the shared cargo cache keep each push cheap, and building on the
|
# builds off the shared cargo cache keep each push cheap, and building on the
|
||||||
@@ -126,17 +116,11 @@ jobs:
|
|||||||
|
|
||||||
warp-ab:
|
warp-ab:
|
||||||
name: Warp A/B budget gate
|
name: Warp A/B budget gate
|
||||||
# Always run on schedule / manual dispatch. Opt-in on PRs: only when the
|
# Always run on schedule / manual dispatch. Never on push — that event only
|
||||||
# `perf-ab` label is present, and for `labeled` events only when the label
|
# feeds build-baseline-cache above.
|
||||||
# being added is `perf-ab` itself (adding an unrelated label to an opted-in
|
|
||||||
# PR must not re-run the gate). Never on push — that event only feeds
|
|
||||||
# build-baseline-cache above.
|
|
||||||
if: >-
|
if: >-
|
||||||
github.event_name == 'schedule' ||
|
github.event_name == 'schedule' ||
|
||||||
github.event_name == 'workflow_dispatch' ||
|
github.event_name == 'workflow_dispatch'
|
||||||
(github.event_name == 'pull_request' &&
|
|
||||||
contains(github.event.pull_request.labels.*.name, 'perf-ab') &&
|
|
||||||
(github.event.action != 'labeled' || github.event.label.name == 'perf-ab'))
|
|
||||||
runs-on: sm-standard-2
|
runs-on: sm-standard-2
|
||||||
# With perf-3's cached baseline binary the common (cache-hit) nightly is
|
# With perf-3's cached baseline binary the common (cache-hit) nightly is
|
||||||
# measurement-only and finishes well under 50min. This ceiling stays
|
# measurement-only and finishes well under 50min. This ceiling stays
|
||||||
@@ -174,10 +158,6 @@ jobs:
|
|||||||
INPUT_ALLOW_REGRESSION: ${{ github.event.inputs.allow_regression }}
|
INPUT_ALLOW_REGRESSION: ${{ github.event.inputs.allow_regression }}
|
||||||
run: |
|
run: |
|
||||||
allow="false"
|
allow="false"
|
||||||
if [[ "${{ github.event_name }}" == "pull_request" ]] \
|
|
||||||
&& ${{ contains(github.event.pull_request.labels.*.name, 'perf-deliberate-tradeoff') }}; then
|
|
||||||
allow="true"
|
|
||||||
fi
|
|
||||||
if [[ "$INPUT_ALLOW_REGRESSION" == "true" ]]; then
|
if [[ "$INPUT_ALLOW_REGRESSION" == "true" ]]; then
|
||||||
allow="true"
|
allow="true"
|
||||||
fi
|
fi
|
||||||
@@ -314,10 +294,10 @@ jobs:
|
|||||||
echo "candidate binary: $cand_src"
|
echo "candidate binary: $cand_src"
|
||||||
|
|
||||||
if [[ "${{ steps.exempt.outputs.allow_regression }}" == "true" ]]; then
|
if [[ "${{ steps.exempt.outputs.allow_regression }}" == "true" ]]; then
|
||||||
args+=(--allow-regression --exemption-reason "labeled perf-deliberate-tradeoff / dispatch override")
|
args+=(--allow-regression --exemption-reason "workflow dispatch override")
|
||||||
fi
|
fi
|
||||||
# Do not let a gate FAIL abort the job here; capture status and surface
|
# Do not let a gate FAIL abort the job here; capture status and surface
|
||||||
# it after the PR comment is posted.
|
# it after the step summary is written.
|
||||||
set +e
|
set +e
|
||||||
bash scripts/run_hotpath_warp_abba.sh "${args[@]}"
|
bash scripts/run_hotpath_warp_abba.sh "${args[@]}"
|
||||||
echo "status=$?" >> "$GITHUB_OUTPUT"
|
echo "status=$?" >> "$GITHUB_OUTPUT"
|
||||||
@@ -382,13 +362,6 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
} >> "$GITHUB_STEP_SUMMARY"
|
} >> "$GITHUB_STEP_SUMMARY"
|
||||||
|
|
||||||
- name: Comment gate result on PR
|
|
||||||
if: always() && github.event_name == 'pull_request' && steps.ab.outputs.gate_md != ''
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: |
|
|
||||||
gh pr comment "${{ github.event.pull_request.number }}" --body-file "${{ steps.ab.outputs.gate_md }}"
|
|
||||||
|
|
||||||
# Scheduled failure alerting is handled by the alert-on-failure job below
|
# Scheduled failure alerting is handled by the alert-on-failure job below
|
||||||
# (perf-2 consuming ci-8's schedule-failure-issue composite action).
|
# (perf-2 consuming ci-8's schedule-failure-issue composite action).
|
||||||
|
|
||||||
@@ -397,7 +370,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
status="${{ steps.ab.outputs.status }}"
|
status="${{ steps.ab.outputs.status }}"
|
||||||
if [[ "$status" != "0" ]]; then
|
if [[ "$status" != "0" ]]; then
|
||||||
echo "::error::warp A/B budget gate failed (exit $status). See the step summary / PR comment / gate.md artifact." >&2
|
echo "::error::warp A/B budget gate failed (exit $status). See the step summary / gate.md artifact." >&2
|
||||||
exit "$status"
|
exit "$status"
|
||||||
fi
|
fi
|
||||||
echo "warp A/B budget gate passed."
|
echo "warp A/B budget gate passed."
|
||||||
@@ -407,14 +380,12 @@ jobs:
|
|||||||
needs: [warp-ab]
|
needs: [warp-ab]
|
||||||
# `always()` is required: without it this job is skipped when a needed
|
# `always()` is required: without it this job is skipped when a needed
|
||||||
# job fails. Alerts only for scheduled (nightly) runs (backlog#1149
|
# job fails. Alerts only for scheduled (nightly) runs (backlog#1149
|
||||||
# ci-8); PR and manual dispatch failures are already watched by a human.
|
# ci-8); manual dispatch failures are already watched by a human.
|
||||||
# `cancelled` is included alongside `failure` on purpose: a job that hits
|
# `cancelled` is included alongside `failure` on purpose: a job that hits
|
||||||
# timeout-minutes ends as `cancelled`, and the 2026-07-11..07-14 nightly
|
# timeout-minutes ends as `cancelled`, and the 2026-07-11..07-14 nightly
|
||||||
# timeouts went silent precisely because the guard was failure-only. The
|
# timeouts went silent precisely because the guard was failure-only. The
|
||||||
# composite action already reports cancelled/timed-out jobs in the issue
|
# composite action already reports cancelled/timed-out jobs in the issue
|
||||||
# body. (Scheduled runs get a unique concurrency group with
|
# body.
|
||||||
# cancel-in-progress off, so a cancellation here means a timeout/manual
|
|
||||||
# abort, never a superseding run.)
|
|
||||||
if: >-
|
if: >-
|
||||||
always() && github.event_name == 'schedule' &&
|
always() && github.event_name == 'schedule' &&
|
||||||
(contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled'))
|
(contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled'))
|
||||||
|
|||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
workflow=".github/workflows/performance-ab.yml"
|
||||||
|
|
||||||
|
require_absent_pattern() {
|
||||||
|
local pattern="$1"
|
||||||
|
local description="$2"
|
||||||
|
|
||||||
|
if grep -Eq -- "$pattern" "$workflow"; then
|
||||||
|
echo "invalid performance A/B workflow contract: $description" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
require_absent_pattern '(^|[^[:alnum:]_])pull_request(_target)?([^[:alnum:]_]|$)' "the workflow must not contain PR event handling"
|
||||||
|
require_absent_pattern 'pull-requests[[:space:]]*:[[:space:]]*write' "the workflow must not receive PR write permission"
|
||||||
|
require_absent_pattern 'permissions[[:space:]]*:[[:space:]]*write-all' "the workflow must not receive broad write permission"
|
||||||
|
|
||||||
|
echo "Performance A/B workflow trust boundary ok."
|
||||||
Reference in New Issue
Block a user