Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/actionlint.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
paths:
.github/workflows/check.yml:
ignore:
# Delivered to Actions but missing from its event docs: https://github.com/rhysd/actionlint/issues/726
- 'invalid activity type "destroyed" for "merge_group" Webhook event'
15 changes: 10 additions & 5 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,16 @@ on:
# would post a passing context over a failed one; on main PRs that is free.
types: [opened, synchronize, reopened, edited, closed]
merge_group:
types: [checks_requested]
# `destroyed` only cancels the dead group's in-flight run, like `closed`.
# GitHub keeps that run queued when the group is rebuilt behind a departed
# entry, where it holds the queue runner ahead of the live groups until they
# time out. Actions delivers this type although its docs omit it.
types: [checks_requested, destroyed]

# Cancel in-flight runs when a new commit lands on the same PR, or when the PR
# is merged/closed (the close event shares this group and supersedes the build).
# Each merge group has its own queue ref, separate from the PR ref.
# Each merge group has its own queue ref, shared by its `destroyed` event and
# separate from the PR ref.
concurrency:
group: check-${{ github.ref }}
cancel-in-progress: true
Expand All @@ -27,9 +32,9 @@ jobs:
# One checkout, dev shell, cache restore, and dispatcher for lints and tests.
check:
name: Check
# A close/merge run exists only to cancel the superseded in-flight build via
# the concurrency group above.
if: github.event.action != 'closed' && (github.event_name != 'pull_request' || github.base_ref == 'release')
# A close/merge or destroyed-group run exists only to cancel the superseded
# in-flight build via the concurrency group above.
if: github.event.action != 'closed' && github.event.action != 'destroyed' && (github.event_name != 'pull_request' || github.base_ref == 'release')
# Leave the variable unset until the local runner is online and verified.
runs-on: ${{ github.event_name == 'merge_group' && vars.MOQ_QUEUE_RUNNER || 'ubuntu-24.04-arm' }}
# `main` and its merge queue write the Rust cache; see .github/actions/rust-cache.
Expand Down
8 changes: 7 additions & 1 deletion sh/gh/queue.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,14 @@ for (const job of [check.jobs.check]) {
assert.equal(condition(job.if, "pull_request", "release"), true);
assert.equal(condition(job.if, "pull_request", "release", true, "edited"), true);
assert.equal(condition(job.if, "pull_request", "release", true, "closed"), false);
assert.equal(condition(job.if, "merge_group", ""), true);
assert.equal(condition(job.if, "merge_group", "", true, "checks_requested"), true);
// A destroyed group's run only cancels its stale build through the shared queue ref.
assert.equal(condition(job.if, "merge_group", "", true, "destroyed"), false);
}
assert.deepEqual(check.on.merge_group.types, ["checks_requested", "destroyed"]);
// biome-ignore lint/suspicious/noTemplateCurlyInString: GitHub expression is literal YAML.
assert.equal(check.concurrency.group, "check-${{ github.ref }}");
assert.equal(check.concurrency["cancel-in-progress"], true);

// Every other workflow stays out of main PRs and main's merge groups: optional
// checks run nightly on main and on release PRs, retargeted ones included.
Expand Down
4 changes: 2 additions & 2 deletions sh/gh/setup.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ for (const name of ["check"]) {
}

test("scope queries support both merge groups and release PRs without path filters", () => {
expect(workflow.on.merge_group).toEqual({ types: ["checks_requested"] });
expect(workflow.on.merge_group).toEqual({ types: ["checks_requested", "destroyed"] });
expect(workflow.on.pull_request).toBeDefined();
expect(workflow.on.pull_request).not.toHaveProperty("paths");
expect(workflow.on.pull_request).not.toHaveProperty("branches");
Expand All @@ -67,7 +67,7 @@ test("main PRs skip Check while the queue and release PRs run it", () => {
expect(workflow.on.pull_request).toEqual({ types: ["opened", "synchronize", "reopened", "edited", "closed"] });
for (const name of ["check"]) {
expect(workflow.jobs[name].if).toBe(
"github.event.action != 'closed' && (github.event_name != 'pull_request' || github.base_ref == 'release')",
"github.event.action != 'closed' && github.event.action != 'destroyed' && (github.event_name != 'pull_request' || github.base_ref == 'release')",
);
}
});
Expand Down
Loading