mirror of
https://github.com/ruvnet/ruflo.git
synced 2026-10-24 12:26:58 +09:00
ci+docs: extend regression guards to ubuntu+macos+windows
Previously only the workspace 'build' job ran cross-platform.
The new regression guards (smoke-install-no-bsqlite,
plugin-hooks-smoke, witness-verify) were ubuntu-only.
Matrix expansion per job, justified by what behaviour each guard
actually tests:
smoke-install-no-bsqlite → ubuntu + macos
plugin-hooks-smoke → ubuntu + macos
witness-verify → ubuntu + macos + windows ← full
Why each scope:
- smoke-install-no-bsqlite uses bash patterns + tests an
OS-independent JS code path; Linux+macOS is sufficient signal.
- plugin-hooks-smoke validates a bash-based hooks.json. Windows
users hit it via WSL/git-bash (same as Linux/macOS); native
cmd/PowerShell is not the user environment.
- witness-verify is pure JS (@noble/ed25519 only). Windows
coverage catches platform-specific bugs like CRLF
normalization breaking the canonical manifest hash, or path
resolution drift in file-marker lookups.
Implementation:
- 'shell: bash' on Windows steps so git-bash is used.
- $RUNNER_TEMP instead of /tmp for OS-portable temp paths.
- 'pnpm pack --pack-destination $RUNNER_TEMP' replaces hardcoded /tmp.
ADR-102 §5 added documenting the matrix decisions, alongside the
existing §1-4 design. Implementation table updated to reflect the
new matrix coverage.
Cost: ~6 extra runner-minutes per CI invocation. Acceptable for
catching OS drift before users.
Co-Authored-By: RuFlo <ruv@ruv.net>
This commit is contained in:
1 parent
f51c11e2c7
commit
c518415909
2 files changed
+75
-22
No files matched your search
+45
-16
@@ -146,11 +146,18 @@ jobs:
|
||||
#
|
||||
# Independent of the workspace `build` job because we only need to
|
||||
# build a single package (memory) — no workspace-wide dist required.
|
||||
name: Smoke (no better-sqlite3) / Node ${{ matrix.node }}
|
||||
runs-on: ubuntu-latest
|
||||
#
|
||||
# Cross-platform: ubuntu + macos (Windows excluded because the smoke
|
||||
# script uses bash-specific patterns and runs in user environments
|
||||
# that typically use WSL/git-bash anyway. The runtime fallback path
|
||||
# the smoke verifies is platform-independent — a passing run on
|
||||
# Linux+macOS proves the JS code path works everywhere.)
|
||||
name: Smoke (no better-sqlite3) / ${{ matrix.os }} / Node ${{ matrix.node }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
node: ['22', '24']
|
||||
|
||||
steps:
|
||||
@@ -177,22 +184,23 @@ jobs:
|
||||
id: pack
|
||||
working-directory: v3/@claude-flow/memory
|
||||
run: |
|
||||
# pnpm pack rewrites workspace:* → resolved versions (npm pack doesn't)
|
||||
TARBALL=$(pnpm pack --pack-destination /tmp 2>&1 | grep -E "\.tgz$" | head -1)
|
||||
# pnpm pack rewrites workspace:* → resolved versions (npm pack doesn't).
|
||||
# Pack to RUNNER_TEMP so the path works on both ubuntu and macos.
|
||||
TARBALL=$(pnpm pack --pack-destination "$RUNNER_TEMP" 2>&1 | grep -E "\.tgz$" | head -1)
|
||||
[ -z "$TARBALL" ] && { echo "pnpm pack produced no tarball"; exit 1; }
|
||||
echo "tarball=$TARBALL" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Install with --omit=optional (simulates Node 26 native build failure)
|
||||
run: |
|
||||
mkdir -p /tmp/smoke
|
||||
cd /tmp/smoke
|
||||
mkdir -p "$RUNNER_TEMP/smoke"
|
||||
cd "$RUNNER_TEMP/smoke"
|
||||
npm init -y >/dev/null
|
||||
npm pkg set type=module
|
||||
npm install "${{ steps.pack.outputs.tarball }}" --omit=optional --no-audit --no-fund
|
||||
|
||||
- name: Assert better-sqlite3 was NOT installed
|
||||
run: |
|
||||
if [ -d /tmp/smoke/node_modules/better-sqlite3 ]; then
|
||||
if [ -d "$RUNNER_TEMP/smoke/node_modules/better-sqlite3" ]; then
|
||||
echo "FAIL: better-sqlite3 installed despite --omit=optional — smoke setup is wrong"
|
||||
exit 1
|
||||
fi
|
||||
@@ -200,8 +208,8 @@ jobs:
|
||||
|
||||
- name: Run smoke script
|
||||
run: |
|
||||
cp v3/@claude-flow/memory/scripts/smoke-no-bsqlite.mjs /tmp/smoke/
|
||||
cd /tmp/smoke
|
||||
cp v3/@claude-flow/memory/scripts/smoke-no-bsqlite.mjs "$RUNNER_TEMP/smoke/"
|
||||
cd "$RUNNER_TEMP/smoke"
|
||||
node smoke-no-bsqlite.mjs
|
||||
|
||||
plugin-hooks-smoke:
|
||||
@@ -213,11 +221,17 @@ jobs:
|
||||
#
|
||||
# Independent of the workspace `build` job — only the cli package needs
|
||||
# to be built. We use `pnpm --filter @claude-flow/cli...` to scope.
|
||||
name: Plugin hooks smoke / Node ${{ matrix.node }}
|
||||
runs-on: ubuntu-latest
|
||||
name: Plugin hooks smoke / ${{ matrix.os }} / Node ${{ matrix.node }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# Cross-platform: ubuntu + macos. Windows excluded because the
|
||||
# plugin's hooks.json uses `/bin/bash -c '...'` and the synthetic
|
||||
# JSON-on-stdin pipeline assumes POSIX shell. On Windows users
|
||||
# run Claude Code via WSL or git-bash, which the test would also
|
||||
# need to simulate; out of scope for this regression guard.
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
# Node 22 is the project's documented baseline (engines.node: '>=20')
|
||||
# but Node 20 had environment-specific issues with multi-line stdin
|
||||
# round-tripping that didn't reproduce on Node 22 or locally — same
|
||||
@@ -273,8 +287,18 @@ jobs:
|
||||
# whose dist files are referenced by witness markers). Avoids depending
|
||||
# on the workspace `build` job so unrelated package failures don't
|
||||
# block this regression guard.
|
||||
name: Witness verify (signed manifest)
|
||||
runs-on: ubuntu-latest
|
||||
#
|
||||
# Cross-platform: ubuntu + macos + windows. The standalone verifier
|
||||
# is pure JS (only @noble/ed25519) — no native deps, no shell calls.
|
||||
# Catches platform-specific JSON canonicalization or path-resolution
|
||||
# bugs (e.g. CRLF normalization on Windows breaking the manifest
|
||||
# hash) before they reach users.
|
||||
name: Witness verify (signed manifest) / ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
needs: [smoke-install-no-bsqlite, plugin-hooks-smoke]
|
||||
|
||||
steps:
|
||||
@@ -295,6 +319,7 @@ jobs:
|
||||
|
||||
- name: Install + build packages witness markers reference
|
||||
working-directory: v3
|
||||
shell: bash
|
||||
run: |
|
||||
pnpm install --frozen-lockfile
|
||||
# --no-bail tolerates unrelated package failures. The standalone
|
||||
@@ -303,6 +328,9 @@ jobs:
|
||||
pnpm --recursive --no-bail run build || true
|
||||
|
||||
- name: Verify witness manifest (via standalone plugin script — no CLI dep)
|
||||
# `shell: bash` makes Windows use git-bash so the same script
|
||||
# works across all 3 OSes. RUNNER_TEMP is set on every runner.
|
||||
shell: bash
|
||||
run: |
|
||||
# Use the project-agnostic verifier from ruflo-core plugin. This
|
||||
# is the same script external adopters use — dogfooding ensures
|
||||
@@ -313,16 +341,16 @@ jobs:
|
||||
set +e
|
||||
node plugins/ruflo-core/scripts/witness/verify.mjs \
|
||||
--manifest verification.md.json \
|
||||
--json > /tmp/witness-result.json 2> /tmp/witness-result.err
|
||||
--json > "$RUNNER_TEMP/witness-result.json" 2> "$RUNNER_TEMP/witness-result.err"
|
||||
VERIFY_EXIT=$?
|
||||
set -e
|
||||
echo "--- verify.mjs exit code: $VERIFY_EXIT ---"
|
||||
echo "--- stderr ---"
|
||||
cat /tmp/witness-result.err || true
|
||||
cat "$RUNNER_TEMP/witness-result.err" || true
|
||||
echo "--- summary ---"
|
||||
node -e "
|
||||
const fs = require('fs');
|
||||
const raw = fs.readFileSync('/tmp/witness-result.json', 'utf8');
|
||||
const raw = fs.readFileSync(process.env.RUNNER_TEMP + '/witness-result.json', 'utf8');
|
||||
if (!raw.trim()) { console.error('verify.mjs produced no JSON output'); process.exit(1); }
|
||||
const r = JSON.parse(raw);
|
||||
console.log(JSON.stringify({signature: r.signature, summary: r.summary}, null, 2));
|
||||
@@ -339,6 +367,7 @@ jobs:
|
||||
- name: History summary (ADR-103 — surface regression-introduction commits)
|
||||
# Soft gate: prints transitions since the previous snapshot. The
|
||||
# `summary` subcommand exits non-zero on any newly-regressed fix.
|
||||
shell: bash
|
||||
run: |
|
||||
if [ -f verification-history.jsonl ]; then
|
||||
node plugins/ruflo-core/scripts/witness/history.mjs \
|
||||
|
||||
@@ -90,21 +90,29 @@ Negative assertions (`absent: 'Recording … : true'`) are critical. A naive `co
|
||||
|
||||
### 2. Wire it into `v3-ci.yml` as a blocking job
|
||||
|
||||
`plugin-hooks-smoke` runs after `build` succeeds and before `publish`, on Node 20 + 22:
|
||||
`plugin-hooks-smoke` runs before `publish`, decoupled from the workspace `build` job (which is gated on unrelated test failures). The matrix is **cross-platform** to catch OS-specific shell behaviour:
|
||||
|
||||
```yaml
|
||||
plugin-hooks-smoke:
|
||||
needs: [build]
|
||||
strategy:
|
||||
matrix:
|
||||
node: ['20', '22']
|
||||
os: [ubuntu-latest, macos-latest] # bash needed; Windows out of scope
|
||||
node: ['22']
|
||||
steps:
|
||||
- install + build v3
|
||||
- install + scoped build of @claude-flow/cli...
|
||||
- node plugins/ruflo-core/scripts/test-hooks.mjs \
|
||||
"node $GITHUB_WORKSPACE/v3/@claude-flow/cli/bin/cli.js"
|
||||
```
|
||||
|
||||
This *blocks* publishing on hook regression. A future PR that adds a non-existent flag to `hooks.json`, or re-introduces the `ctx.args[0] || ctx.flags.X` priority bug, fails this job before reaching users.
|
||||
Cross-platform reasoning per job (full matrix in §5):
|
||||
|
||||
| Job | Linux | macOS | Windows | Why |
|
||||
|---|---|---|---|---|
|
||||
| `plugin-hooks-smoke` | ✓ | ✓ | — | Plugin's `hooks.json` uses `/bin/bash -c` and synthetic JSON-on-stdin assumes POSIX shell. Windows users run Claude Code via WSL/git-bash, which the test would also need to simulate; out of scope for this guard. |
|
||||
| `smoke-install-no-bsqlite` | ✓ | ✓ | — | Smoke script uses bash patterns; same reasoning. |
|
||||
| `witness-verify` | ✓ | ✓ | ✓ | Pure JS (only `@noble/ed25519`). Catches platform-specific JSON canonicalisation or path-resolution bugs (e.g. Windows CRLF normalisation breaking the manifest hash) before they reach users. |
|
||||
|
||||
This *blocks* publishing on hook regression. A future PR that adds a non-existent flag to `hooks.json`, or re-introduces the `ctx.args[0] || ctx.flags.X` priority bug, fails this job before reaching users on whichever OS the bug surfaces.
|
||||
|
||||
### 3. Codify the CLI flag-priority convention
|
||||
|
||||
@@ -137,14 +145,30 @@ This is the second instance (after `smoke-install-no-bsqlite` from #1867) of the
|
||||
|
||||
Future regression categories should follow the same shape rather than relying on in-process unit tests that miss subprocess/install/parser realities.
|
||||
|
||||
### 5. Cross-platform matrix
|
||||
|
||||
GitHub Actions provides `ubuntu-latest`, `macos-latest`, and `windows-latest` runners. The matrix per job reflects whether platform behaviour is load-bearing for *that specific guard*:
|
||||
|
||||
| Job | ubuntu | macos | windows | Rationale |
|
||||
|---|---|---|---|---|
|
||||
| `smoke-install-no-bsqlite` | ✓ | ✓ | — | Tests `npm install --omit=optional` round-trip behaviour. The smoke script uses bash; Windows runners have git-bash but the regression class (native build failure on Node 26 without prebuilds) is OS-independent. Linux + macOS coverage is sufficient signal for the JS code path. |
|
||||
| `plugin-hooks-smoke` | ✓ | ✓ | — | The plugin's `hooks.json` uses `/bin/bash -c '...'`. Windows users run Claude Code via WSL/git-bash (where the test would behave the same as Linux/macOS); native cmd/PowerShell isn't the user environment for these hooks. |
|
||||
| `witness-verify` | ✓ | ✓ | ✓ | Pure JS — only `@noble/ed25519`. Catches platform-specific bugs that pure unit tests miss: e.g. Windows CRLF line-ending normalization breaking the canonical manifest hash, or Node `path` differences breaking the marker-cited file lookup. Always full coverage. |
|
||||
| `build` | ✓ | ✓ | ✓ | Pre-existing matrix; covers tsc behaviour across OSes. |
|
||||
|
||||
Bash steps in cross-platform jobs use `shell: bash` to make Windows fall through to git-bash, and `$RUNNER_TEMP` instead of `/tmp` for OS-portable temp paths.
|
||||
|
||||
The matrix expansion costs ~6 extra runner-minutes per CI invocation (3 OSes × ~2 min for witness-verify, 1 OS × ~2 min each for the two smoke jobs that gained macOS). Acceptable for catching OS drift before users do.
|
||||
|
||||
## Implementation
|
||||
|
||||
| Artifact | Path | Purpose |
|
||||
|----------|------|---------|
|
||||
| Plugin hook fix | `plugins/ruflo-core/hooks/hooks.json` | Real flags, multi-line-safe wrappers |
|
||||
| Smoke harness | `plugins/ruflo-core/scripts/test-hooks.mjs` | 7 assertions; runs against any CLI invocation string |
|
||||
| CI job | `.github/workflows/v3-ci.yml` (`plugin-hooks-smoke`) | Node 20 + 22 matrix; blocks `publish` |
|
||||
| CI job | `.github/workflows/v3-ci.yml` (`plugin-hooks-smoke`) | ubuntu + macos × Node 22; blocks `publish` |
|
||||
| CLI parser fix | `v3/@claude-flow/cli/src/commands/hooks.ts` | 14-site flag/positional priority swap |
|
||||
| Witness verify cross-platform | `v3-ci.yml` (`witness-verify`) | ubuntu + macos + windows; pure-JS verifier dogfooding |
|
||||
|
||||
The smoke harness accepts an arbitrary CLI invocation string, so it can also run against the *published* CLI (`npx --yes @claude-flow/cli@latest`) as a post-release sanity check — the same script doubles as a pre-merge guard and a release canary.
|
||||
|
||||
|
||||
Reference in new issue
Block a user