Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit c0a3465

Browse files
committed
Task 58: enforce packaged bootstrapper path
1 parent dd3f0f8 commit c0a3465

File tree

21 files changed

+211
-162
lines changed

21 files changed

+211
-162
lines changed

.github/workflows/ci.yml

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,50 @@ jobs:
270270
- name: Verify release pack readiness idempotency
271271
run: pnpm --filter @wpkernel/cli exec tsx scripts/check-release-pack-ci.ts
272272

273+
bootstrapper:
274+
name: Bootstrapper Resolution
275+
runs-on: ubuntu-latest
276+
needs: [setup, build]
277+
steps:
278+
- name: Checkout
279+
uses: actions/checkout@v4
280+
281+
- name: Setup pnpm
282+
uses: pnpm/action-setup@v4
283+
with:
284+
version: ${{ env.PNPM_VERSION }}
285+
286+
- name: Setup Node.js
287+
uses: actions/setup-node@v4
288+
with:
289+
node-version: ${{ env.NODE_VERSION }}
290+
cache: 'pnpm'
291+
292+
- name: Restore node_modules
293+
uses: actions/cache@v4
294+
with:
295+
path: |
296+
node_modules
297+
packages/*/node_modules
298+
examples/*/node_modules
299+
key: node-modules-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
300+
fail-on-cache-miss: true
301+
302+
- name: Restore build outputs
303+
uses: actions/cache@v4
304+
with:
305+
path: |
306+
packages/*/dist
307+
packages/*/*.tsbuildinfo
308+
examples/*/build
309+
examples/*/dist
310+
packages/php-json-ast/vendor
311+
key: build-${{ runner.os }}-${{ hashFiles('packages/*/src/**','examples/*/src/**','tsconfig*.json','pnpm-lock.yaml','packages/php-json-ast/composer.lock','packages/php-json-ast/composer.json') }}
312+
fail-on-cache-miss: true
313+
314+
- name: Verify bootstrapper resolution
315+
run: pnpm --filter @wpkernel/cli exec tsx scripts/check-bootstrapper-resolution-ci.ts
316+
273317
# E2E Tests (disabled)
274318
# e2e-test:
275319
# name: E2E Tests
@@ -331,7 +375,7 @@ jobs:
331375
all-checks:
332376
name: All Checks Passed
333377
runs-on: ubuntu-latest
334-
needs: [lint, build, unit-test, release-pack]
378+
needs: [lint, build, unit-test, release-pack, bootstrapper]
335379
if: always()
336380
steps:
337381
- name: Check status

DEVELOPMENT.md

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -95,18 +95,19 @@ pnpm e2e --project chromium
9595
9696
## 🔧 Core Scripts Cheat Sheet
9797

98-
| Task | Script | VS Code Task | Purpose |
99-
| ---------------- | ----------------------------------------------------------------------- | -------------------------------- | ------------------------------------------------------------------------- |
100-
| **Setup** | `pnpm install` | Tasks: Install | Install all dependencies |
101-
| **Build** | `pnpm build` | Tasks: Build All | One-shot production build |
102-
| **Dev mode** | `pnpm dev` | Tasks: Dev: Watch All | Rebuild packages on changes |
103-
| **WordPress** | `pnpm wp:fresh` | Tasks: WordPress: Fresh Start | Start WP + seed test data |
104-
| **Unit tests** | `pnpm test` | Tasks: Test: Unit Tests | Jest tests (no WordPress) |
105-
| **E2E tests** | `pnpm e2e` | Tasks: Test: E2E Tests | Playwright (headless) - **run from root only** |
106-
| **E2E debug** | `pnpm e2e:ui` | Tasks: Test: E2E Tests (UI) | Playwright with UI - **run from root only** |
107-
| **Format** | `pnpm format` | Tasks: Format | Prettier code formatting |
108-
| **Type check** | `pnpm typecheck` | Tasks: TypeCheck: All | TypeScript validation |
109-
| **Release gate** | `pnpm --filter @wpkernel/cli exec tsx scripts/check-release-pack-ci.ts` | Workflow: Release Pack Readiness | Deterministic publish-order + artefact verification (same helper CI runs) |
98+
| Task | Script | VS Code Task | Purpose |
99+
| --------------------- | ---------------------------------------------------------------------------------- | --------------------------------- | ------------------------------------------------------------------------- |
100+
| **Setup** | `pnpm install` | Tasks: Install | Install all dependencies |
101+
| **Build** | `pnpm build` | Tasks: Build All | One-shot production build |
102+
| **Dev mode** | `pnpm dev` | Tasks: Dev: Watch All | Rebuild packages on changes |
103+
| **WordPress** | `pnpm wp:fresh` | Tasks: WordPress: Fresh Start | Start WP + seed test data |
104+
| **Unit tests** | `pnpm test` | Tasks: Test: Unit Tests | Jest tests (no WordPress) |
105+
| **E2E tests** | `pnpm e2e` | Tasks: Test: E2E Tests | Playwright (headless) - **run from root only** |
106+
| **E2E debug** | `pnpm e2e:ui` | Tasks: Test: E2E Tests (UI) | Playwright with UI - **run from root only** |
107+
| **Format** | `pnpm format` | Tasks: Format | Prettier code formatting |
108+
| **Type check** | `pnpm typecheck` | Tasks: TypeCheck: All | TypeScript validation |
109+
| **Release gate** | `pnpm --filter @wpkernel/cli exec tsx scripts/check-release-pack-ci.ts` | Workflow: Release Pack Readiness | Deterministic publish-order + artefact verification (same helper CI runs) |
110+
| **Bootstrapper gate** | `pnpm --filter @wpkernel/cli exec tsx scripts/check-bootstrapper-resolution-ci.ts` | Workflow: Bootstrapper Resolution | Executes the compiled create-wpk bootstrapper in an isolated workspace |
110111

111112
> **💡 Pro Tip**: Use VS Code tasks (`Cmd+Shift+P` → "Run Task") - they're pre-configured and more reliable than remembering scripts!
112113

docs/.vitepress/critical-create-generate-failure.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,8 @@ Guarantee the compiled `create-wpk` bootstrapper resolves only its bundled depen
113113
**Completion log.** Update after each run:
114114

115115
- [x] 2025-11-11 — Added `createBootstrapperResolutionReadinessHelper` to execute `node packages/create-wpk/dist/index.js -- --help` inside a `/tmp/wpk-bootstrapper-*` workspace with `WPK_CLI_FORCE_SOURCE=0`, surfacing `EnvironmentalError(bootstrapper.resolve)` diagnostics when the compiled bootstrapper escapes its tarball dependencies and documenting the harness in code and tests.
116-
- [ ] _Run log placeholder — update after execution_
116+
- [x] 2025-11-21 — Updated the pipeline build to run `tsc --noEmit` so Vite preserves ESM `.js` specifiers, preventing the helper bundle from rewriting relative imports without extensions; confirmed `node packages/create-wpk/dist/index.js -- --help` resolves in an isolated `/tmp/wpk-bootstrapper-*` workspace.
117+
- [x] 2025-11-22 — Removed the `WPK_CLI_FORCE_SOURCE` escape hatch, forcing the CLI binary to require compiled dist assets and wiring a `bootstrapper` CI job that runs `scripts/check-bootstrapper-resolution-ci.ts` to execute `packages/create-wpk/dist/index.js -- --help` on every PR.
117118

118119
**Discovery to finish before coding.**
119120

@@ -126,7 +127,7 @@ The harness now lives alongside `createBootstrapperResolutionReadinessHelper` in
126127

127128
**Fix.** Adjust packaging (Rollup config, `package.json#files`) so dependencies land in the tarball, and update tests to assert the helper succeeds. Document the resolution contract here once implemented.
128129

129-
**Retire.** Source-mode escape hatches (`WPK_CLI_FORCE_SOURCE`) that mask missing bundled files.
130+
**Retire.** Source-mode escape hatches (`WPK_CLI_FORCE_SOURCE`) that mask missing bundled files. The bin loader and readiness harness now refuse to honour the flag, and CI exercises the compiled bootstrapper directly so regressions surface immediately.
130131

131132
## Task 59 — Quickstart Fidelity
132133

docs/api/@wpkernel/cli/README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,6 @@ wpk and UI packages.
114114
- [CreateHelperOptions](interfaces/CreateHelperOptions.md)
115115
- [DxContext](interfaces/DxContext.md)
116116
- [DxEnvironment](interfaces/DxEnvironment.md)
117-
- [DxRuntimeFlags](interfaces/DxRuntimeFlags.md)
118117
- [FileWriteRecord](interfaces/FileWriteRecord.md)
119118
- [FileWriterSummary](interfaces/FileWriterSummary.md)
120119
- [GenerationManifest](interfaces/GenerationManifest.md)

docs/api/@wpkernel/cli/interfaces/DxEnvironment.md

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,6 @@ Directory where the CLI process was invoked.
2020

2121
---
2222

23-
### flags
24-
25-
```ts
26-
readonly flags: DxRuntimeFlags;
27-
```
28-
29-
Feature flags toggled by the current runtime.
30-
31-
---
32-
3323
### projectRoot
3424

3525
```ts

docs/api/@wpkernel/cli/interfaces/DxRuntimeFlags.md

Lines changed: 0 additions & 22 deletions
This file was deleted.

packages/cli/bin/wpk.js

100755100644
Lines changed: 21 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -3,93 +3,42 @@
33
/**
44
* WPKernel CLI
55
*
6-
* Main executable entry point
6+
* Main executable entry point.
77
*/
88

99
const distInitModule = new URL('../dist/commands/init.js', import.meta.url);
1010
const distRunModule = new URL('../dist/cli/run.js', import.meta.url);
11-
const sourceInitModule = new URL('../src/commands/init.ts', import.meta.url);
12-
const sourceRunModule = new URL('../src/cli/run.ts', import.meta.url);
13-
const forceSource = process.env.WPK_CLI_FORCE_SOURCE === '1';
1411

15-
let initModuleUrl = distInitModule;
16-
const runModule = await loadCliModule();
17-
18-
globalThis.__WPK_CLI_MODULE_URL__ = initModuleUrl.href;
12+
globalThis.__WPK_CLI_MODULE_URL__ = distInitModule.href;
1913

2014
try {
21-
const { runCli } = runModule;
15+
const { runCli } = await import(distRunModule.href);
2216
await runCli(process.argv.slice(2));
2317
} catch (error) {
24-
console.error('[wpk] fatal', error);
18+
handleLoadFailure(error);
2519
process.exitCode = 1;
2620
}
2721

28-
async function loadCliModule() {
29-
if (!forceSource) {
30-
globalThis.__WPK_CLI_MODULE_URL__ = distInitModule.href;
31-
32-
try {
33-
return await import(distRunModule.href);
34-
} catch (error) {
35-
if (!isMissingModuleError(error, distRunModule)) {
36-
throw error;
37-
}
22+
function handleLoadFailure(error) {
23+
if (isMissingModuleError(error)) {
24+
console.error(
25+
'[wpk] missing compiled CLI artifacts. ' +
26+
'Build the CLI with "pnpm --filter @wpkernel/cli build" before invoking wpk.'
27+
);
28+
if (error) {
29+
console.error(error);
3830
}
31+
return;
3932
}
4033

41-
initModuleUrl = sourceInitModule;
42-
globalThis.__WPK_CLI_MODULE_URL__ = sourceInitModule.href;
43-
44-
try {
45-
const { tsImport } = await import('tsx/esm/api');
46-
return await tsImport(sourceRunModule.href, import.meta.url);
47-
} catch (innerError) {
48-
if (isMissingSpecifier(innerError, 'tsx/esm/api')) {
49-
throw new Error(
50-
'The "tsx" peer dependency is required to run the WPKernel CLI from source. ' +
51-
'Install it (e.g. "pnpm add -D tsx") or build the CLI with ' +
52-
'"pnpm --filter @wpkernel/cli build" before running wpk.'
53-
);
54-
}
55-
56-
throw innerError;
57-
}
58-
}
59-
60-
function isMissingModuleError(error, expectedUrl) {
61-
if (!error || typeof error !== 'object') {
62-
return false;
63-
}
64-
65-
if ('code' in error && error.code === 'ERR_MODULE_NOT_FOUND') {
66-
if ('url' in error && error.url === expectedUrl.href) {
67-
return true;
68-
}
69-
70-
if (typeof error.message === 'string') {
71-
return (
72-
error.message.includes(expectedUrl.href) ||
73-
error.message.includes(expectedUrl.pathname)
74-
);
75-
}
76-
}
77-
78-
return false;
34+
console.error('[wpk] fatal', error);
7935
}
8036

81-
function isMissingSpecifier(error, specifier) {
82-
if (!error || typeof error !== 'object') {
83-
return false;
84-
}
85-
86-
if (
87-
'code' in error &&
88-
error.code === 'ERR_MODULE_NOT_FOUND' &&
89-
typeof error.message === 'string'
90-
) {
91-
return error.message.includes(specifier);
92-
}
93-
94-
return false;
37+
function isMissingModuleError(error) {
38+
return Boolean(
39+
error &&
40+
typeof error === 'object' &&
41+
'code' in error &&
42+
error.code === 'ERR_MODULE_NOT_FOUND'
43+
);
9544
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import path from 'node:path';
2+
import process from 'node:process';
3+
import { createBootstrapperResolutionReadinessHelper } from '../src/dx/readiness/helpers/bootstrapperResolution';
4+
import type { DxContext } from '../src/dx/context';
5+
6+
interface Reporter {
7+
info: (message: string, context?: unknown) => void;
8+
warn: (message: string, context?: unknown) => void;
9+
error: (message: string, context?: unknown) => void;
10+
debug: (message: string, context?: unknown) => void;
11+
child: (namespace: string) => Reporter;
12+
}
13+
14+
function createSilentReporter(): Reporter {
15+
return {
16+
info: () => undefined,
17+
warn: () => undefined,
18+
error: () => undefined,
19+
debug: () => undefined,
20+
child: () => createSilentReporter(),
21+
} satisfies Reporter;
22+
}
23+
24+
function buildContext(): DxContext {
25+
const reporter = createSilentReporter();
26+
27+
return {
28+
reporter,
29+
workspace: null,
30+
environment: {
31+
cwd: process.cwd(),
32+
projectRoot: path.resolve('packages/cli'),
33+
workspaceRoot: null,
34+
},
35+
} satisfies DxContext;
36+
}
37+
38+
function assertReadyStatus(
39+
stage: 'detection' | 'confirmation' | 'rerun',
40+
status: 'ready' | 'pending',
41+
message?: string
42+
): void {
43+
if (status === 'ready') {
44+
return;
45+
}
46+
47+
const reason = message ?? 'No diagnostic message provided.';
48+
throw new Error(
49+
`bootstrapper-resolution helper ${stage} reported ${status} status: ${reason}`
50+
);
51+
}
52+
53+
async function main(): Promise<void> {
54+
const helper = createBootstrapperResolutionReadinessHelper();
55+
const context = buildContext();
56+
57+
const detection = await helper.detect(context);
58+
assertReadyStatus('detection', detection.status, detection.message);
59+
60+
const confirmation = await helper.confirm(context, detection.state);
61+
assertReadyStatus(
62+
'confirmation',
63+
confirmation.status,
64+
confirmation.message
65+
);
66+
67+
const rerun = await helper.detect(context);
68+
assertReadyStatus('rerun', rerun.status, rerun.message);
69+
70+
const duration =
71+
rerun.state.lastRun?.durationMs ?? detection.state.lastRun?.durationMs;
72+
console.log(
73+
`Bootstrapper resolved bundled CLI dependencies in ${duration ?? 'unknown'}ms.`
74+
);
75+
}
76+
77+
main().catch((error) => {
78+
console.error(error);
79+
process.exitCode = 1;
80+
});

packages/cli/scripts/check-release-pack-ci.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ function buildContext(): DxContext {
4949
cwd: process.cwd(),
5050
projectRoot: path.resolve('packages/cli'),
5151
workspaceRoot: null,
52-
flags: { forceSource: process.env.WPK_CLI_FORCE_SOURCE === '1' },
5352
},
5453
} satisfies DxContext;
5554
}

0 commit comments

Comments
 (0)