mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-11-07 06:19:43 -10:00
fix(ci): preserve independent validation for main push SHAs (#15508)
This commit is contained in:
4 files changed
+77
-59
No files matched your search
@@ -60,19 +60,12 @@ concurrency:
|
||||
# repo's namespace and PR runs DO cancel in progress, so a fork whose
|
||||
# default branch is `main` cancels a victim PR's run on every push.
|
||||
# Supersede within a PR is kept: one branch backs one open PR per fork.
|
||||
# `main` never cancels in progress, and that is intended rather than
|
||||
# accidental: a shared group holds at most one running plus one pending
|
||||
# run, and a newer pending run REPLACES the older one even with
|
||||
# cancel-in-progress: false, so back-to-back merges still coalesce onto the
|
||||
# newest tree — but the in-flight run always finishes and reports. Merges
|
||||
# land every ~18min (median) against this ~30min lane, and with no merge
|
||||
# queue this run is the only fast witness of a merged tree; cancelling it
|
||||
# starved the suite once already — e2e.yml measured 67 of 100 push runs
|
||||
# cancelled and only 25 ever reported (#5859). The coverage upload is
|
||||
# `always()`-gated, so a cancelled run would also publish partial coverage
|
||||
# under the complete run's artifact name. Release branches are treated the
|
||||
# same way.
|
||||
group: '${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.head.repo.full_name || github.repository }}-${{ github.head_ref || github.ref }}'
|
||||
# A ref-only group replaces older pending main pushes even when cancellation
|
||||
# is disabled. Give each push-tip SHA its own group so every merged tree can
|
||||
# report independently. Repeated pushes/reruns of one SHA still share a group.
|
||||
# Keep running main and release validations uncancellable.
|
||||
group: |-
|
||||
${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.head.repo.full_name || github.repository }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/main' && github.sha || github.head_ref || github.ref }}
|
||||
cancel-in-progress: |-
|
||||
${{ github.ref != 'refs/heads/main' && !startsWith(github.ref, 'refs/heads/release/') }}
|
||||
|
||||
|
||||
@@ -17,20 +17,12 @@ on:
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
# Scope the group by event so pushes to `main` coalesce with each other without
|
||||
# ever touching the nightly schedule or a manual dispatch — they share the ref
|
||||
# but not the event. feat/e2e/** pushes coalesce within their own branch.
|
||||
# Each main push-tip SHA needs its own group: GitHub replaces older pending
|
||||
# runs in a shared group even with cancellation disabled. Same-SHA reruns
|
||||
# still share a group; other events and development branches stay ref-scoped.
|
||||
group: |-
|
||||
${{ github.workflow }}-${{ github.event_name }}-${{ github.head_ref || github.ref_name }}
|
||||
# Do NOT cancel in-progress runs on `main`: a full run takes ~40min while
|
||||
# merges land every ~18min (median), so cancelling on every merge starved the
|
||||
# suite — over 100 push runs, 67 were cancelled and only 25 ever reported.
|
||||
# With cancellation off, GitHub keeps at most ONE pending run per group and
|
||||
# cancels the previously pending one, so the queue collapses to the newest
|
||||
# tree on its own: the in-flight run always finishes, and each result covers
|
||||
# the batch of commits merged since the last one (bisect that range when it
|
||||
# goes red). Dev branches keep cancelling superseded runs — nobody bisects
|
||||
# those, and the latest push is the only tree of interest.
|
||||
${{ github.workflow }}-${{ github.event_name }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/main' && github.sha || github.head_ref || github.ref_name }}
|
||||
# Running main validations must finish; development pushes still supersede.
|
||||
cancel-in-progress: |-
|
||||
${{ github.event_name == 'push' && github.ref_name != 'main' }}
|
||||
|
||||
|
||||
@@ -927,37 +927,73 @@ describe('post-merge push lane', () => {
|
||||
});
|
||||
|
||||
it('scopes the concurrency group and keeps main uncancellable', () => {
|
||||
// All three components, the way e2e-workflow.test.js pins its own.
|
||||
//
|
||||
// Event: unscoped, the group collapses onto `Qwen Code CI-refs/heads/main`
|
||||
// and the post-merge run shares it with the nightly (60-minute lanes, no
|
||||
// cancellation on main) and with any PR from a fork branch named literally
|
||||
// `refs/heads/main` — a legal ref name.
|
||||
//
|
||||
// Repo + ref: `head_ref` alone is not unique. Two PRs with the same head
|
||||
// branch name — including from different forks, which evaluate groups in
|
||||
// the base repo's namespace — share a group, and PR runs DO cancel in
|
||||
// progress, so they cancel each other. A fork's default branch is `main`,
|
||||
// the most common head_ref there is. Dropping the ref half entirely would
|
||||
// collapse every PR into one group and leave this suite green.
|
||||
//
|
||||
// cancel-in-progress: `main` stays out of cancellation on purpose, not by
|
||||
// accident. A shared group holds at most one running plus one pending run,
|
||||
// and a newer pending run REPLACES the older one even while this reads
|
||||
// `false`, so back-to-back merges still coalesce onto the newest tree —
|
||||
// but the in-flight run always finishes and reports, which matters
|
||||
// because with no merge queue this lane is the only fast witness of a
|
||||
// merged tree and its coverage upload is `always()`-gated. The history of
|
||||
// flipping it the other way is recorded once already: 67 of 100 push runs
|
||||
// cancelled and only 25 ever reported (#5859). Pinning the literal keeps
|
||||
// a later "make main cancellable" change from landing unnoticed.
|
||||
expect(String(ci.concurrency.group)).toBe(
|
||||
'${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.head.repo.full_name || github.repository }}-${{ github.head_ref || github.ref }}',
|
||||
expect(String(ci.concurrency.group).trim()).toBe(
|
||||
"${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.head.repo.full_name || github.repository }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/main' && github.sha || github.head_ref || github.ref }}",
|
||||
);
|
||||
expect(String(ci.concurrency['cancel-in-progress']).trim()).toBe(
|
||||
"${{ github.ref != 'refs/heads/main' && !startsWith(github.ref, 'refs/heads/release/') }}",
|
||||
);
|
||||
});
|
||||
|
||||
it.each([
|
||||
['push', 'refs/heads/main', '', true],
|
||||
['push', 'refs/heads/feat/e2e/example', '', false],
|
||||
['push', 'refs/heads/release/example', '', false],
|
||||
['schedule', 'refs/heads/main', '', false],
|
||||
['workflow_dispatch', 'refs/heads/main', '', false],
|
||||
['workflow_dispatch', 'refs/heads/release/example', '', false],
|
||||
['pull_request', 'refs/pull/1/merge', 'feature', false],
|
||||
['merge_group', 'refs/heads/gh-readonly-queue/main/pr-1', '', false],
|
||||
])(
|
||||
'groups %s on %s without changing other events',
|
||||
(event, ref, headRef, perSha) => {
|
||||
const workflows = [ci];
|
||||
if (['push', 'schedule', 'workflow_dispatch'].includes(event)) {
|
||||
workflows.push(
|
||||
parse(readFileSync('.github/workflows/e2e.yml', 'utf8')),
|
||||
);
|
||||
}
|
||||
for (const workflow of workflows) {
|
||||
for (const sourceRepo of event === 'pull_request'
|
||||
? ['one/fork', 'two/fork']
|
||||
: ['']) {
|
||||
for (const sha of ['a'.repeat(40), 'b'.repeat(40)]) {
|
||||
for (const attempt of [1, 2]) {
|
||||
const github = {
|
||||
workflow: workflow.name,
|
||||
repository: 'QwenLM/qwen-code',
|
||||
event_name: event,
|
||||
event: {
|
||||
pull_request: { head: { repo: { full_name: sourceRepo } } },
|
||||
},
|
||||
ref,
|
||||
ref_name: ref.replace('refs/heads/', ''),
|
||||
head_ref: headRef,
|
||||
sha,
|
||||
run_id: attempt,
|
||||
run_attempt: attempt,
|
||||
};
|
||||
// Evaluate the actual YAML's string/boolean expressions. Exact
|
||||
// expression pins above and in e2e-workflow constrain this subset.
|
||||
const group = workflow.concurrency.group
|
||||
.trim()
|
||||
.replace(/\$\{\{(.*?)\}\}/g, (_, expression) =>
|
||||
new Function('github', `return (${expression});`)(github),
|
||||
);
|
||||
const isCi = workflow === ci;
|
||||
const repoPart = isCi
|
||||
? `${sourceRepo || github.repository}-`
|
||||
: '';
|
||||
const refPart = headRef || (isCi ? ref : github.ref_name);
|
||||
expect(group).toBe(
|
||||
`${workflow.name}-${event}-${repoPart}${perSha ? sha : refPart}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
describe('platform lanes — the retired sensitivity classifier', () => {
|
||||
|
||||
@@ -264,13 +264,10 @@ describe('e2e workflow', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('scopes the concurrency group by event and ref', () => {
|
||||
// Scoping by event keeps main pushes coalescing with each other without
|
||||
// touching the nightly schedule or a manual dispatch on the same ref.
|
||||
const group = yml.concurrency.group;
|
||||
expect(group).toContain('github.workflow');
|
||||
expect(group).toContain('github.event_name');
|
||||
expect(group).toContain('github.head_ref || github.ref_name');
|
||||
it('scopes main pushes by SHA and other events by ref', () => {
|
||||
expect(yml.concurrency.group.trim()).toBe(
|
||||
"${{ github.workflow }}-${{ github.event_name }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/main' && github.sha || github.head_ref || github.ref_name }}",
|
||||
);
|
||||
});
|
||||
|
||||
it('isolates child-process-heavy suites from the three parallel forks', () => {
|
||||
|
||||
Reference in new issue
Block a user