@slowcook-ai/cli 0.19.5 → 0.19.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +184 -28
- package/dist/cli.js +53 -73
- package/dist/cli.js.map +1 -1
- package/dist/commands/serve/config.d.ts +140 -0
- package/dist/commands/serve/config.d.ts.map +1 -0
- package/dist/commands/serve/config.js +174 -0
- package/dist/commands/serve/config.js.map +1 -0
- package/dist/commands/serve/detect.d.ts +31 -0
- package/dist/commands/serve/detect.d.ts.map +1 -0
- package/dist/commands/serve/detect.js +56 -0
- package/dist/commands/serve/detect.js.map +1 -0
- package/dist/commands/serve/dev.d.ts +51 -0
- package/dist/commands/serve/dev.d.ts.map +1 -0
- package/dist/commands/serve/dev.js +126 -0
- package/dist/commands/serve/dev.js.map +1 -0
- package/dist/commands/serve/index.d.ts +28 -0
- package/dist/commands/serve/index.d.ts.map +1 -0
- package/dist/commands/serve/index.js +216 -0
- package/dist/commands/serve/index.js.map +1 -0
- package/dist/commands/serve/mock.d.ts +39 -0
- package/dist/commands/serve/mock.d.ts.map +1 -0
- package/dist/commands/serve/mock.js +132 -0
- package/dist/commands/serve/mock.js.map +1 -0
- package/dist/commands/serve/staging.d.ts +39 -0
- package/dist/commands/serve/staging.d.ts.map +1 -0
- package/dist/commands/serve/staging.js +190 -0
- package/dist/commands/serve/staging.js.map +1 -0
- package/dist/commands/stories/index.d.ts +15 -0
- package/dist/commands/stories/index.d.ts.map +1 -0
- package/dist/commands/stories/index.js +280 -0
- package/dist/commands/stories/index.js.map +1 -0
- package/dist/commands/stories/status.d.ts +74 -0
- package/dist/commands/stories/status.d.ts.map +1 -0
- package/dist/commands/stories/status.js +176 -0
- package/dist/commands/stories/status.js.map +1 -0
- package/dist/commands.manifest.d.ts +37 -0
- package/dist/commands.manifest.d.ts.map +1 -0
- package/dist/commands.manifest.js +289 -0
- package/dist/commands.manifest.js.map +1 -0
- package/dist/help.d.ts +43 -0
- package/dist/help.d.ts.map +1 -0
- package/dist/help.js +131 -0
- package/dist/help.js.map +1 -0
- package/package.json +3 -3
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `slowcook serve <profile> <verb>` — multi-mode dev/mock/staging.
|
|
3
|
+
*
|
|
4
|
+
* See `docs/plans/0.20-design-discussions.md` design #5.
|
|
5
|
+
*
|
|
6
|
+
* Phase 1 (this cut): `serve dev` is implemented end-to-end. `serve
|
|
7
|
+
* mock` and `serve staging` parse config + print a "Phase 2/3 stub"
|
|
8
|
+
* notice; the runtime implementation lands in tasks #20 / #21.
|
|
9
|
+
*
|
|
10
|
+
* Backward-compat: `slowcook dev-env <verb>` continues to work as an
|
|
11
|
+
* alias for `slowcook serve dev <verb>` (wired in cli.ts).
|
|
12
|
+
*/
|
|
13
|
+
import { loadServeConfig, getProfile } from "./config.js";
|
|
14
|
+
import { planServeDev } from "./dev.js";
|
|
15
|
+
import { planServeMock } from "./mock.js";
|
|
16
|
+
import { detectMockRunnable } from "./detect.js";
|
|
17
|
+
import { planServeStaging } from "./staging.js";
|
|
18
|
+
export function parseServeArgs(argv) {
|
|
19
|
+
const args = { repoRoot: process.cwd() };
|
|
20
|
+
// First two positionals (if present + not flags) are profile + verb.
|
|
21
|
+
let positionalIdx = 0;
|
|
22
|
+
for (let i = 0; i < argv.length; i++) {
|
|
23
|
+
const a = argv[i];
|
|
24
|
+
if (!a)
|
|
25
|
+
continue;
|
|
26
|
+
if (a.startsWith("-")) {
|
|
27
|
+
const next = argv[i + 1];
|
|
28
|
+
if (a === "--branch" && next) {
|
|
29
|
+
args.branch = next;
|
|
30
|
+
i++;
|
|
31
|
+
}
|
|
32
|
+
else if (a === "--story" && next) {
|
|
33
|
+
args.story = next;
|
|
34
|
+
i++;
|
|
35
|
+
}
|
|
36
|
+
else if (a === "--service" && next) {
|
|
37
|
+
args.service = next;
|
|
38
|
+
i++;
|
|
39
|
+
}
|
|
40
|
+
else if (a === "--scenario" && next) {
|
|
41
|
+
args.scenario = next;
|
|
42
|
+
i++;
|
|
43
|
+
}
|
|
44
|
+
else if (a === "--cwd" && next) {
|
|
45
|
+
args.repoRoot = next;
|
|
46
|
+
i++;
|
|
47
|
+
}
|
|
48
|
+
else if (a === "--follow" || a === "-f") {
|
|
49
|
+
args.follow = true;
|
|
50
|
+
}
|
|
51
|
+
else if (a === "--prune") {
|
|
52
|
+
args.prune = true;
|
|
53
|
+
}
|
|
54
|
+
else if (a === "--dry-run") {
|
|
55
|
+
args.dryRun = true;
|
|
56
|
+
}
|
|
57
|
+
else if (a === "--help" || a === "-h") {
|
|
58
|
+
args.verb = "--help";
|
|
59
|
+
}
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
if (positionalIdx === 0) {
|
|
63
|
+
args.profile = a;
|
|
64
|
+
positionalIdx++;
|
|
65
|
+
}
|
|
66
|
+
else if (positionalIdx === 1) {
|
|
67
|
+
args.verb = a;
|
|
68
|
+
positionalIdx++;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return args;
|
|
72
|
+
}
|
|
73
|
+
export function printHelp() {
|
|
74
|
+
console.log(`
|
|
75
|
+
slowcook serve — multi-mode dev / mock / staging on a shared box
|
|
76
|
+
|
|
77
|
+
Usage:
|
|
78
|
+
slowcook serve <profile> <verb> [options]
|
|
79
|
+
|
|
80
|
+
Profiles:
|
|
81
|
+
dev Bind-mount source for fast UI iteration.
|
|
82
|
+
mock Vite-dev mock app for vibe-feedback loops (auto-skip if mock/ lacks scripts.dev).
|
|
83
|
+
staging Built-image staging + named-scenario seed reset for PM walkthroughs.
|
|
84
|
+
|
|
85
|
+
Verbs:
|
|
86
|
+
up Bring up the profile (compose overlay or consumer's bringup_cmd).
|
|
87
|
+
sync [--branch X] Force-push branch X (or current HEAD) to the profile's source_branch.
|
|
88
|
+
down [--prune] Stop the profile's services. --prune also drops volumes.
|
|
89
|
+
logs [--service] [--follow] Tail logs (optionally one service).
|
|
90
|
+
reset [--scenario <name>] Re-run a staging scenario's seed scripts (no-op for dev/mock).
|
|
91
|
+
|
|
92
|
+
Backward-compat:
|
|
93
|
+
slowcook dev-env push ≡ slowcook serve dev sync
|
|
94
|
+
slowcook dev-env up ≡ slowcook serve dev up
|
|
95
|
+
slowcook dev-env reset ≡ slowcook serve dev reset
|
|
96
|
+
|
|
97
|
+
Config:
|
|
98
|
+
.brewing/serve.yaml (new, optional) OR .brewing/dev-env.yaml (legacy).
|
|
99
|
+
See \`docs/plans/0.20-design-discussions.md\` design #5 for the schema.
|
|
100
|
+
|
|
101
|
+
Examples:
|
|
102
|
+
slowcook serve dev up
|
|
103
|
+
slowcook serve dev sync --story 042
|
|
104
|
+
slowcook serve dev logs --service patient --follow
|
|
105
|
+
slowcook serve dev down --prune
|
|
106
|
+
`);
|
|
107
|
+
}
|
|
108
|
+
export async function serve(argv) {
|
|
109
|
+
const args = parseServeArgs(argv);
|
|
110
|
+
if (args.verb === "--help" || (!args.profile && !args.verb)) {
|
|
111
|
+
printHelp();
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
if (!args.profile) {
|
|
115
|
+
console.error("slowcook serve: missing <profile>. Try `slowcook serve --help`.");
|
|
116
|
+
process.exit(64);
|
|
117
|
+
}
|
|
118
|
+
if (!args.verb) {
|
|
119
|
+
console.error(`slowcook serve ${args.profile}: missing <verb>. Try \`slowcook serve --help\`.`);
|
|
120
|
+
process.exit(64);
|
|
121
|
+
}
|
|
122
|
+
let config;
|
|
123
|
+
try {
|
|
124
|
+
config = loadServeConfig(args.repoRoot);
|
|
125
|
+
}
|
|
126
|
+
catch (e) {
|
|
127
|
+
console.error(`slowcook serve: ${e.message}`);
|
|
128
|
+
process.exit(64);
|
|
129
|
+
}
|
|
130
|
+
const profile = getProfile(config, args.profile);
|
|
131
|
+
if (!profile) {
|
|
132
|
+
const available = Object.keys(config.profiles).join(", ") || "(none)";
|
|
133
|
+
console.error(`slowcook serve: profile "${args.profile}" not found in config. Available: ${available}.`);
|
|
134
|
+
process.exit(64);
|
|
135
|
+
}
|
|
136
|
+
switch (args.profile) {
|
|
137
|
+
case "dev": {
|
|
138
|
+
const result = runDevVerb(args, config, profile);
|
|
139
|
+
for (const line of result.output)
|
|
140
|
+
console.log(line);
|
|
141
|
+
if (result.exitCode !== 0)
|
|
142
|
+
process.exit(result.exitCode);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
case "mock": {
|
|
146
|
+
// Trade-off #4: auto-skip if mock/ isn't vite-runnable.
|
|
147
|
+
const detect = detectMockRunnable(args.repoRoot);
|
|
148
|
+
if (!detect.hasDevScript) {
|
|
149
|
+
console.log(`[serve mock] skipped — ${detect.reason}`);
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
const result = runMockVerb(args, config, profile);
|
|
153
|
+
for (const line of result.output)
|
|
154
|
+
console.log(line);
|
|
155
|
+
if (result.exitCode !== 0)
|
|
156
|
+
process.exit(result.exitCode);
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
case "staging": {
|
|
160
|
+
const result = runStagingVerb(args, config, profile);
|
|
161
|
+
for (const line of result.output)
|
|
162
|
+
console.log(line);
|
|
163
|
+
if (result.exitCode !== 0)
|
|
164
|
+
process.exit(result.exitCode);
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
default:
|
|
168
|
+
console.error(`slowcook serve: profile "${args.profile}" has no runtime in this release.`);
|
|
169
|
+
process.exit(64);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
function runDevVerb(args, config, profile) {
|
|
173
|
+
if (!profile)
|
|
174
|
+
throw new Error("unreachable");
|
|
175
|
+
const devArgs = {
|
|
176
|
+
verb: args.verb,
|
|
177
|
+
branch: args.branch,
|
|
178
|
+
story: args.story,
|
|
179
|
+
service: args.service,
|
|
180
|
+
follow: args.follow,
|
|
181
|
+
prune: args.prune,
|
|
182
|
+
repoRoot: args.repoRoot,
|
|
183
|
+
dryRun: args.dryRun,
|
|
184
|
+
};
|
|
185
|
+
return planServeDev(devArgs, config, profile);
|
|
186
|
+
}
|
|
187
|
+
function runMockVerb(args, config, profile) {
|
|
188
|
+
if (!profile)
|
|
189
|
+
throw new Error("unreachable");
|
|
190
|
+
const mockArgs = {
|
|
191
|
+
verb: args.verb,
|
|
192
|
+
branch: args.branch,
|
|
193
|
+
service: args.service,
|
|
194
|
+
follow: args.follow,
|
|
195
|
+
prune: args.prune,
|
|
196
|
+
repoRoot: args.repoRoot,
|
|
197
|
+
dryRun: args.dryRun,
|
|
198
|
+
};
|
|
199
|
+
return planServeMock(mockArgs, config, profile);
|
|
200
|
+
}
|
|
201
|
+
function runStagingVerb(args, config, profile) {
|
|
202
|
+
if (!profile)
|
|
203
|
+
throw new Error("unreachable");
|
|
204
|
+
const stagingArgs = {
|
|
205
|
+
verb: args.verb,
|
|
206
|
+
branch: args.branch,
|
|
207
|
+
scenario: args.scenario,
|
|
208
|
+
service: args.service,
|
|
209
|
+
follow: args.follow,
|
|
210
|
+
prune: args.prune,
|
|
211
|
+
repoRoot: args.repoRoot,
|
|
212
|
+
dryRun: args.dryRun,
|
|
213
|
+
};
|
|
214
|
+
return planServeStaging(stagingArgs, config, profile);
|
|
215
|
+
}
|
|
216
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/commands/serve/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `slowcook serve mock <verb>` — Phase 2 implementation.
|
|
3
|
+
*
|
|
4
|
+
* The mock profile runs the consumer's `mock/` package as a vite-dev
|
|
5
|
+
* server on a shared box, so the PM + designer + vibe-feedback loop
|
|
6
|
+
* all hit the same artefact at the same URL. Mock-vite runs alongside
|
|
7
|
+
* `serve dev` on the same host with distinct ports + profile-prefixed
|
|
8
|
+
* container names (per design #5).
|
|
9
|
+
*
|
|
10
|
+
* Verbs (Phase 2):
|
|
11
|
+
* - up — bring up the mock profile (vite-dev compose overlay)
|
|
12
|
+
* - sync — force-push the mock-tracking branch (default `main`) so
|
|
13
|
+
* the box pulls the latest mockups
|
|
14
|
+
* - down — stop the mock services (volumes preserved)
|
|
15
|
+
* - logs — pass-through to docker-compose logs
|
|
16
|
+
* - reset — no-op (mock has no scenario seed)
|
|
17
|
+
*
|
|
18
|
+
* Auto-skip per Trade-off #4: if the consumer's `mock/package.json`
|
|
19
|
+
* has no `scripts.dev`, slowcook prints a notice + exits 0 instead of
|
|
20
|
+
* trying to bring up an unrunnable vite-dev. Detection runs from
|
|
21
|
+
* `index.ts`'s dispatcher before this module is called.
|
|
22
|
+
*/
|
|
23
|
+
import type { ProfileConfig, ServeConfig } from "./config.js";
|
|
24
|
+
import type { DevVerbResult } from "./dev.js";
|
|
25
|
+
export interface MockVerbArgs {
|
|
26
|
+
verb: string;
|
|
27
|
+
branch?: string;
|
|
28
|
+
service?: string;
|
|
29
|
+
follow?: boolean;
|
|
30
|
+
prune?: boolean;
|
|
31
|
+
repoRoot: string;
|
|
32
|
+
dryRun?: boolean;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Pure planner for the mock profile. Same shape as `planServeDev` so
|
|
36
|
+
* the cli wrapper can render output uniformly.
|
|
37
|
+
*/
|
|
38
|
+
export declare function planServeMock(args: MockVerbArgs, _config: ServeConfig, profile: ProfileConfig): DevVerbResult;
|
|
39
|
+
//# sourceMappingURL=mock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mock.d.ts","sourceRoot":"","sources":["../../../src/commands/serve/mock.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE9C,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,YAAY,EAClB,OAAO,EAAE,WAAW,EACpB,OAAO,EAAE,aAAa,GACrB,aAAa,CAqBf"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `slowcook serve mock <verb>` — Phase 2 implementation.
|
|
3
|
+
*
|
|
4
|
+
* The mock profile runs the consumer's `mock/` package as a vite-dev
|
|
5
|
+
* server on a shared box, so the PM + designer + vibe-feedback loop
|
|
6
|
+
* all hit the same artefact at the same URL. Mock-vite runs alongside
|
|
7
|
+
* `serve dev` on the same host with distinct ports + profile-prefixed
|
|
8
|
+
* container names (per design #5).
|
|
9
|
+
*
|
|
10
|
+
* Verbs (Phase 2):
|
|
11
|
+
* - up — bring up the mock profile (vite-dev compose overlay)
|
|
12
|
+
* - sync — force-push the mock-tracking branch (default `main`) so
|
|
13
|
+
* the box pulls the latest mockups
|
|
14
|
+
* - down — stop the mock services (volumes preserved)
|
|
15
|
+
* - logs — pass-through to docker-compose logs
|
|
16
|
+
* - reset — no-op (mock has no scenario seed)
|
|
17
|
+
*
|
|
18
|
+
* Auto-skip per Trade-off #4: if the consumer's `mock/package.json`
|
|
19
|
+
* has no `scripts.dev`, slowcook prints a notice + exits 0 instead of
|
|
20
|
+
* trying to bring up an unrunnable vite-dev. Detection runs from
|
|
21
|
+
* `index.ts`'s dispatcher before this module is called.
|
|
22
|
+
*/
|
|
23
|
+
import { execSync } from "node:child_process";
|
|
24
|
+
/**
|
|
25
|
+
* Pure planner for the mock profile. Same shape as `planServeDev` so
|
|
26
|
+
* the cli wrapper can render output uniformly.
|
|
27
|
+
*/
|
|
28
|
+
export function planServeMock(args, _config, profile) {
|
|
29
|
+
switch (args.verb) {
|
|
30
|
+
case "up":
|
|
31
|
+
return planUp(args, profile);
|
|
32
|
+
case "sync":
|
|
33
|
+
return planSync(args, profile);
|
|
34
|
+
case "down":
|
|
35
|
+
return planDown(args, profile);
|
|
36
|
+
case "logs":
|
|
37
|
+
return planLogs(args, profile);
|
|
38
|
+
case "reset":
|
|
39
|
+
return {
|
|
40
|
+
exitCode: 0,
|
|
41
|
+
output: ["[serve mock reset] mock profile has no scenario seed; nothing to reset (only meaningful for staging)."],
|
|
42
|
+
};
|
|
43
|
+
default:
|
|
44
|
+
return {
|
|
45
|
+
exitCode: 64,
|
|
46
|
+
output: [`Unknown verb: ${args.verb}. See \`slowcook serve --help\`.`],
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function planUp(_args, profile) {
|
|
51
|
+
const overlay = profile.compose_overlay;
|
|
52
|
+
const apps = Object.keys(profile.apps);
|
|
53
|
+
const lines = [`[serve mock up] bringing up: ${apps.join(", ") || "(no apps configured)"}`];
|
|
54
|
+
if (overlay) {
|
|
55
|
+
lines.push(` cmd: docker compose -f ${overlay} up -d --build`);
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
lines.push(" (no compose_overlay set; falling back to `pnpm --filter ./mock dev` if mock/ is vite-runnable)");
|
|
59
|
+
}
|
|
60
|
+
// PM + designer reference URL: surface the vite port if exactly one app.
|
|
61
|
+
if (apps.length === 1) {
|
|
62
|
+
const vitePort = profile.apps[apps[0]].port;
|
|
63
|
+
lines.push(` vite dev URL (once up): http://<your-box>:${vitePort}`);
|
|
64
|
+
}
|
|
65
|
+
return { exitCode: 0, output: lines };
|
|
66
|
+
}
|
|
67
|
+
function planSync(args, profile) {
|
|
68
|
+
const sourceBranch = profile.source_branch;
|
|
69
|
+
let localBranch = args.branch;
|
|
70
|
+
if (!localBranch && !args.dryRun) {
|
|
71
|
+
try {
|
|
72
|
+
localBranch = execSync("git rev-parse --abbrev-ref HEAD", {
|
|
73
|
+
cwd: args.repoRoot,
|
|
74
|
+
encoding: "utf8",
|
|
75
|
+
}).trim();
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// handled below
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
else if (!localBranch && args.dryRun) {
|
|
82
|
+
localBranch = "<current-branch>";
|
|
83
|
+
}
|
|
84
|
+
if (!localBranch || localBranch === "HEAD") {
|
|
85
|
+
return {
|
|
86
|
+
exitCode: 64,
|
|
87
|
+
output: ["[serve mock sync] couldn't resolve a local branch (detached HEAD?). Pass --branch <name>."],
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
const lines = [`[serve mock sync] ${localBranch} → origin/${sourceBranch}`];
|
|
91
|
+
if (args.dryRun) {
|
|
92
|
+
lines.push(` would run: git push --force origin ${localBranch}:${sourceBranch}`);
|
|
93
|
+
return { exitCode: 0, output: lines };
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
execSync(`git push --force origin ${localBranch}:${sourceBranch}`, {
|
|
97
|
+
cwd: args.repoRoot,
|
|
98
|
+
stdio: "inherit",
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return {
|
|
103
|
+
exitCode: 1,
|
|
104
|
+
output: [...lines, `[serve mock sync] git push failed. Check push permissions on origin/${sourceBranch}.`],
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
lines.push(`[serve mock sync] done. The mock-deploy workflow on origin/${sourceBranch} will fire next.`);
|
|
108
|
+
return { exitCode: 0, output: lines };
|
|
109
|
+
}
|
|
110
|
+
function planDown(args, profile) {
|
|
111
|
+
const overlay = profile.compose_overlay;
|
|
112
|
+
if (!overlay) {
|
|
113
|
+
return { exitCode: 64, output: ["[serve mock down] no compose_overlay set; nothing to stop."] };
|
|
114
|
+
}
|
|
115
|
+
const cmd = args.prune
|
|
116
|
+
? `docker compose -f ${overlay} down -v`
|
|
117
|
+
: `docker compose -f ${overlay} down`;
|
|
118
|
+
return { exitCode: 0, output: ["[serve mock down]", ` cmd: ${cmd}`] };
|
|
119
|
+
}
|
|
120
|
+
function planLogs(args, profile) {
|
|
121
|
+
const overlay = profile.compose_overlay;
|
|
122
|
+
if (!overlay) {
|
|
123
|
+
return { exitCode: 64, output: ["[serve mock logs] no compose_overlay set; nothing to tail."] };
|
|
124
|
+
}
|
|
125
|
+
const follow = args.follow ? "-f" : "";
|
|
126
|
+
const service = args.service ?? "";
|
|
127
|
+
return {
|
|
128
|
+
exitCode: 0,
|
|
129
|
+
output: [` cmd: docker compose -f ${overlay} logs ${follow} ${service}`.replace(/\s+/g, " ").trim()],
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=mock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mock.js","sourceRoot":"","sources":["../../../src/commands/serve/mock.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `slowcook serve staging <verb>` — Phase 3 implementation.
|
|
3
|
+
*
|
|
4
|
+
* The staging profile runs a built-image deployment for PM walkthroughs +
|
|
5
|
+
* manual QA. Distinct from `dev` (bind-mount source) and `mock` (vite-dev):
|
|
6
|
+
*
|
|
7
|
+
* - mode: built-image (Trade-off #3 — slowcook ships zero image-build;
|
|
8
|
+
* consumer's `bringup_cmd` does the heavy lifting).
|
|
9
|
+
* - reset: named-scenario re-seed via `seed.scenarios: {demo: ..., ...}`
|
|
10
|
+
* (Trade-off #5 — map shape from day 1; no single-value form).
|
|
11
|
+
*
|
|
12
|
+
* Verbs (Phase 3):
|
|
13
|
+
* - up — bring up the staging profile via consumer's bringup_cmd
|
|
14
|
+
* - sync — push the staging-tracking branch (default `main`); the
|
|
15
|
+
* box's CI/deploy chain picks it up + rebuilds the image
|
|
16
|
+
* - down — stop the profile's services (volumes preserved)
|
|
17
|
+
* - logs — pass-through to docker-compose logs
|
|
18
|
+
* - reset --scenario <name> — re-run the named scenario's seed scripts;
|
|
19
|
+
* protected by the optional `seed.guard_env` env-var sentinel
|
|
20
|
+
* to prevent accidental wipes from interactive sessions.
|
|
21
|
+
*
|
|
22
|
+
* Per design #5 "Additional decisions" — the staging seed is idempotent;
|
|
23
|
+
* each scenario's scripts MUST be re-runnable. Slowcook just invokes them
|
|
24
|
+
* via `ts-node` (Trade-off #2) inside the container.
|
|
25
|
+
*/
|
|
26
|
+
import type { ProfileConfig, ServeConfig } from "./config.js";
|
|
27
|
+
import type { DevVerbResult } from "./dev.js";
|
|
28
|
+
export interface StagingVerbArgs {
|
|
29
|
+
verb: string;
|
|
30
|
+
branch?: string;
|
|
31
|
+
scenario?: string;
|
|
32
|
+
service?: string;
|
|
33
|
+
follow?: boolean;
|
|
34
|
+
prune?: boolean;
|
|
35
|
+
repoRoot: string;
|
|
36
|
+
dryRun?: boolean;
|
|
37
|
+
}
|
|
38
|
+
export declare function planServeStaging(args: StagingVerbArgs, _config: ServeConfig, profile: ProfileConfig): DevVerbResult;
|
|
39
|
+
//# sourceMappingURL=staging.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"staging.d.ts","sourceRoot":"","sources":["../../../src/commands/serve/staging.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE9C,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,eAAe,EACrB,OAAO,EAAE,WAAW,EACpB,OAAO,EAAE,aAAa,GACrB,aAAa,CAkBf"}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `slowcook serve staging <verb>` — Phase 3 implementation.
|
|
3
|
+
*
|
|
4
|
+
* The staging profile runs a built-image deployment for PM walkthroughs +
|
|
5
|
+
* manual QA. Distinct from `dev` (bind-mount source) and `mock` (vite-dev):
|
|
6
|
+
*
|
|
7
|
+
* - mode: built-image (Trade-off #3 — slowcook ships zero image-build;
|
|
8
|
+
* consumer's `bringup_cmd` does the heavy lifting).
|
|
9
|
+
* - reset: named-scenario re-seed via `seed.scenarios: {demo: ..., ...}`
|
|
10
|
+
* (Trade-off #5 — map shape from day 1; no single-value form).
|
|
11
|
+
*
|
|
12
|
+
* Verbs (Phase 3):
|
|
13
|
+
* - up — bring up the staging profile via consumer's bringup_cmd
|
|
14
|
+
* - sync — push the staging-tracking branch (default `main`); the
|
|
15
|
+
* box's CI/deploy chain picks it up + rebuilds the image
|
|
16
|
+
* - down — stop the profile's services (volumes preserved)
|
|
17
|
+
* - logs — pass-through to docker-compose logs
|
|
18
|
+
* - reset --scenario <name> — re-run the named scenario's seed scripts;
|
|
19
|
+
* protected by the optional `seed.guard_env` env-var sentinel
|
|
20
|
+
* to prevent accidental wipes from interactive sessions.
|
|
21
|
+
*
|
|
22
|
+
* Per design #5 "Additional decisions" — the staging seed is idempotent;
|
|
23
|
+
* each scenario's scripts MUST be re-runnable. Slowcook just invokes them
|
|
24
|
+
* via `ts-node` (Trade-off #2) inside the container.
|
|
25
|
+
*/
|
|
26
|
+
import { execSync } from "node:child_process";
|
|
27
|
+
export function planServeStaging(args, _config, profile) {
|
|
28
|
+
switch (args.verb) {
|
|
29
|
+
case "up":
|
|
30
|
+
return planUp(args, profile);
|
|
31
|
+
case "sync":
|
|
32
|
+
return planSync(args, profile);
|
|
33
|
+
case "down":
|
|
34
|
+
return planDown(args, profile);
|
|
35
|
+
case "logs":
|
|
36
|
+
return planLogs(args, profile);
|
|
37
|
+
case "reset":
|
|
38
|
+
return planReset(args, profile);
|
|
39
|
+
default:
|
|
40
|
+
return {
|
|
41
|
+
exitCode: 64,
|
|
42
|
+
output: [`Unknown verb: ${args.verb}. See \`slowcook serve --help\`.`],
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function planUp(_args, profile) {
|
|
47
|
+
const lines = [`[serve staging up] mode: ${profile.mode}`];
|
|
48
|
+
if (profile.bringup_cmd) {
|
|
49
|
+
// Trade-off #3 — consumer's bring-up runs the show. Slowcook just shells out.
|
|
50
|
+
lines.push(` cmd: ${profile.bringup_cmd}`);
|
|
51
|
+
return { exitCode: 0, output: lines };
|
|
52
|
+
}
|
|
53
|
+
if (profile.compose_overlay) {
|
|
54
|
+
lines.push(` cmd: docker compose -f ${profile.compose_overlay} up -d`);
|
|
55
|
+
return { exitCode: 0, output: lines };
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
exitCode: 64,
|
|
59
|
+
output: ["[serve staging up] neither bringup_cmd nor compose_overlay set; nothing to bring up."],
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function planSync(args, profile) {
|
|
63
|
+
const sourceBranch = profile.source_branch;
|
|
64
|
+
let localBranch = args.branch;
|
|
65
|
+
if (!localBranch && !args.dryRun) {
|
|
66
|
+
try {
|
|
67
|
+
localBranch = execSync("git rev-parse --abbrev-ref HEAD", {
|
|
68
|
+
cwd: args.repoRoot,
|
|
69
|
+
encoding: "utf8",
|
|
70
|
+
}).trim();
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
// handled below
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
else if (!localBranch && args.dryRun) {
|
|
77
|
+
localBranch = "<current-branch>";
|
|
78
|
+
}
|
|
79
|
+
if (!localBranch || localBranch === "HEAD") {
|
|
80
|
+
return {
|
|
81
|
+
exitCode: 64,
|
|
82
|
+
output: ["[serve staging sync] couldn't resolve a local branch (detached HEAD?). Pass --branch <name>."],
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
const lines = [`[serve staging sync] ${localBranch} → origin/${sourceBranch} (staging-deploy CI rebuilds)`];
|
|
86
|
+
if (args.dryRun) {
|
|
87
|
+
lines.push(` would run: git push --force origin ${localBranch}:${sourceBranch}`);
|
|
88
|
+
return { exitCode: 0, output: lines };
|
|
89
|
+
}
|
|
90
|
+
try {
|
|
91
|
+
execSync(`git push --force origin ${localBranch}:${sourceBranch}`, {
|
|
92
|
+
cwd: args.repoRoot,
|
|
93
|
+
stdio: "inherit",
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
return {
|
|
98
|
+
exitCode: 1,
|
|
99
|
+
output: [...lines, `[serve staging sync] git push failed. Check push permissions on origin/${sourceBranch}.`],
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
lines.push(`[serve staging sync] done.`);
|
|
103
|
+
return { exitCode: 0, output: lines };
|
|
104
|
+
}
|
|
105
|
+
function planDown(args, profile) {
|
|
106
|
+
if (!profile.compose_overlay) {
|
|
107
|
+
return { exitCode: 64, output: ["[serve staging down] no compose_overlay set; nothing to stop."] };
|
|
108
|
+
}
|
|
109
|
+
const cmd = args.prune
|
|
110
|
+
? `docker compose -f ${profile.compose_overlay} down -v`
|
|
111
|
+
: `docker compose -f ${profile.compose_overlay} down`;
|
|
112
|
+
return { exitCode: 0, output: ["[serve staging down]", ` cmd: ${cmd}`] };
|
|
113
|
+
}
|
|
114
|
+
function planLogs(args, profile) {
|
|
115
|
+
if (!profile.compose_overlay) {
|
|
116
|
+
return { exitCode: 64, output: ["[serve staging logs] no compose_overlay set; nothing to tail."] };
|
|
117
|
+
}
|
|
118
|
+
const follow = args.follow ? "-f" : "";
|
|
119
|
+
const service = args.service ?? "";
|
|
120
|
+
return {
|
|
121
|
+
exitCode: 0,
|
|
122
|
+
output: [` cmd: docker compose -f ${profile.compose_overlay} logs ${follow} ${service}`.replace(/\s+/g, " ").trim()],
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* `serve staging reset --scenario <name>` — re-run the named scenario's
|
|
127
|
+
* seed scripts. Idempotency is the seed-script author's job; slowcook
|
|
128
|
+
* just invokes them via ts-node (Trade-off #2 — runtime model locked).
|
|
129
|
+
*
|
|
130
|
+
* Guard: if `seed.guard_env` is set in the profile, the named env var
|
|
131
|
+
* must be non-empty in the caller's environment. Prevents accidental
|
|
132
|
+
* wipes from an interactive `serve staging reset`.
|
|
133
|
+
*/
|
|
134
|
+
function planReset(args, profile) {
|
|
135
|
+
if (!args.scenario) {
|
|
136
|
+
const available = profile.seed?.scenarios ? Object.keys(profile.seed.scenarios) : [];
|
|
137
|
+
return {
|
|
138
|
+
exitCode: 64,
|
|
139
|
+
output: [
|
|
140
|
+
"[serve staging reset] --scenario <name> required.",
|
|
141
|
+
` available scenarios: ${available.length ? available.join(", ") : "(none — declare seed.scenarios in serve.yaml)"}`,
|
|
142
|
+
],
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
const scenario = profile.seed?.scenarios[args.scenario];
|
|
146
|
+
if (!scenario) {
|
|
147
|
+
const available = profile.seed?.scenarios ? Object.keys(profile.seed.scenarios) : [];
|
|
148
|
+
return {
|
|
149
|
+
exitCode: 64,
|
|
150
|
+
output: [
|
|
151
|
+
`[serve staging reset] scenario "${args.scenario}" not found in seed.scenarios.`,
|
|
152
|
+
` available: ${available.length ? available.join(", ") : "(none)"}`,
|
|
153
|
+
],
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
if (profile.seed?.guard_env) {
|
|
157
|
+
const guard = profile.seed.guard_env;
|
|
158
|
+
const value = process.env[guard];
|
|
159
|
+
if (!value) {
|
|
160
|
+
return {
|
|
161
|
+
exitCode: 1,
|
|
162
|
+
output: [
|
|
163
|
+
`[serve staging reset] blocked — guard env \`${guard}\` is not set.`,
|
|
164
|
+
` set ${guard}=1 in this shell to authorise a reset.`,
|
|
165
|
+
],
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
const lines = [`[serve staging reset] scenario=${args.scenario}`];
|
|
170
|
+
for (const script of scenario.scripts) {
|
|
171
|
+
lines.push(` ts-node ${script}`);
|
|
172
|
+
}
|
|
173
|
+
if (args.dryRun || scenario.scripts.length === 0) {
|
|
174
|
+
return { exitCode: 0, output: lines };
|
|
175
|
+
}
|
|
176
|
+
// Real reset: shell out to ts-node for each script. Idempotency is the
|
|
177
|
+
// seed-script author's contract; slowcook bails on any non-zero exit.
|
|
178
|
+
for (const script of scenario.scripts) {
|
|
179
|
+
try {
|
|
180
|
+
execSync(`pnpm exec ts-node ${script}`, { cwd: args.repoRoot, stdio: "inherit" });
|
|
181
|
+
}
|
|
182
|
+
catch {
|
|
183
|
+
lines.push(`[serve staging reset] script ${script} failed; aborting.`);
|
|
184
|
+
return { exitCode: 1, output: lines };
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
lines.push(`[serve staging reset] scenario=${args.scenario} complete.`);
|
|
188
|
+
return { exitCode: 0, output: lines };
|
|
189
|
+
}
|
|
190
|
+
//# sourceMappingURL=staging.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"staging.js","sourceRoot":"","sources":["../../../src/commands/serve/staging.ts"],"names":[],"mappings":"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"}
|