mirror of
https://github.com/ever-co/ever-gauzy.git
synced 2026-10-02 01:54:50 +08:00
ci: stop concurrent CodeQL and duplicate gate runs on PRs and develop/stage (#10259)
* ci: stop concurrent CodeQL and duplicate gate runs on PRs and develop/stage CodeQL ran through GitHub's code-scanning DEFAULT SETUP, which generates its workflow server-side (run path `dynamic/github-code-scanning/codeql`). There is no file to put a `concurrency:` block in, and GitHub does not supersede its own default-setup runs, so every push to a pull request started another full analysis alongside the ones already in flight. Measured on #10254 on 2026-09-21: runs 35547460875 and 35548451790 overlapped by 12 minutes, and 35580875040 / 35582701347 / 35582925033 were all running at 09:22. Each run is two jobs of 25-35 minutes on the 4-vCPU ARC pool. Converts CodeQL to advanced setup with a real workflow file that reproduces the default-setup configuration exactly - the two analyses it actually ran (javascript-typescript, actions; the other three listed languages are aliases), the default query suite, the default remote threat model, the weekly schedule, and the ever-k8s-linux-x64-4 pool - and adds the cancel-in-progress policy that default setup could not have. Branch coverage stays at develop/stage/master because all three are protected and default setup was scanning all three. Also closes the remaining concurrency gaps on the PR and develop/stage surface: - build.yml and secrets-analysis.yml both pair an unfiltered `pull_request:` with a `push:` on the same branches, so while a release-cascade PR is open (today #10257, stage -> stage-apps) one push fires two runs of the same tree in two groups that could never cancel each other. Keying the group on the head ref collapses them; qualifying it by head repository keeps a fork branch of the same name from claiming the group and cancelling a real build before its job skips. - harvest-secrets-to-openbao.yml was the only workflow of 70 with no concurrency block, and two overlapping dispatches can interleave into a torn OpenBao snapshot. - external-uptime-monitor.yml now cancels superseded PR self-tests while still never cancelling a live probe, which owns the alert issue. Deliberately unchanged: the deploy and release workflows keep `cancel-in-progress: false`, and test-unit.yml keeps it on the stage gate. Each documents an incident where cancelling caused real harm. Requires default setup to be disabled first - GitHub rejects CodeQL SARIF uploads from a workflow while default setup is enabled. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * ci: collapse duplicate CodeQL runs and teach cspell the OpenBao name Two review follow-ups on this branch. Greptile P2: codeql.yml had the same duplicate-run defect this PR fixes in build.yml and secrets-analysis.yml. Both of its triggers cover develop/stage/master, so while a cascade pull request is open (develop -> stage), one push to develop fires a push run on refs/heads/develop and a pull_request run on refs/pull/N/merge. Keyed on github.ref those are different strings, so cancel-in-progress could never collapse them and the same tree was analysed twice. Now keyed on the head ref and qualified by head repository, the same shape used in the other two files. Cspell: this PR is the first change to harvest-secrets-to-openbao.yml since the spellcheck was added, and the action only scans changed files - so `openbao` had never been checked before and surfaced as an unknown word. Added to .cspell.json next to the other product names. Not taken: CodeRabbit asked for the actions in codeql.yml to be pinned to commit SHAs. Declined as inconsistent rather than wrong - this repository tag-pins 547 `uses:` references against a handful of SHA pins, and there is no dependabot config to keep SHAs current, so pinning one new file would leave an outlier that nothing updates. Worth doing repo-wide as a deliberate policy, not here. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * ci: use US spelling in the codeql concurrency comment Cspell flagged `analysed` at codeql.yml:60. This repository is US English throughout (the job is named `Analyze`), so the comment is corrected rather than the word added to the dictionary. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
5fafd072af
commit
58bf570f64
@@ -42,7 +42,26 @@ on:
|
||||
# group serializes unrelated builds and silently cancels them (see the image-build starvation
|
||||
# incident). Within this workflow+ref, only the newest commit is built.
|
||||
concurrency:
|
||||
group: build-${{ github.workflow }}-${{ github.ref }}
|
||||
# Keyed on the HEAD REF, not on `github.ref`, so the two events that fire for one commit land in
|
||||
# the same group. `pull_request:` above is unfiltered, so while a release-cascade PR is open whose
|
||||
# head is a build branch (today: #10257, `stage` -> `stage-apps`), a single push to `stage` starts
|
||||
# BOTH a push run on `refs/heads/stage` AND a pull_request run on `refs/pull/10257/merge`. Under
|
||||
# `github.ref` those are different strings, so neither cancelled the other and the fleet compiled
|
||||
# the same tree twice — two 3-hour builds per push, for as long as the cascade PR stays open.
|
||||
#
|
||||
# The trade-off, stated plainly: the pull_request run builds the MERGE commit and the push run
|
||||
# builds the branch head. For a fast-forward cascade those are the same tree and the second build
|
||||
# is pure waste; if the base has diverged they differ, and collapsing them means only the newer
|
||||
# event's view is compiled. That is acceptable here because no branch declares a required status
|
||||
# check, so nothing is gated on the verdict that loses — but it IS a real narrowing, not a free win.
|
||||
#
|
||||
# Qualified by the head REPOSITORY as well as the ref. Fork pull requests are skipped at the job
|
||||
# level, but a skipped job still creates a run that claims the group first — so without the repo
|
||||
# in the key, a fork branch named `develop` could cancel a genuine `develop` build and then skip.
|
||||
group: >-
|
||||
build-${{ github.workflow }}-${{ github.event_name == 'pull_request'
|
||||
&& format('{0}@{1}', github.event.pull_request.head.repo.full_name, github.event.pull_request.head.ref)
|
||||
|| format('{0}@{1}', github.repository, github.ref_name) }}
|
||||
# Was `github.event_name == 'pull_request'`, which left branch pushes uncancelled:
|
||||
# four merges to develop in an hour meant four full 3-h compiles of commits that were
|
||||
# already superseded. This job only compiles — nothing outside the run depends on a
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
name: 'CodeQL'
|
||||
|
||||
# CodeQL moved from GitHub's DEFAULT SETUP to advanced setup for exactly one reason:
|
||||
# default setup cannot be given a concurrency policy.
|
||||
#
|
||||
# Default setup has no workflow file — GitHub generates the run server-side, which is why its
|
||||
# runs show up under the path `dynamic/github-code-scanning/codeql` instead of this directory.
|
||||
# There is nowhere to put a `concurrency:` block, and GitHub does not supersede its own runs, so
|
||||
# every push to a pull request started ANOTHER full analysis alongside the ones already running.
|
||||
# Measured on #10254 on 2026-09-21: runs 35547460875 (00:21→00:52) and 35548451790 (00:40→01:14)
|
||||
# overlapped by 12 minutes, and 35580875040 / 35582701347 / 35582925033 were all in flight at
|
||||
# 09:22. Each run is two jobs of roughly 25-35 minutes on the 4-vCPU ARC pool, so every overlap
|
||||
# pinned two extra self-hosted runner pods to a commit that had already been superseded.
|
||||
#
|
||||
# This file reproduces what default setup was configured to do — same languages, the default
|
||||
# query suite, the default (remote) threat model, the same weekly schedule and the same runner
|
||||
# label — and adds the one thing it could not have.
|
||||
#
|
||||
# Parity note: default setup listed four languages (actions, javascript, javascript-typescript,
|
||||
# typescript), but `javascript`, `typescript` and `javascript-typescript` are three names for one
|
||||
# analysis — its runs only ever produced two jobs, and the uploaded analyses only ever carried the
|
||||
# categories `/language:javascript-typescript` and `/language:actions`. The matrix below is those
|
||||
# two, which is the same coverage and not a reduction.
|
||||
|
||||
# Branch list is parity, not a widening. Default setup scans the default branch AND every
|
||||
# protected branch, plus pull requests targeting them — and `develop`, `stage` and `master` are
|
||||
# all protected on this repository, so default setup was already scanning all three. Confirmed
|
||||
# against the uploaded analyses, which include `refs/heads/stage` as well as `refs/heads/develop`.
|
||||
# Listing only `develop` here would have quietly dropped CodeQL from the release cascade.
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- develop
|
||||
- stage
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- develop
|
||||
- stage
|
||||
- master
|
||||
# Default setup ran a weekly scan so that a new CodeQL release re-examines unchanged code.
|
||||
# Keeping it means an advisory published after a file was last touched still finds it.
|
||||
schedule:
|
||||
- cron: '27 4 * * 1'
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
# The point of this file. A newer commit on the same ref wins and the superseded analysis is
|
||||
# killed. Safe here in a way it is not for the deploy and release workflows: this job only reads
|
||||
# the checkout and uploads a SARIF result, so a half-finished run leaves nothing behind — the
|
||||
# next run re-uploads the complete picture for that ref. Nothing downstream is wired to this
|
||||
# workflow's conclusion (no `workflow_run` consumer), and none of develop/stage/master declares
|
||||
# a required status check, so a cancelled run cannot wedge a pull request's merge gate either.
|
||||
# If required checks are ever introduced, re-check this line before trusting it.
|
||||
#
|
||||
# Keyed on the HEAD REF rather than `github.ref`, for the same reason and with the same trade-off
|
||||
# as `build.yml`: both triggers above cover develop/stage/master, so while a cascade pull request
|
||||
# is open (develop -> stage), one push to `develop` fires a push run on `refs/heads/develop` AND a
|
||||
# pull_request run on `refs/pull/N/merge`. Under `github.ref` those are different strings, so
|
||||
# neither could cancel the other and the same tree was analyzed twice. Qualified by the head
|
||||
# REPOSITORY as well, because a fork pull request's run claims the group before its job skips.
|
||||
group: >-
|
||||
${{ github.workflow }}-${{ github.event_name == 'pull_request'
|
||||
&& format('{0}@{1}', github.event.pull_request.head.repo.full_name, github.event.pull_request.head.ref)
|
||||
|| format('{0}@{1}', github.repository, github.ref_name) }}
|
||||
cancel-in-progress: true
|
||||
|
||||
# Least privilege at the workflow level; the analyze job widens only what CodeQL needs.
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze (${{ matrix.language }})
|
||||
# Never run jobs for a pull request from a fork (owner decision 2026-09-16); branch PRs and pushes still run.
|
||||
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }}
|
||||
# Same pool default setup used (`runner_label: ever-k8s-linux-x64-4`), read through the repo
|
||||
# variable the rest of this directory uses so a fleet rename is a one-place change. The
|
||||
# fallback keeps the workflow runnable if the ARC pool is ever unavailable.
|
||||
runs-on: ${{ vars.RUNNER_LINUX_X64_4 || 'ubuntu-latest' }}
|
||||
timeout-minutes: 120
|
||||
permissions:
|
||||
# Upload the SARIF results to the code scanning API. Required by every advanced setup.
|
||||
security-events: write
|
||||
# Lets the `actions` language analysis read workflow definitions and run metadata.
|
||||
actions: read
|
||||
# Fetching CodeQL query packs.
|
||||
packages: read
|
||||
contents: read
|
||||
strategy:
|
||||
# One language failing must not hide the other language's findings.
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- language: javascript-typescript
|
||||
build-mode: none
|
||||
- language: actions
|
||||
build-mode: none
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
# Analysis only reads the tree; the checkout credential does not need to outlive the step.
|
||||
persist-credentials: false
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v4
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
build-mode: ${{ matrix.build-mode }}
|
||||
# No `queries:` and no `config:` on purpose. Default setup ran `query_suite: default`
|
||||
# with `threat_model: remote`, and both of those ARE the action's defaults — naming them
|
||||
# explicitly would not change the analysis, it would only create a second place to keep
|
||||
# in sync. Widen to `security-extended` here if the team ever decides to.
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v4
|
||||
with:
|
||||
# Produces the same `/language:<lang>` category default setup uploaded, so the existing
|
||||
# alerts are matched rather than duplicated as a new tool's findings.
|
||||
category: '/language:${{ matrix.language }}'
|
||||
@@ -74,7 +74,13 @@ concurrency:
|
||||
# can never delay or displace a real production probe.
|
||||
group: >-
|
||||
external-uptime-monitor-${{ github.event_name == 'pull_request' && github.ref || 'live' }}
|
||||
cancel-in-progress: false
|
||||
# Per-event, because the two halves of that group want opposite answers. A LIVE probe must never
|
||||
# be cancelled: it owns the alert issue, and killing it mid-flight can leave an incident open with
|
||||
# no run left to close it. A pull-request self-test owns nothing — alerting is force-disabled on
|
||||
# `pull_request` (see "Decide alerting mode") — so when a reviewer pushes again, the older
|
||||
# self-test is pure waste and the newest commit should win, which is what this repository does
|
||||
# everywhere else on pull requests.
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
jobs:
|
||||
probe:
|
||||
|
||||
@@ -8,6 +8,22 @@
|
||||
name: harvest-secrets-to-openbao
|
||||
on:
|
||||
workflow_dispatch: {}
|
||||
|
||||
# The only workflow in this directory that had no concurrency policy at all.
|
||||
#
|
||||
# The group is deliberately NOT keyed on `github.ref`. Both destination paths are derived from
|
||||
# `github.repository` alone (ROLE/DEST below), so a dispatch from any branch writes the SAME two
|
||||
# OpenBao keys — a ref-scoped group would serialize nothing. The write is two separate, non-atomic
|
||||
# POSTs whose payloads are frozen at job start, so two overlapping dispatches can interleave into
|
||||
# a torn snapshot: secrets from one run and vars from the other.
|
||||
#
|
||||
# `cancel-in-progress: false`, not true. Killing a harvest mid-write is the one outcome worse than
|
||||
# making the second operator wait — it would leave the destination half-updated with no signal that
|
||||
# it happened. Runs are manual and take seconds, so queueing costs nothing.
|
||||
concurrency:
|
||||
group: harvest-secrets-to-openbao-${{ github.repository }}
|
||||
cancel-in-progress: false
|
||||
|
||||
permissions:
|
||||
id-token: write # for GitHub OIDC
|
||||
contents: read
|
||||
|
||||
@@ -7,7 +7,16 @@ on:
|
||||
pull_request:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.ref }}-${{ github.workflow }}
|
||||
# Same shape as `build.yml`, for the same reason and with the same trade-off written out there:
|
||||
# `pull_request:` here is unfiltered, so while a cascade PR whose head is `develop` is open, one
|
||||
# push to `develop` fires a push run and a pull_request run on the same tree, in two groups that
|
||||
# could never cancel each other — two full-history TruffleHog scans instead of one. Keying on the
|
||||
# head ref collapses them; qualifying by the head repository keeps a fork branch of the same name
|
||||
# from claiming this repository's group and cancelling a real scan before its job skips.
|
||||
group: >-
|
||||
${{ github.workflow }}-${{ github.event_name == 'pull_request'
|
||||
&& format('{0}@{1}', github.event.pull_request.head.repo.full_name, github.event.pull_request.head.ref)
|
||||
|| format('{0}@{1}', github.repository, github.ref_name) }}
|
||||
cancel-in-progress: true
|
||||
|
||||
# Least-privilege scope for the automatic GITHUB_TOKEN.
|
||||
|
||||
Reference in New Issue
Block a user