Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
d86b6b4
fix(mod,deploy): reject private repos with a clear error (#119)
EnderOfWorlds007 May 5, 2026
05e5d4e
test(e2e): tighten fake-passing assertions across the suite
ottovlotto May 4, 2026
bda2cc0
test(e2e): apply adversarial-QA review findings across the suite
ottovlotto May 4, 2026
1c140a4
fix(mod): silence false origin warning and verify repo before download
UtkarshBhardwaj007 May 5, 2026
567ec43
chore: version packages
github-actions[bot] May 5, 2026
c588948
ci(e2e): add cold-start init smoke job in fresh ubuntu container
ottovlotto May 4, 2026
a13568e
Merge pull request #121 from paritytech/fix/mod-origin-and-repo-probe
UtkarshBhardwaj007 May 5, 2026
3f3fdca
Merge origin/main into becca/e2e-fixed-domains
ottovlotto May 4, 2026
3e17194
fix(e2e): adjust init test timeouts and bypass connect() in toolchain…
ottovlotto May 4, 2026
1fda854
fix(e2e): drop bogus differential-CID check in re-deploy test
ottovlotto May 4, 2026
dce0336
test(e2e): strengthen mod happy-path test to cover the full clone flow
ottovlotto May 4, 2026
8bb6b97
Merge branch 'main' into becca/e2e-fixed-domains
ottovlotto May 5, 2026
fb1bded
Merge branch 'main' into becca/e2e-fixed-domains
ottovlotto May 5, 2026
dbe5655
test(e2e): pass --private on all fixture deploys
ottovlotto May 5, 2026
017f91f
Merge remote-tracking branch 'origin/main' into becca/e2e-fixed-domains
ottovlotto May 5, 2026
95b5811
test(e2e,ci): apply review fixes from main-merge
ottovlotto May 5, 2026
2eaa742
fix(e2e): strip actual rustup/cargo/forge dirs in toolchain test, not…
ottovlotto May 5, 2026
3e7a3f8
test(e2e): drop flaky toolchain-detection test, defer to cold-smoke
ottovlotto May 5, 2026
a41c046
ci(e2e): cold-smoke installs via install.sh after Dev Release
ottovlotto May 5, 2026
afd6f91
test(e2e): self-heal mod fixture across registry redeploys
ottovlotto May 5, 2026
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
85 changes: 85 additions & 0 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ on:
schedule:
- cron: "0 6 * * *"
workflow_dispatch:
# Fires the init-cold-smoke job after Dev Release publishes the per-PR
# `dev/<branch>` tag, so the smoke can install via the same one-liner
# the dev-release bot posts on the PR.
workflow_run:
workflows: ["Dev Release"]
types: [completed]

permissions:
pull-requests: write # sticky PR comment posting
Expand Down Expand Up @@ -466,3 +472,82 @@ jobs:
-H "Accept: application/vnd.github+json" \
"$API_URL/repos/$REPO/issues" \
-d "$(jq -n --arg title "$TITLE" --arg body "$BODY" '{title: $title, body: $body}')"

init-cold-smoke:
# Runs `dot init` inside a fresh ubuntu:22.04 container — no rustup,
# no IPFS, no foundry, no cdm pre-installed. The cell-based jobs
# above run on a GitHub Actions runner that already has most of
# those tools on PATH, so they can't catch regressions in the
# install / post-install path-config logic (e.g. paritytech/
# playground-app#118 — newly-installed rustup unreachable from the
# same init process). This job is the cold-start defence: every
# dependency must be installed AND become reachable to subsequent
# init steps in the same process.
#
# Install path: the same `curl … install.sh | VERSION=… bash`
# one-liner the dev-release bot posts on every PR — i.e. exactly
# what an end user runs. install.sh downloads the SEA binary,
# adds it to PATH, then runs `dot init` as its final step (errors
# surface via exit code; see CLAUDE.md "Non-obvious invariants").
# Going through the published binary catches Bun-compile-only
# regressions that `bun run src/index.ts` masks.
#
# Triggers:
# - workflow_run (Dev Release succeeded) → install dev/<branch>
# for that PR; this is the per-PR cold-start gate.
# - schedule / workflow_dispatch on main → install latest
# stable release (no VERSION).
name: Init cold-start smoke test
runs-on: ${{ github.repository_owner == 'paritytech' && 'parity-default' || 'ubuntu-latest' }}
timeout-minutes: 30
if: |
github.event_name == 'schedule' ||
github.event_name == 'workflow_dispatch' ||
(github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success')
steps:
- name: Run dot init via install.sh in fresh ubuntu container
env:
# Empty for schedule/dispatch → install.sh resolves the
# latest stable tag. Set to dev/<head-branch> after a
# successful Dev Release so we test the PR's binary.
VERSION_OVERRIDE: ${{ github.event_name == 'workflow_run' && format('dev/{0}', github.event.workflow_run.head_branch) || '' }}
run: |
docker run --rm \
-e CI=true \
-e VERSION="$VERSION_OVERRIDE" \
ubuntu:22.04 \
bash -eux -c '
# install.sh needs only curl + ca-certs to fetch the
# SEA binary. Anything beyond this is what `dot init`
# (called from install.sh) installs for itself.
apt-get update -qq
DEBIAN_FRONTEND=noninteractive apt-get install -qq -y --no-install-recommends \
curl ca-certificates bash

# ── The actual smoke test ──────────────────────────────
# End-user install path. install.sh exits non-zero if
# the embedded `dot init` fails, so the pipeline alone
# catches the #118-class "installed but unreachable"
# regression. We additionally grep the captured output
# for failed-dependency markers because some failure
# modes still let init exit 0 (e.g. an optional row
# rendered with the failure glyph).
curl -fsSL https://raw.githubusercontent.com/paritytech/playground-cli/main/install.sh \
| bash 2>&1 | tee /tmp/init.log

grep -q "setup complete" /tmp/init.log || {
echo "::error::dot init did not reach '\''setup complete'\''"
exit 1
}

# Reject failed-row markers rendered by the dot TUI.
# Fail glyph is U+2715 ✕ (see src/utils/ui/theme/
# tokens.ts); we also accept U+2717 ✗ for terminal-
# font fallback. Word-anchored "failed" only — bare
# " error" would also match " errors" (e.g. apt's
# "0 errors") and apt warnings unrelated to the smoke.
if grep -E "✗ |✕ |\bFAILED\b|\bfailed dependency\b" /tmp/init.log; then
echo "::error::dot init reported a failed dependency"
exit 1
fi
'
78 changes: 55 additions & 23 deletions e2e/cli/build.test.ts
Original file line number Diff line number Diff line change
@@ -1,47 +1,79 @@
import { describe, test, expect, beforeEach, afterEach } from "vitest";
import { existsSync, rmSync } from "node:fs";
import { resolve } from "node:path";
import { describe, test, expect, afterEach } from "vitest";
import { cpSync, existsSync, mkdtempSync, rmSync } from "node:fs";
import { join, resolve } from "node:path";
import { tmpdir } from "node:os";
import { dot } from "./helpers/dot.js";
import { fixturePath } from "./fixtures/templates.js";

describe("dot build", () => {
const frontendOnly = fixturePath("frontend-only");
const distDir = resolve(frontendOnly, "dist");
/**
* Copy a fixture into a fresh temp dir so the build output lives there,
* not inside the source-controlled fixture. This keeps build.test.ts
* order-independent: previously we deleted `frontend-only/dist` between
* tests, which left the dir absent for any later test that ran with
* `--no-build` (deploy, diagnostic). With a copy, the original fixture
* is never touched.
*/
function stageFixture(name: string): string {
const dir = mkdtempSync(join(tmpdir(), `dot-e2e-build-${name}-`));
cpSync(fixturePath(name), dir, { recursive: true });
return dir;
}

beforeEach(() => {
if (existsSync(distDir)) {
rmSync(distDir, { recursive: true });
}
});
describe("dot build", () => {
const stagedDirs: string[] = [];

afterEach(() => {
if (existsSync(distDir)) {
rmSync(distDir, { recursive: true });
for (const d of stagedDirs) {
try {
rmSync(d, { recursive: true, force: true });
} catch { /* best-effort */ }
}
stagedDirs.length = 0;
});

function stage(name: string): string {
const d = stageFixture(name);
stagedDirs.push(d);
return d;
}

test("builds a frontend-only project", async () => {
const result = await dot(["build", "--dir", frontendOnly]);
expect(result.exitCode).toBe(0);
const project = stage("frontend-only");
// The fixture ships a pre-built dist for use by deploy tests.
// Remove it from the staged copy so we can assert that *this*
// build invocation produced the artifacts.
const distDir = resolve(project, "dist");
if (existsSync(distDir)) rmSync(distDir, { recursive: true });

const result = await dot(["build", "--dir", project]);
expect(
result.exitCode,
`build failed: ${result.stdout}\n${result.stderr}`,
).toBe(0);
expect(result.stdout).toContain("Build succeeded");
expect(existsSync(resolve(distDir, "index.html"))).toBe(true);
});

test("exits non-zero on build failure with error output", async () => {
const broken = fixturePath("broken-contract");
const result = await dot(["build", "--dir", broken]);
const project = stage("broken-contract");
const result = await dot(["build", "--dir", project]);
expect(result.exitCode).not.toBe(0);
// The broken-contract fixture's build script writes to stderr
// The fixture's package.json runs:
// "build": "echo 'Compile error: unexpected token' >&2 && exit 1"
// Match the exact text — proves the runner invoked the user's script
// and surfaced its stderr, not just any pipeline-level error.
const output = result.stdout + result.stderr;
expect(output).toMatch(/error|fail/i);
expect(output).toContain("Compile error: unexpected token");
});

test("exits non-zero when no build strategy can be detected", async () => {
const contractsOnly = fixturePath("contracts-only");
const result = await dot(["build", "--dir", contractsOnly]);
const project = stage("contracts-only");
const result = await dot(["build", "--dir", project]);
expect(result.exitCode).not.toBe(0);
// contracts-only has no package.json — no build strategy
// Exact wording from src/utils/build/detect.ts:
// `No build strategy detected. Add a "build" script to package.json,
// or install vite/next/typescript.`
const output = result.stdout + result.stderr;
expect(output).toMatch(/no build|detect|strategy|package\.json/i);
expect(output).toContain("No build strategy detected");
});
});
1 change: 1 addition & 0 deletions e2e/cli/chaos.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ function chaosDeployArgs(domain: string, buildDir: string, dir: string): string[
"--buildDir",
buildDir,
"--playground",
"--private",
"--suri",
SIGNER.suri,
"--dir",
Expand Down
Loading
Loading