diff --git a/.github/workflows/performance-ratchet.yml b/.github/workflows/performance-ratchet.yml index dbafb4a90..39e3a0c4d 100644 --- a/.github/workflows/performance-ratchet.yml +++ b/.github/workflows/performance-ratchet.yml @@ -94,19 +94,21 @@ jobs: timeout-minutes: 15 steps: + # This job later receives secrets.PAT, so its actions are pinned + # to reviewed commits, as in refresh-windows-embedded-mpv-runtime. - name: Checkout code - uses: actions/checkout@v7 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false # The ratchet scripts are dependency-free Node; no pnpm install. - name: Setup Node.js - uses: actions/setup-node@v7 + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version-file: '.nvmrc' - name: Download run summaries - uses: actions/download-artifact@v8 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: pattern: performance-ratchet-run-* path: ${{ runner.temp }}/runs @@ -158,7 +160,11 @@ jobs: # triggers no workflows, and this one needs ci.yml's Initial bytes # ratchet run. Same secret as refresh-windows-embedded-mpv-runtime. # The PR number is only known once the PR exists, so evidencePr is - # filled in afterwards and the single commit amended. + # filled in afterwards and the single commit amended. Each run + # replaces the branch with one fresh commit, but never over an open + # tightening PR that someone else committed to (review edits, an + # "Update branch" merge); explicit leases also refuse a push that + # lands between that check and ours. - name: Create or update the tightening pull request if: steps.tighten.outputs.changed == 'true' && github.ref == 'refs/heads/master' env: @@ -179,16 +185,33 @@ jobs: fi baselines=tools/performance/journey-baselines.json + bot_email="41898282+github-actions[bot]@users.noreply.github.com" + gh auth setup-git git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git config user.email "$bot_email" + + pr="$(gh api "repos/$REPOSITORY/pulls?base=master&head=${REPOSITORY%%/*}:$BRANCH_NAME&state=open" --jq '.[0].number // empty')" + remote_tip="$(git ls-remote --heads origin "refs/heads/$BRANCH_NAME" | cut -f1)" + if [ -n "$pr" ] && [ -n "$remote_tip" ]; then + foreign="$(gh api "repos/$REPOSITORY/compare/master...$remote_tip" | + jq -r --arg bot "$bot_email" '.commits[] | select(.commit.author.email != $bot) | "\(.sha[0:9]) \(.commit.author.name)"')" + if [ -n "$foreign" ]; then + echo "::warning::Pull request #$pr has commits this workflow did not make; its branch is left as is. Merge or close it so the next run can refresh it. This run's numbers are in the job summary." + echo "$foreign" + exit 0 + fi + fi + if [ -n "$remote_tip" ]; then + git fetch --no-tags --depth=1 origin "$remote_tip" + fi + git switch -C "$BRANCH_NAME" git add -- "$baselines" git commit -m "$TITLE" -m "Measured by $RUN_URL" - git fetch origin "$BRANCH_NAME:refs/remotes/origin/$BRANCH_NAME" || true - git push --force-with-lease origin "HEAD:refs/heads/$BRANCH_NAME" + git push --force-with-lease="refs/heads/$BRANCH_NAME:$remote_tip" origin "HEAD:refs/heads/$BRANCH_NAME" + pushed="$(git rev-parse HEAD)" - pr="$(gh api "repos/$REPOSITORY/pulls?base=master&head=${REPOSITORY%%/*}:$BRANCH_NAME&state=open" --jq '.[0].number // empty')" if [ -z "$pr" ]; then pr="$(gh api -X POST "repos/$REPOSITORY/pulls" \ -f title="$TITLE" -f head="$BRANCH_NAME" -f base=master \ @@ -200,7 +223,7 @@ jobs: --fill-evidence-pr "$pr" --evidence-run "$RUN_URL" git add -- "$baselines" git commit --amend --no-edit - git push --force-with-lease origin "HEAD:refs/heads/$BRANCH_NAME" + git push --force-with-lease="refs/heads/$BRANCH_NAME:$pushed" origin "HEAD:refs/heads/$BRANCH_NAME" { echo "Automated weekly tightening of \`$baselines\` from [three runner measurements]($RUN_URL) of \`$HEAD_SHA\`." diff --git a/docs/architecture/performance-journeys.md b/docs/architecture/performance-journeys.md index eeb25a32c..e1cc64bbf 100644 --- a/docs/architecture/performance-journeys.md +++ b/docs/architecture/performance-journeys.md @@ -585,11 +585,18 @@ When the file changed and the run is on `master`, the job pushes `automation/performance-ratchet` and opens (or updates) a pull request with the per-run table, the diff and the run URL, labelled `no-release-note`. It pushes with the existing `PAT` secret, as the Windows MPV pin refresh does, -because a pull request pushed with `GITHUB_TOKEN` starts no CI. When no +because a pull request pushed with `GITHUB_TOKEN` starts no CI. Each run +replaces the branch with one fresh commit, except when the open tightening +pull request carries a commit the workflow did not make (a review edit, an +"Update branch" merge): then it leaves the branch alone with a warning, and +the numbers stay in the job summary. When no baseline was below its value in all three runs, the workflow ends without a pull request. A dispatch on another branch measures and prints the diff but -never opens one; use `gh workflow run performance-ratchet.yml --ref ` -to validate a change to the workflow. Review the pull request like a manual +never opens one, so `gh workflow run performance-ratchet.yml --ref ` +validates a change to the workflow once the file is on `master`. GitHub only +dispatches workflows that exist on the default branch, so before the first +merge of a new or renamed workflow add a temporary `push` trigger for the +branch and drop it before review, as #1760 did. Review the pull request like a manual tightening: if `master` moved since the measured commit, the `Initial bytes ratchet` job on the pull request is what shows that the new value still holds (the concurrent-merge effect above). diff --git a/docs/architecture/validation-map.md b/docs/architecture/validation-map.md index 7903d5685..362b83fb9 100644 --- a/docs/architecture/validation-map.md +++ b/docs/architecture/validation-map.md @@ -263,7 +263,9 @@ target `master` and for `master` pushes (dispatch it with `gh workflow run ci.yml --ref ` for a stacked branch). The weekly `performance-ratchet.yml` workflow lowers baselines through a bot PR; validate a change to it with `gh workflow run performance-ratchet.yml --ref `, -which measures but opens no PR off `master`. `perf:journeys` builds the `electron-performance` configuration and runs every +which measures but opens no PR off `master`. Dispatch needs the workflow file +on `master`; before that, see the temporary-trigger note under Weekly +tightening in the performance journeys document. `perf:journeys` builds the `electron-performance` configuration and runs every journey spec against the Xtream mock: J1 launch, then J2 open-source (a second set of launches, each followed by the click on the portal card), both written to the same summary file; its probe specs run with