Skip to content

ci: split the E2E specs across shards by how long they take - #4447

Open
danielmlr wants to merge 4 commits into
mainfrom
daniel/ci-e2e-shard-balance
Open

danielmlr wants to merge 4 commits into
mainfrom
daniel/ci-e2e-shard-balance

Conversation

@danielmlr

@danielmlr danielmlr commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

What does this PR do?

Gives the eight E2E shards about the same amount of test time. Playwright's --shard splits the tests by count, not by time, and with fullyParallel: false each shard gets a block of neighbouring spec files. On main that left shard 2 with 15 minutes of tests and shard 8 with 3, and the slowest shard set the length of a full CI run.

An E2E job now asks Playwright for the spec list and takes its share from .github/scripts/e2e-shard.mjs, which packs the files by the durations in e2e-shard-times.json, longest first. A new or renamed spec is on that list too, so it still runs, counted as an average one until its time is recorded. The shards upload Playwright's JSON reports as artifacts, and e2e-shard.mjs --record <dir> refreshes the times from a downloaded set. The tests, the Table E2E job and the order of the Node and Cloudflare steps stay as they are.

#4329 changes the same E2E steps and gets updated once this merges.

Type of change

  • Bug fix
  • Feature (requires an accepted feature plan or RFC)
  • Refactor (no behavior change)
  • Translation
  • Documentation
  • Performance improvement
  • Tests
  • Chore (dependencies, CI, tooling)

Your connection to this change

  • I use EmDash and ran into the problem this PR addresses
  • I maintain a tool, dependency, or service that EmDash uses, and this change concerns that integration
  • A Maintainer approved this specific change before I opened the PR

Checklist

  • I have read CONTRIBUTING.md
  • pnpm typecheck passes
  • pnpm lint passes
  • pnpm test passes (or targeted tests for my change)
  • pnpm format has been run
  • I have added/updated tests for my changes (if applicable)
  • User-visible strings in the admin UI are wrapped for translation (if applicable). Do not include messages.po changes except in translation PRs — a workflow extracts catalogs on merge to main. (n/a: no admin UI change)
  • I have added and reviewed the user-facing changeset (if this PR changes a published package) (n/a: CI workflow and scripts only)
  • New features link to their merged design PR: https://github.com/emdash-cms/emdash/pull/... (n/a: not a feature)
  • I have included screenshots below if this PR changes the UI (n/a: no UI change)

AI assistance disclosure

  • This PR includes AI-generated code or text — model/tool: Claude Fable 5.1 (review pass: Claude Opus 5.5)

Screenshots / test output

  • Before: shard 2 of 8 ran 15.1 minutes of Node and Cloudflare tests, shard 8 ran 3.0, and an even share is 8.4. That's each spec file's median time over six full CI runs after ci: build the E2E fixtures once instead of two or three times per job #4317 merged on 2026-10-27, read off the shard logs.
  • The committed times come from those runs. Dealt out by them, the shards get 8.4 to 8.5 minutes each, 6 to 9 spec files, and each of the 56 files lands in one shard.
  • node --test .github/scripts/e2e-shard.test.mjs: 9 tests pass, and the Test Plan job now runs them next to approval.test.mjs. playwright test --list --reporter=json takes about 4 seconds and runs no global setup. With a spec file that can't load (a test.only under CI, a duplicate test title), the pick step fails with Playwright's own error.
  • In this PR's first CI run, every job was green. Each shard listed its files, ran its share of the 378 tests and uploaded its e2e-timings-<n> artifact. The shards ran 6.3 to 9.8 minutes of tests each, against 3.0 to 15.1 on main. The slowest E2E job took 14.0 minutes, against 18.3 to 20.1 in the six runs, and the whole run finished 14.6 minutes after it started, against about 20.
  • In two green fork runs of the first two commits (first, second), shard 8 ran the same files in 8.8 and 8.9 minutes. In this PR's run, every one of them took about 30% less time, so the shard came to 6.3. What's left of the spread is mostly how fast each runner happens to be.

Playwright's --shard deals spec files out by test count, so shard 2 of 8
ran 15 minutes of tests while shard 8 ran 3, and the slowest shard set
the length of every full CI run.

Each E2E job now asks Playwright for the spec list and picks its share
with .github/scripts/e2e-shard.mjs, which packs files by the durations
recorded in e2e-shard-times.json. Every shard uploads its JSON report so
the times can be refreshed with the script's --record mode.
A directory holding the reports of several runs made --record add a
file's times together. The shard script's tests also ran in every shard
job after the install and build; the Test Plan job runs them once now.
A spec file that fails to load (a missing import, a duplicate test title,
test.only under forbidOnly) leaves the list report with no suites, so the
shard script failed with "has no spec files" and the real error never
reached the log. The input-order test also gets two files of equal weight,
so it covers the name tie-break.
Three of the shard script's tests run it in a temporary folder and left
that folder behind on every run. Each now removes its folder when it
finishes, as scripts/archive-changelogs.test.mjs does.
@changeset-bot

changeset-bot Bot commented Oct 28, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 6555550

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@emdashbot emdashbot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duration-weighted, file-level sharding is the right fix for the shard imbalance under fullyParallel: false and workers: 1. The allocator is deterministic and dependency-free, and it fits the existing .github/scripts/*.mjs tooling and node --test checks. Both reviewers approved and raised no findings. They checked the shard allocator, the timing recorder, the workflow selection and failure handling, and the Playwright reporter configuration. The timing data covers every current non-table spec. The workflow keeps Node running before Cloudflare, and the Table E2E job stays separate. The review was static, so neither reviewer ran tests or CI.


Reviewed by anthropic/claude-opus-5-5 and openai/gpt-6.1-sol.

@cloudflare-workers-and-pages

Copy link
Copy Markdown

🚀 Deploying Preview to Cloudflare 🚀

Preview URL: https://daniel-ci-e2e-shard-balance.try.emdashcms.com, https://daniel-ci-e2e-shard-balance-emdash-playground.emdash-cms.workers.dev (commit 6555550)

This URL reflects your latest Preview deployment

Preview Deployments by commit

Status Deployment URL Commit Updated (UTC) See this deployment's details
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://893f0128.try.emdashcms.com, https://893f0128-emdash-playground.emdash-cms.workers.dev 6555550 2026-10-28T00:16:17.128Z Visit the dashboard ↗

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant