Skip to content
Open
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
420 changes: 420 additions & 0 deletions .claude/skills/verify-dependabot/SKILL.md

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions .github/dependency-coverage/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Dependency coverage ledger

One JSON file per dependency, at `<ecosystem>/<directory>/<package>.json`, records the tests that verify how our code uses it. The `/verify-dependabot` skill (`.claude/skills/verify-dependabot/SKILL.md`) writes these files when a package is first bumped, and a human reviews them in that Dependabot PR.

A file on `master` means its tests have been reviewed, so later minor and patch bumps of that package can auto-merge when CI is green. Adding or editing a file outside a reviewed PR bypasses that review.
27 changes: 27 additions & 0 deletions .github/workflows/dev-server.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: Dev Server

on:
push:
branches:
- master
pull_request:
workflow_call:

jobs:
dev-server:
name: Dev server smoke test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: '20'

- name: Install root deps
run: npm ci

# Designers, engineers and Claude Code cloud sessions all use the dev
# server, so check it starts, compiles and answers the stubbed endpoints.
- name: Start dev server and check it
run: scripts/dev-server-smoke.sh
3 changes: 2 additions & 1 deletion packages/join-flow/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@
"start": "webpack serve --config './webpack/dev.js'",
"build": "webpack --config './webpack/production.js'",
"test": "jest",
"test:build": "REACT_APP_USE_TEST_DATA=true webpack --config './webpack/production.js'"
"test:build": "REACT_APP_USE_TEST_DATA=true webpack --config './webpack/production.js'",
"test:dev-server": "../../scripts/dev-server-smoke.sh"
},
"proxy": "http://localhost:8080",
"eslintConfig": {
Expand Down
10 changes: 10 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,16 @@ The suite tests against one pinned WordPress release: the `wordpress:<version>-p

The PHP version in the tags (8.2) matches the plugin's minimum and the PHP version used in CI and `composer.json`; change them together.

### Dev server

`.github/workflows/dev-server.yml` runs `scripts/dev-server-smoke.sh` on every PR, which starts the dev server and checks it serves the page, compiles the bundle and answers the stubbed REST endpoints. Run it locally with `npm --prefix packages/join-flow run test:dev-server` (port 3000 must be free).

### Dependabot PRs

The `/verify-dependabot` Claude Code skill (`.claude/skills/verify-dependabot/SKILL.md`) decides whether a Dependabot PR can merge. The first time a package is bumped, it adds the smallest test that pins down how our code uses the package, proves the test can fail, and pushes it to the Dependabot branch. If the upgrade breaks anything, new tests or existing ones, it fixes our code until CI is green. Fresh review agents then review its commits as review comments on the PR, and it fixes what they find and answers each thread, for up to three rounds. Only then does it ask the person who added the dependency to review. If three rounds aren't enough, it asks that person for help instead. After that first review, minor and patch bumps of the same package auto-merge when CI is green. Major bumps always wait for a human.

`.github/dependency-coverage/` records which packages have reviewed tests, one file per package. A file only reaches `master` through a reviewed, merged PR, so adding or editing one by hand bypasses that review.

### Claude Code cloud sessions

`.claude/hooks/session-start.sh` runs at the start of each Claude Code cloud session (and does nothing elsewhere). It installs the npm and Composer dependencies and starts Docker, so all three test suites can run. Two things are specific to that environment:
Expand Down
38 changes: 38 additions & 0 deletions scripts/bundled-packages.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/usr/bin/env node
// Prints how many modules of each named package end up in the production
// join-flow bundle. 0 means the package is build tooling only and never ships
// to users. Usage: scripts/bundled-packages.js <package> [<package>...]
const { execFileSync } = require("child_process");
const fs = require("fs");
const os = require("os");
const path = require("path");

const packages = process.argv.slice(2);
if (packages.length === 0) {
console.error("Usage: scripts/bundled-packages.js <package> [<package>...]");
process.exit(2);
}

const joinFlow = path.resolve(__dirname, "../packages/join-flow");
const statsFile = path.join(
fs.mkdtempSync(path.join(os.tmpdir(), "join-flow-stats-")),
"stats.json"
);
execFileSync(
path.join(joinFlow, "node_modules/.bin/webpack"),
["--config", "./webpack/production.js", `--json=${statsFile}`],
{ cwd: joinFlow, stdio: ["ignore", "ignore", "inherit"] }
);
const stats = JSON.parse(fs.readFileSync(statsFile, "utf8"));

// Concatenated modules hold the modules they were built from in `modules`.
const flatten = (modules) =>
modules.flatMap((m) => [m, ...flatten(m.modules || [])]);
const modules = flatten(stats.modules);

for (const name of packages) {
const count = modules.filter((m) =>
(m.name || "").includes(`node_modules/${name}/`)
).length;
console.log(`${name}\t${count}`);
}
64 changes: 64 additions & 0 deletions scripts/dev-server-smoke.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#!/bin/bash
# Starts the join-flow dev server and checks it serves the page, compiles the
# bundle and answers the stubbed REST endpoints. Designers, engineers and
# Claude Code cloud sessions all rely on the dev server, and nothing else in
# CI runs it.
set -euo pipefail

ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
PORT=3000
BASE="http://localhost:$PORT"
LOG="$(mktemp)"

if curl -fsS -o /dev/null "$BASE/" 2>/dev/null; then
echo "FAIL: something is already listening on port $PORT; stop it first"
exit 1
fi

cd "$ROOT/packages/join-flow"
# Run webpack's own binary rather than npx so $! is the server process.
# --no-color keeps the log greppable: webpack colours its output on CI.
node node_modules/.bin/webpack serve --config ./webpack/dev.js --no-color >"$LOG" 2>&1 &
SERVER_PID=$!
trap 'kill "$SERVER_PID" 2>/dev/null || true' EXIT

fail() {
echo "FAIL: $1"
echo "--- dev server output ---"
cat "$LOG"
exit 1
}

# webpack-dev-server holds requests until the first compile finishes, so
# waiting for the page also waits for the bundle.
for _ in $(seq 1 120); do
if curl -fsS -o /dev/null "$BASE/" 2>/dev/null; then
break
fi
kill -0 "$SERVER_PID" 2>/dev/null || fail "dev server exited"
sleep 1
done

PAGE="$(curl -fsS "$BASE/")" || fail "page did not load"
grep -q 'src="bundle.js"' <<<"$PAGE" ||
fail "page does not include bundle.js"
echo "ok: page"

BUNDLE="$(curl -fsS "$BASE/bundle.js")" || fail "bundle.js did not load"
grep -q 'webpack/hot' <<<"$BUNDLE" ||
fail "bundle.js did not compile with hot reloading"
echo "ok: bundle"

# The server answers before webpack has written its summary, so wait for it.
for _ in $(seq 1 30); do
grep -qE "compiled (successfully|with)" "$LOG" && break
sleep 1
done
grep -q "compiled successfully" "$LOG" ||
fail "webpack reported errors or warnings"
echo "ok: compiled without errors"

[ "$(curl -fsS -X POST -H 'Content-Type: application/json' -d '{"a":1}' \
"$BASE/join/v1/join")" = "{}" ] ||
fail "stubbed REST endpoint did not answer"
echo "ok: stubbed REST endpoint"
Loading