Skip to content

Commit eea5606

Browse files
ci(e2e): split into 7-cell matrix (Phase 4); add foundry+CDM full-deploy tests
1 parent b56a891 commit eea5606

5 files changed

Lines changed: 173 additions & 24 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"playground-cli": patch
3+
---
4+
5+
E2E suite now runs as a 7-cell matrix on CI: 4 no-publish cells in parallel + 3 publish cells serial (sharing the registry signer to avoid nonce races). Adds full-deploy tests for the Foundry and CDM backends. Per-cell pass/fail is visible in the sticky PR comment with cell-specific JUnit + forensic logs.

.github/workflows/e2e.yml

Lines changed: 109 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,23 @@ concurrency:
1919
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
2020

2121
jobs:
22-
test:
23-
name: "E2E · current"
22+
test-no-publish:
23+
name: "E2E · ${{ matrix.cell }}"
2424
runs-on: ubuntu-latest
25-
timeout-minutes: 55
25+
timeout-minutes: 25
26+
strategy:
27+
fail-fast: false
28+
max-parallel: 5
29+
matrix:
30+
include:
31+
- cell: pr-install
32+
pattern: "dot install"
33+
- cell: pr-preflight
34+
pattern: "dot build|preflight and validation"
35+
- cell: pr-mod
36+
pattern: "dot mod — clone"
37+
- cell: pr-init-session
38+
pattern: "dot init|session management"
2639
outputs:
2740
tag: ${{ steps.tag.outputs.tag }}
2841
steps:
@@ -62,13 +75,89 @@ jobs:
6275
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
6376
echo "Resolved DOT_TAG=$TAG (event=${{ github.event_name }})"
6477
65-
- name: Run E2E tests (one retry on transient testnet failures)
78+
- name: Run E2E cell (one retry on transient testnet failures)
79+
uses: nick-fields/retry@v3
80+
with:
81+
timeout_minutes: 20
82+
max_attempts: 2
83+
retry_wait_seconds: 30
84+
command: pnpm exec vitest run --config e2e/vitest.config.ts -t "${{ matrix.pattern }}"
85+
env:
86+
TEST_TEMPLATE_DOMAIN: dot-cli-mod-fixture.dot
87+
TEST_TEMPLATE_REPO: https://github.com/paritytech/Rock-Paper-Scissors
88+
DOT_DEPLOY_VERBOSE: "1"
89+
DOT_TAG: ${{ steps.tag.outputs.tag }}
90+
DOT_TELEMETRY: "1"
91+
92+
- name: Upload forensic artefacts
93+
if: always()
94+
continue-on-error: true
95+
uses: actions/upload-artifact@v4
96+
with:
97+
name: e2e-reports-${{ matrix.cell }}
98+
path: e2e-reports/
99+
retention-days: 7
100+
if-no-files-found: ignore
101+
102+
test-publish:
103+
name: "E2E · ${{ matrix.cell }}"
104+
runs-on: ubuntu-latest
105+
timeout-minutes: 55
106+
strategy:
107+
fail-fast: false
108+
max-parallel: 1 # serial — share SIGNER + registry domains
109+
matrix:
110+
include:
111+
- cell: pr-deploy-frontend
112+
pattern: "full pipeline"
113+
- cell: pr-deploy-foundry
114+
pattern: "deploy — foundry"
115+
- cell: pr-deploy-cdm
116+
pattern: "deploy — CDM"
117+
steps:
118+
- uses: actions/checkout@v4
119+
120+
- uses: pnpm/action-setup@v4
121+
122+
- uses: actions/setup-node@v4
123+
with:
124+
node-version: "22"
125+
126+
- uses: oven-sh/setup-bun@v2
127+
with:
128+
bun-version: latest
129+
130+
- name: Install Kubo (IPFS)
131+
run: |
132+
wget -q https://dist.ipfs.tech/kubo/v0.32.1/kubo_v0.32.1_linux-amd64.tar.gz
133+
tar xf kubo_v0.32.1_linux-amd64.tar.gz
134+
sudo mv kubo/ipfs /usr/local/bin/
135+
ipfs init --profile=test
136+
ipfs version
137+
138+
- run: pnpm install
139+
140+
- name: Resolve DOT_TAG from trigger
141+
id: tag
142+
shell: bash
143+
run: |
144+
# Phase 1 triggers: pull_request / push:main / schedule / workflow_dispatch.
145+
# Phase 7 will add `release` (with TAG=e2e-ci-release).
146+
case "${{ github.event_name }}" in
147+
schedule) TAG=e2e-ci-nightly ;;
148+
workflow_dispatch) TAG=e2e-ci-dispatch ;;
149+
*) TAG=e2e-ci-pr ;;
150+
esac
151+
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
152+
echo "Resolved DOT_TAG=$TAG (event=${{ github.event_name }})"
153+
154+
- name: Run E2E cell (one retry on transient testnet failures)
66155
uses: nick-fields/retry@v3
67156
with:
68157
timeout_minutes: 25
69158
max_attempts: 2
70159
retry_wait_seconds: 30
71-
command: pnpm test:e2e
160+
command: pnpm exec vitest run --config e2e/vitest.config.ts -t "${{ matrix.pattern }}"
72161
env:
73162
TEST_TEMPLATE_DOMAIN: dot-cli-mod-fixture.dot
74163
TEST_TEMPLATE_REPO: https://github.com/paritytech/Rock-Paper-Scissors
@@ -81,22 +170,19 @@ jobs:
81170
continue-on-error: true
82171
uses: actions/upload-artifact@v4
83172
with:
84-
name: e2e-reports-current
173+
name: e2e-reports-${{ matrix.cell }}
85174
path: e2e-reports/
86175
retention-days: 7
87176
if-no-files-found: ignore
88177

89178
report:
90179
name: E2E Report
91-
needs: [test]
180+
needs: [test-no-publish, test-publish]
92181
if: always()
93182
runs-on: ubuntu-latest
94183
timeout-minutes: 5
95184
steps:
96185
- name: Download JUnit + forensic artefacts
97-
# Phase 1 has one upload (e2e-reports-current). When Phase 4 adds
98-
# the matrix, drop `merge-multiple: true` and parse per-cell sub-dirs
99-
# so per-leg junit.xml don't collide on the same flat path.
100186
uses: actions/download-artifact@v4
101187
with:
102188
pattern: e2e-reports-*
@@ -127,11 +213,20 @@ jobs:
127213
128214
- name: Parse JUnit and render report body
129215
env:
130-
AGGREGATE: ${{ needs.test.result }}
216+
AGGREGATE_PUBLISH: ${{ needs.test-publish.result }}
217+
AGGREGATE_NO_PUBLISH: ${{ needs.test-no-publish.result }}
131218
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
132-
TAG: ${{ needs.test.outputs.tag }}
219+
TAG: ${{ needs.test-no-publish.outputs.tag }}
133220
shell: /usr/bin/bash -euo pipefail {0}
134221
run: |
222+
if [ "$AGGREGATE_PUBLISH" = "success" ] && [ "$AGGREGATE_NO_PUBLISH" = "success" ]; then
223+
AGGREGATE=success
224+
elif [ "$AGGREGATE_PUBLISH" = "cancelled" ] || [ "$AGGREGATE_NO_PUBLISH" = "cancelled" ]; then
225+
AGGREGATE=cancelled
226+
else
227+
AGGREGATE=failure
228+
fi
229+
135230
emoji() { case "$1" in
136231
success) echo "✅ PASS";;
137232
failure) echo "❌ FAIL";;
@@ -261,10 +356,10 @@ jobs:
261356
- name: Open failure issue
262357
if: >
263358
(github.event_name == 'schedule' || github.event_name == 'release') &&
264-
needs.test.result != 'success'
359+
(needs.test-no-publish.result != 'success' || needs.test-publish.result != 'success')
265360
env:
266361
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
267-
TAG: ${{ needs.test.outputs.tag }}
362+
TAG: ${{ needs.test-no-publish.outputs.tag }}
268363
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
269364
API_URL: ${{ github.api_url }}
270365
REPO: ${{ github.repository }}

CLAUDE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ These are things that aren't self-evident from reading the code and have bitten
5656

5757
- **Local launcher:** `tools/e2e-local.sh [smoke|pr|nightly]` — also callable via `pnpm test:e2e:smoke`, `pnpm test:e2e:pr`, `pnpm test:e2e:nightly`.
5858
- **CI workflow:** `.github/workflows/e2e.yml` — runs on PR / push:main / cron 06:00 UTC / workflow_dispatch.
59+
- **CI matrix:** 7 cells in two matrices — `test-no-publish` (parallel: pr-install, pr-preflight, pr-mod, pr-init-session) + `test-publish` (max-parallel: 1: pr-deploy-frontend, pr-deploy-foundry, pr-deploy-cdm). Each cell runs a subset via `vitest -t "<pattern>"`.
5960
- **Test files:** `e2e/cli/*.test.ts` (vitest, spawned via `bun run src/index.ts`).
6061
- **Reports directory:** `e2e-reports/junit.xml` + `e2e-reports/dot-runs.log` (gitignored).
6162
- **Tag prefix:** `DOT_TAG=e2e-{ci|local}-{trigger}` so Sentry dashboards filter test traffic. The CLI plumbs `DOT_TAG` into the `cli.tag` root-span attribute via `src/telemetry-config.ts`.

e2e/cli/deploy.test.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,3 +237,49 @@ describe("dot deploy --playground — full pipeline (requires Paseo + IPFS)", ()
237237
expect(output.toLowerCase()).toMatch(/revert|taken|registered|owned|unavailable|already/);
238238
});
239239
});
240+
241+
describe("dot deploy — foundry (requires Paseo + IPFS)", () => {
242+
test("foundry deploy completes end-to-end", { timeout: 450_000 }, async () => {
243+
const domain = E2E_DOMAINS.foundry;
244+
const result = await dot([
245+
"deploy",
246+
"--signer", "dev",
247+
"--domain", domain,
248+
"--buildDir", absBuildDir(foundry),
249+
"--contracts",
250+
"--playground",
251+
"--suri", SIGNER.suri,
252+
"--dir", foundry,
253+
], { timeout: 400_000 });
254+
255+
expect(
256+
result.exitCode,
257+
`foundry deploy failed: ${result.stdout}\n${result.stderr}`,
258+
).toBe(0);
259+
expect(result.stdout).toContain("Deploy complete");
260+
expect(result.stdout).toContain(domain);
261+
});
262+
});
263+
264+
describe("dot deploy — CDM (requires Paseo + IPFS)", () => {
265+
test("CDM deploy completes end-to-end", { timeout: 450_000 }, async () => {
266+
const domain = E2E_DOMAINS.cdm;
267+
const result = await dot([
268+
"deploy",
269+
"--signer", "dev",
270+
"--domain", domain,
271+
"--buildDir", absBuildDir(rustCdm),
272+
"--contracts",
273+
"--playground",
274+
"--suri", SIGNER.suri,
275+
"--dir", rustCdm,
276+
], { timeout: 400_000 });
277+
278+
expect(
279+
result.exitCode,
280+
`CDM deploy failed: ${result.stdout}\n${result.stderr}`,
281+
).toBe(0);
282+
expect(result.stdout).toContain("Deploy complete");
283+
expect(result.stdout).toContain(domain);
284+
});
285+
});

e2e/cli/mod.test.ts

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ afterEach(() => {
3535
tempDirs.length = 0;
3636
});
3737

38-
describe("dot mod", () => {
38+
describe("dot mod — clone", () => {
3939
test.skipIf(!TEST_DOMAIN)(
4040
"clones the registered template into a fresh directory",
4141
{ timeout: 180_000 },
@@ -59,6 +59,17 @@ describe("dot mod", () => {
5959
},
6060
);
6161

62+
test("exits non-zero with signer suggestion when no signer available", async () => {
63+
const tempHome = makeTempDir("dot-e2e-mod-home-");
64+
const cwd = makeTempDir("dot-e2e-mod-cwd-");
65+
const result = await dot(["mod", "some-app.dot"], { home: tempHome, cwd });
66+
expect(result.exitCode).not.toBe(0);
67+
const output = result.stdout + result.stderr;
68+
expect(output).toMatch(/signer|init|log.?in/i);
69+
});
70+
});
71+
72+
describe("dot mod — registry miss", () => {
6273
test("reports a registry-miss for an unknown domain", async () => {
6374
const cwd = makeTempDir("dot-e2e-mod-unknown-");
6475
const result = await dot(
@@ -69,13 +80,4 @@ describe("dot mod", () => {
6980
expect(result.exitCode).not.toBe(0);
7081
expect(output).toMatch(/not found/i);
7182
});
72-
73-
test("exits non-zero with signer suggestion when no signer available", async () => {
74-
const tempHome = makeTempDir("dot-e2e-mod-home-");
75-
const cwd = makeTempDir("dot-e2e-mod-cwd-");
76-
const result = await dot(["mod", "some-app.dot"], { home: tempHome, cwd });
77-
expect(result.exitCode).not.toBe(0);
78-
const output = result.stdout + result.stderr;
79-
expect(output).toMatch(/signer|init|log.?in/i);
80-
});
8183
});

0 commit comments

Comments
 (0)