@basaltbytes/odoo-agentic-dev 0.1.0-beta.1
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 +464 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +73 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/compose.d.ts +19 -0
- package/dist/commands/compose.js +29 -0
- package/dist/commands/compose.js.map +1 -0
- package/dist/commands/doctor.d.ts +35 -0
- package/dist/commands/doctor.js +209 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/down.d.ts +23 -0
- package/dist/commands/down.js +46 -0
- package/dist/commands/down.js.map +1 -0
- package/dist/commands/info.d.ts +10 -0
- package/dist/commands/info.js +42 -0
- package/dist/commands/info.js.map +1 -0
- package/dist/commands/json-report.d.ts +45 -0
- package/dist/commands/json-report.js +55 -0
- package/dist/commands/json-report.js.map +1 -0
- package/dist/commands/link-source.d.ts +33 -0
- package/dist/commands/link-source.js +116 -0
- package/dist/commands/link-source.js.map +1 -0
- package/dist/commands/list.d.ts +29 -0
- package/dist/commands/list.js +117 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/logs.d.ts +17 -0
- package/dist/commands/logs.js +28 -0
- package/dist/commands/logs.js.map +1 -0
- package/dist/commands/prune.d.ts +54 -0
- package/dist/commands/prune.js +118 -0
- package/dist/commands/prune.js.map +1 -0
- package/dist/commands/psql.d.ts +8 -0
- package/dist/commands/psql.js +24 -0
- package/dist/commands/psql.js.map +1 -0
- package/dist/commands/reset-db.d.ts +34 -0
- package/dist/commands/reset-db.js +86 -0
- package/dist/commands/reset-db.js.map +1 -0
- package/dist/commands/resolve-context.d.ts +17 -0
- package/dist/commands/resolve-context.js +30 -0
- package/dist/commands/resolve-context.js.map +1 -0
- package/dist/commands/run.d.ts +31 -0
- package/dist/commands/run.js +83 -0
- package/dist/commands/run.js.map +1 -0
- package/dist/commands/setup.d.ts +46 -0
- package/dist/commands/setup.js +93 -0
- package/dist/commands/setup.js.map +1 -0
- package/dist/commands/shell.d.ts +20 -0
- package/dist/commands/shell.js +46 -0
- package/dist/commands/shell.js.map +1 -0
- package/dist/commands/state-hooks.d.ts +28 -0
- package/dist/commands/state-hooks.js +86 -0
- package/dist/commands/state-hooks.js.map +1 -0
- package/dist/commands/test.d.ts +24 -0
- package/dist/commands/test.js +70 -0
- package/dist/commands/test.js.map +1 -0
- package/dist/commands/trailing-args.d.ts +10 -0
- package/dist/commands/trailing-args.js +14 -0
- package/dist/commands/trailing-args.js.map +1 -0
- package/dist/commands/up.d.ts +23 -0
- package/dist/commands/up.js +62 -0
- package/dist/commands/up.js.map +1 -0
- package/dist/commands/update.d.ts +7 -0
- package/dist/commands/update.js +31 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/commands/worktree.d.ts +97 -0
- package/dist/commands/worktree.js +355 -0
- package/dist/commands/worktree.js.map +1 -0
- package/dist/config/load-recipe.d.ts +14 -0
- package/dist/config/load-recipe.js +75 -0
- package/dist/config/load-recipe.js.map +1 -0
- package/dist/config/schema.d.ts +9 -0
- package/dist/config/schema.js +190 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/core/command-plan.d.ts +34 -0
- package/dist/core/command-plan.js +112 -0
- package/dist/core/command-plan.js.map +1 -0
- package/dist/core/compose-model.d.ts +18 -0
- package/dist/core/compose-model.js +80 -0
- package/dist/core/compose-model.js.map +1 -0
- package/dist/core/compose-project.d.ts +3 -0
- package/dist/core/compose-project.js +15 -0
- package/dist/core/compose-project.js.map +1 -0
- package/dist/core/database-name.d.ts +15 -0
- package/dist/core/database-name.js +59 -0
- package/dist/core/database-name.js.map +1 -0
- package/dist/core/environment.d.ts +84 -0
- package/dist/core/environment.js +78 -0
- package/dist/core/environment.js.map +1 -0
- package/dist/core/port-allocator.d.ts +30 -0
- package/dist/core/port-allocator.js +58 -0
- package/dist/core/port-allocator.js.map +1 -0
- package/dist/core/project-recipe.d.ts +163 -0
- package/dist/core/project-recipe.js +13 -0
- package/dist/core/project-recipe.js.map +1 -0
- package/dist/core/safety.d.ts +11 -0
- package/dist/core/safety.js +16 -0
- package/dist/core/safety.js.map +1 -0
- package/dist/core/worktree-context.d.ts +32 -0
- package/dist/core/worktree-context.js +94 -0
- package/dist/core/worktree-context.js.map +1 -0
- package/dist/errors/errors.d.ts +124 -0
- package/dist/errors/errors.js +129 -0
- package/dist/errors/errors.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/platform/command-runner.d.ts +30 -0
- package/dist/platform/command-runner.js +65 -0
- package/dist/platform/command-runner.js.map +1 -0
- package/dist/platform/docker-compose.d.ts +69 -0
- package/dist/platform/docker-compose.js +239 -0
- package/dist/platform/docker-compose.js.map +1 -0
- package/dist/platform/git.d.ts +10 -0
- package/dist/platform/git.js +35 -0
- package/dist/platform/git.js.map +1 -0
- package/dist/platform/odoo-lifecycle.d.ts +30 -0
- package/dist/platform/odoo-lifecycle.js +109 -0
- package/dist/platform/odoo-lifecycle.js.map +1 -0
- package/dist/platform/port-probe.d.ts +7 -0
- package/dist/platform/port-probe.js +13 -0
- package/dist/platform/port-probe.js.map +1 -0
- package/dist/platform/process-supervisor.d.ts +16 -0
- package/dist/platform/process-supervisor.js +23 -0
- package/dist/platform/process-supervisor.js.map +1 -0
- package/dist/platform/state-store.d.ts +37 -0
- package/dist/platform/state-store.js +122 -0
- package/dist/platform/state-store.js.map +1 -0
- package/dist/suppress-sqlite-warning.d.ts +1 -0
- package/dist/suppress-sqlite-warning.js +18 -0
- package/dist/suppress-sqlite-warning.js.map +1 -0
- package/dist/testing/fake-adapters.d.ts +34 -0
- package/dist/testing/fake-adapters.js +90 -0
- package/dist/testing/fake-adapters.js.map +1 -0
- package/package.json +78 -0
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { Console, Effect, Option } from "effect";
|
|
3
|
+
import { Command, Flag } from "effect/unstable/cli";
|
|
4
|
+
import { loadRecipe } from "../config/load-recipe.js";
|
|
5
|
+
import { classifyEnvironments } from "../core/environment.js";
|
|
6
|
+
import { buildWorktreeContext } from "../core/worktree-context.js";
|
|
7
|
+
import { tail } from "../errors/errors.js";
|
|
8
|
+
import { CommandRunner } from "../platform/command-runner.js";
|
|
9
|
+
import { DockerCompose } from "../platform/docker-compose.js";
|
|
10
|
+
import { Git } from "../platform/git.js";
|
|
11
|
+
import { PortProbe } from "../platform/port-probe.js";
|
|
12
|
+
import { resolveStateDbPath, StateStore } from "../platform/state-store.js";
|
|
13
|
+
import { buildProbes } from "./prune.js";
|
|
14
|
+
/** Engines floor: Node >= 22.15 (first unflagged node:sqlite). */
|
|
15
|
+
export const nodeVersionOk = (version) => {
|
|
16
|
+
const [major = 0, minor = 0] = version.split(".").map((part) => Number.parseInt(part, 10));
|
|
17
|
+
return major > 22 || (major === 22 && minor >= 15);
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* "Compose v2" means the Go `docker compose` plugin (the python v1
|
|
21
|
+
* `docker-compose` is unsupported). Plugin releases moved past v2 numbering
|
|
22
|
+
* (v5.x today), so accept any major >= 2 instead of grepping for "v2".
|
|
23
|
+
*/
|
|
24
|
+
export const composeVersionOk = (stdout) => {
|
|
25
|
+
const match = stdout.match(/version\s+v?(\d+)/i);
|
|
26
|
+
return match !== null && Number.parseInt(match[1], 10) >= 2;
|
|
27
|
+
};
|
|
28
|
+
export const detectWsl = (procVersion) => procVersion !== null && procVersion.toLowerCase().includes("microsoft");
|
|
29
|
+
/** Total: /proc/version is absent on non-Linux hosts, which reads as null. */
|
|
30
|
+
const readProcVersion = () => {
|
|
31
|
+
try {
|
|
32
|
+
return readFileSync("/proc/version", "utf8");
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
const WSL_GUIDANCE = "WSL2 detected — keep repos inside the WSL filesystem (not /mnt/c), " +
|
|
39
|
+
"install Node, pnpm, git, and the docker CLI inside WSL, enable Docker Desktop " +
|
|
40
|
+
"WSL integration, and pass Linux paths";
|
|
41
|
+
export const hasHardFailure = (checks) => checks.some((check) => check.hard && !check.ok);
|
|
42
|
+
export const formatDoctorReport = (checks) => checks
|
|
43
|
+
.map((check) => `${check.ok ? "✓" : "✗"} ${check.name} — ${check.detail}${check.ok ? "" : check.hard ? " (hard)" : " (soft)"}`)
|
|
44
|
+
.join("\n");
|
|
45
|
+
/**
|
|
46
|
+
* Run every doctor probe and fold each outcome — success or failure — into a
|
|
47
|
+
* check row. This never fails: a broken docker/state/config is a finding, not
|
|
48
|
+
* an error. The exit-code decision belongs to the command via hasHardFailure.
|
|
49
|
+
*/
|
|
50
|
+
export const collectDoctorChecks = (explicitConfigPath) => Effect.gen(function* () {
|
|
51
|
+
const runner = yield* CommandRunner;
|
|
52
|
+
const compose = yield* DockerCompose;
|
|
53
|
+
const store = yield* StateStore;
|
|
54
|
+
const probe = yield* PortProbe;
|
|
55
|
+
const git = yield* Git;
|
|
56
|
+
const checks = [];
|
|
57
|
+
const exec = (command, args) => runner
|
|
58
|
+
.run({ command, args })
|
|
59
|
+
.pipe(Effect.catch((error) => Effect.succeed({ exitCode: -1, stdout: "", stderr: error.message })));
|
|
60
|
+
const dockerVersion = yield* exec("docker", ["version", "--format", "json"]);
|
|
61
|
+
checks.push({
|
|
62
|
+
name: "docker-daemon",
|
|
63
|
+
ok: dockerVersion.exitCode === 0,
|
|
64
|
+
hard: true,
|
|
65
|
+
detail: dockerVersion.exitCode === 0
|
|
66
|
+
? "docker daemon responsive"
|
|
67
|
+
: tail(dockerVersion.stderr) || "docker CLI not found",
|
|
68
|
+
});
|
|
69
|
+
const composeVersion = yield* exec("docker", ["compose", "version"]);
|
|
70
|
+
const composeOk = composeVersion.exitCode === 0 && composeVersionOk(composeVersion.stdout);
|
|
71
|
+
checks.push({
|
|
72
|
+
name: "compose-v2",
|
|
73
|
+
ok: composeOk,
|
|
74
|
+
hard: true,
|
|
75
|
+
detail: composeOk
|
|
76
|
+
? (composeVersion.stdout.trim().split("\n")[0] ?? "")
|
|
77
|
+
: "docker compose v2+ not found (python compose v1 is unsupported)",
|
|
78
|
+
});
|
|
79
|
+
checks.push({
|
|
80
|
+
name: "node-version",
|
|
81
|
+
ok: nodeVersionOk(process.versions.node),
|
|
82
|
+
hard: true,
|
|
83
|
+
detail: `node ${process.versions.node} (need >= 22.15)`,
|
|
84
|
+
});
|
|
85
|
+
const config = yield* loadRecipe({
|
|
86
|
+
cwd: process.cwd(),
|
|
87
|
+
explicitPath: explicitConfigPath,
|
|
88
|
+
env: process.env,
|
|
89
|
+
}).pipe(Effect.map((loaded) => ({ ok: true, ...loaded })), Effect.catch((error) => Effect.succeed({ ok: false, message: error.message })));
|
|
90
|
+
checks.push({
|
|
91
|
+
name: "config",
|
|
92
|
+
ok: config.ok,
|
|
93
|
+
hard: false,
|
|
94
|
+
detail: config.ok
|
|
95
|
+
? `project "${config.recipe.project.id}" at ${config.rootDir}`
|
|
96
|
+
: config.message,
|
|
97
|
+
});
|
|
98
|
+
let ctx = null;
|
|
99
|
+
if (config.ok) {
|
|
100
|
+
const derived = yield* git.state(config.rootDir).pipe(Effect.flatMap((gitState) => buildWorktreeContext({
|
|
101
|
+
rootDir: config.rootDir,
|
|
102
|
+
recipe: config.recipe,
|
|
103
|
+
env: process.env,
|
|
104
|
+
git: gitState,
|
|
105
|
+
})), Effect.map((value) => ({ ok: true, ctx: value })), Effect.catch((error) => Effect.succeed({ ok: false, message: error.message })));
|
|
106
|
+
checks.push({
|
|
107
|
+
name: "context",
|
|
108
|
+
ok: derived.ok,
|
|
109
|
+
hard: false,
|
|
110
|
+
detail: derived.ok
|
|
111
|
+
? `database ${derived.ctx.databaseName}, compose project ${derived.ctx.composeProjectName}, port ${derived.ctx.odooHttpPort}`
|
|
112
|
+
: derived.message,
|
|
113
|
+
});
|
|
114
|
+
if (derived.ok)
|
|
115
|
+
ctx = derived.ctx;
|
|
116
|
+
}
|
|
117
|
+
const rowsResult = yield* store.list({}).pipe(Effect.map((rows) => ({ ok: true, rows })), Effect.catch((error) => Effect.succeed({ ok: false, message: error.message })));
|
|
118
|
+
const rows = rowsResult.ok ? rowsResult.rows : null;
|
|
119
|
+
if (ctx !== null) {
|
|
120
|
+
const currentCtx = ctx;
|
|
121
|
+
const free = yield* probe.isFree(currentCtx.odooHttpPort);
|
|
122
|
+
const holder = rows?.find((row) => row.odooHttpPort === currentCtx.odooHttpPort)?.composeProject ?? null;
|
|
123
|
+
checks.push({
|
|
124
|
+
name: "port",
|
|
125
|
+
// a busy port is fine as long as we can name the stack sitting on it
|
|
126
|
+
ok: free || holder !== null,
|
|
127
|
+
hard: true,
|
|
128
|
+
detail: free
|
|
129
|
+
? `port ${currentCtx.odooHttpPort} is free`
|
|
130
|
+
: holder !== null
|
|
131
|
+
? `port ${currentCtx.odooHttpPort} in use by known stack "${holder}"`
|
|
132
|
+
: `port ${currentCtx.odooHttpPort} in use by an unknown process`,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
if (rows !== null) {
|
|
136
|
+
const byPort = new Map();
|
|
137
|
+
for (const row of rows) {
|
|
138
|
+
if (row.odooHttpPort === 0)
|
|
139
|
+
continue; // adopted rows: port unknown
|
|
140
|
+
byPort.set(row.odooHttpPort, [...(byPort.get(row.odooHttpPort) ?? []), row.composeProject]);
|
|
141
|
+
}
|
|
142
|
+
const collisions = [...byPort.entries()].filter(([, stacks]) => stacks.length > 1);
|
|
143
|
+
checks.push({
|
|
144
|
+
name: "port-collisions",
|
|
145
|
+
ok: collisions.length === 0,
|
|
146
|
+
hard: false,
|
|
147
|
+
detail: collisions.length === 0
|
|
148
|
+
? "no port collisions among known stacks"
|
|
149
|
+
: collisions.map(([port, stacks]) => `port ${port}: ${stacks.join(", ")}`).join("; "),
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
checks.push({
|
|
153
|
+
name: "state-db",
|
|
154
|
+
ok: rowsResult.ok,
|
|
155
|
+
hard: true,
|
|
156
|
+
detail: rowsResult.ok
|
|
157
|
+
? `${resolveStateDbPath()} (${rowsResult.rows.length} environment(s))`
|
|
158
|
+
: rowsResult.message,
|
|
159
|
+
});
|
|
160
|
+
const gitVersion = yield* exec("git", ["--version"]);
|
|
161
|
+
checks.push({
|
|
162
|
+
name: "git",
|
|
163
|
+
ok: gitVersion.exitCode === 0,
|
|
164
|
+
hard: true,
|
|
165
|
+
detail: gitVersion.exitCode === 0 ? gitVersion.stdout.trim() : "git not found on PATH",
|
|
166
|
+
});
|
|
167
|
+
checks.push({
|
|
168
|
+
name: "wsl",
|
|
169
|
+
ok: true,
|
|
170
|
+
hard: false,
|
|
171
|
+
detail: detectWsl(readProcVersion()) ? WSL_GUIDANCE : "not running under WSL",
|
|
172
|
+
});
|
|
173
|
+
const scopedRows = rows === null
|
|
174
|
+
? null
|
|
175
|
+
: config.ok
|
|
176
|
+
? rows.filter((r) => r.projectId === config.recipe.project.id)
|
|
177
|
+
: rows;
|
|
178
|
+
const pruneCheck = yield* scopedRows === null
|
|
179
|
+
? Effect.succeed({ ok: true, detail: "skipped (state registry unavailable)" })
|
|
180
|
+
: compose.listProjects().pipe(Effect.flatMap((dockerProjects) => buildProbes(scopedRows).pipe(Effect.map((probes) => {
|
|
181
|
+
const candidates = classifyEnvironments({
|
|
182
|
+
rows: scopedRows,
|
|
183
|
+
dockerProjects,
|
|
184
|
+
probes,
|
|
185
|
+
olderThanDays: null,
|
|
186
|
+
allowShared: false,
|
|
187
|
+
now: new Date().toISOString(),
|
|
188
|
+
}).filter((c) => c.reason !== "keep" && c.reason !== "shared-skipped");
|
|
189
|
+
return candidates.length === 0
|
|
190
|
+
? { ok: true, detail: "no prune candidates" }
|
|
191
|
+
: {
|
|
192
|
+
ok: false,
|
|
193
|
+
detail: `${candidates.length} prune candidate(s) — run \`odoo-agentic-dev prune\``,
|
|
194
|
+
};
|
|
195
|
+
}))), Effect.catch(() => Effect.succeed({ ok: true, detail: "skipped (docker unavailable)" })));
|
|
196
|
+
checks.push({ name: "prune-candidates", hard: false, ...pruneCheck });
|
|
197
|
+
return checks;
|
|
198
|
+
});
|
|
199
|
+
export const doctorCommand = Command.make("doctor", {
|
|
200
|
+
json: Flag.boolean("json").pipe(Flag.withDescription("print machine-readable JSON")),
|
|
201
|
+
config: Flag.string("config").pipe(Flag.optional),
|
|
202
|
+
}, (flags) => Effect.gen(function* () {
|
|
203
|
+
const checks = yield* collectDoctorChecks(Option.getOrUndefined(flags.config));
|
|
204
|
+
yield* Console.log(flags.json ? JSON.stringify({ checks }, null, 2) : formatDoctorReport(checks));
|
|
205
|
+
if (hasHardFailure(checks)) {
|
|
206
|
+
process.exitCode = 1;
|
|
207
|
+
}
|
|
208
|
+
}));
|
|
209
|
+
//# sourceMappingURL=doctor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { Command } from "effect/unstable/cli";
|
|
3
|
+
import type { OdooAgenticDevConfig } from "../core/project-recipe.js";
|
|
4
|
+
import type { WorktreeContext } from "../core/worktree-context.js";
|
|
5
|
+
import type { StateStoreApi } from "../platform/state-store.js";
|
|
6
|
+
import type { SharedDatabaseProtectionError, StateError } from "../errors/errors.js";
|
|
7
|
+
export declare const guardDown: (recipe: OdooAgenticDevConfig, ctx: WorktreeContext, flags: {
|
|
8
|
+
volumes: boolean;
|
|
9
|
+
allowShared: boolean;
|
|
10
|
+
}) => Effect.Effect<void, SharedDatabaseProtectionError>;
|
|
11
|
+
export declare const buildDownArgs: (flags: {
|
|
12
|
+
volumes: boolean;
|
|
13
|
+
}) => Array<string>;
|
|
14
|
+
/** After a successful teardown: `--volumes` forgets the environment, plain down only touches it. */
|
|
15
|
+
export declare const finalizeDownState: (ctx: WorktreeContext, flags: {
|
|
16
|
+
volumes: boolean;
|
|
17
|
+
}) => Effect.Effect<void, StateError, StateStoreApi>;
|
|
18
|
+
export declare const downCommand: Command.Command<"down", {
|
|
19
|
+
readonly volumes: boolean;
|
|
20
|
+
readonly allowShared: boolean;
|
|
21
|
+
readonly json: boolean;
|
|
22
|
+
readonly config: import("effect/Option").Option<string>;
|
|
23
|
+
}, {}, import("../errors/errors.js").RuntimeError, import("../platform/git.js").GitApi | import("../platform/docker-compose.js").DockerComposeApi | StateStoreApi>;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { Command, Flag } from "effect/unstable/cli";
|
|
3
|
+
import { assertSharedDatabaseAllowed } from "../core/safety.js";
|
|
4
|
+
import { DockerCompose } from "../platform/docker-compose.js";
|
|
5
|
+
import { StateStore } from "../platform/state-store.js";
|
|
6
|
+
import { resolveContext } from "./resolve-context.js";
|
|
7
|
+
import { withJsonReport } from "./json-report.js";
|
|
8
|
+
export const guardDown = (recipe, ctx, flags) => flags.volumes
|
|
9
|
+
? assertSharedDatabaseAllowed({
|
|
10
|
+
databaseName: ctx.databaseName,
|
|
11
|
+
sharedDatabase: recipe.project.sharedDatabase,
|
|
12
|
+
allowShared: flags.allowShared,
|
|
13
|
+
action: "down --volumes",
|
|
14
|
+
})
|
|
15
|
+
: Effect.void;
|
|
16
|
+
export const buildDownArgs = (flags) => [
|
|
17
|
+
"down",
|
|
18
|
+
...(flags.volumes ? ["--volumes"] : []),
|
|
19
|
+
];
|
|
20
|
+
/** After a successful teardown: `--volumes` forgets the environment, plain down only touches it. */
|
|
21
|
+
export const finalizeDownState = (ctx, flags) => Effect.gen(function* () {
|
|
22
|
+
const store = yield* StateStore;
|
|
23
|
+
yield* flags.volumes
|
|
24
|
+
? store.remove(ctx.composeProjectName)
|
|
25
|
+
: store.touch(ctx.composeProjectName);
|
|
26
|
+
});
|
|
27
|
+
export const downCommand = Command.make("down", {
|
|
28
|
+
volumes: Flag.boolean("volumes").pipe(Flag.withDescription("also remove this worktree's volumes")),
|
|
29
|
+
allowShared: Flag.boolean("allow-shared"),
|
|
30
|
+
json: Flag.boolean("json").pipe(Flag.withDescription("suppress decorative output; print one final JSON report line")),
|
|
31
|
+
config: Flag.string("config").pipe(Flag.optional),
|
|
32
|
+
}, (flags) => withJsonReport("down", flags.json, (report) => Effect.gen(function* () {
|
|
33
|
+
const { ctx, recipe } = yield* resolveContext(flags.config);
|
|
34
|
+
yield* report.setContext(ctx);
|
|
35
|
+
yield* guardDown(recipe, ctx, flags);
|
|
36
|
+
const compose = yield* DockerCompose;
|
|
37
|
+
yield* compose.ensureAvailable();
|
|
38
|
+
const ref = yield* compose.prepareComposeFile(recipe, ctx);
|
|
39
|
+
yield* report.say(`Stopping compose project: ${ctx.composeProjectName} (database: ${ctx.databaseName})`);
|
|
40
|
+
yield* compose.stream(ref, buildDownArgs(flags));
|
|
41
|
+
yield* report.action("compose-down");
|
|
42
|
+
if (flags.volumes)
|
|
43
|
+
yield* report.action("remove-volumes");
|
|
44
|
+
yield* finalizeDownState(ctx, flags);
|
|
45
|
+
})));
|
|
46
|
+
//# sourceMappingURL=down.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"down.js","sourceRoot":"","sources":["../../src/commands/down.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Command } from "effect/unstable/cli";
|
|
2
|
+
import type { WorktreeContext } from "../core/worktree-context.js";
|
|
3
|
+
export declare const buildInfoText: (ctx: WorktreeContext) => string;
|
|
4
|
+
export declare const buildInfoJson: (ctx: WorktreeContext) => string;
|
|
5
|
+
export declare const buildInfoEnv: (ctx: WorktreeContext) => string;
|
|
6
|
+
export declare const infoCommand: Command.Command<"info", {
|
|
7
|
+
readonly json: boolean;
|
|
8
|
+
readonly env: boolean;
|
|
9
|
+
readonly config: import("effect/Option").Option<string>;
|
|
10
|
+
}, {}, import("../errors/errors.js").RuntimeError, import("../platform/git.js").GitApi>;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Console, Effect } from "effect";
|
|
2
|
+
import { Command, Flag } from "effect/unstable/cli";
|
|
3
|
+
import { resolveContext } from "./resolve-context.js";
|
|
4
|
+
export const buildInfoText = (ctx) => {
|
|
5
|
+
const out = [
|
|
6
|
+
`Worktree: ${ctx.worktreeName}`,
|
|
7
|
+
`Database: ${ctx.databaseName}`,
|
|
8
|
+
`Compose project: ${ctx.composeProjectName}`,
|
|
9
|
+
`Odoo URL: ${ctx.odooBaseUrl}/web?db=${ctx.databaseName}`,
|
|
10
|
+
];
|
|
11
|
+
for (const [name, port] of ctx.companionPorts)
|
|
12
|
+
out.push(`${name} URL: http://localhost:${port}`);
|
|
13
|
+
return out.join("\n");
|
|
14
|
+
};
|
|
15
|
+
export const buildInfoJson = (ctx) => JSON.stringify({
|
|
16
|
+
rootDir: ctx.rootDir,
|
|
17
|
+
worktreeName: ctx.worktreeName,
|
|
18
|
+
databaseName: ctx.databaseName,
|
|
19
|
+
composeProjectName: ctx.composeProjectName,
|
|
20
|
+
odooHttpPort: ctx.odooHttpPort,
|
|
21
|
+
odooBaseUrl: ctx.odooBaseUrl,
|
|
22
|
+
odooUrl: `${ctx.odooBaseUrl}/web?db=${ctx.databaseName}`,
|
|
23
|
+
companions: Object.fromEntries(ctx.companionPorts),
|
|
24
|
+
env: ctx.env,
|
|
25
|
+
}, null, 2);
|
|
26
|
+
export const buildInfoEnv = (ctx) => Object.entries(ctx.env)
|
|
27
|
+
.map(([key, value]) => `${key}=${value}`)
|
|
28
|
+
.join("\n");
|
|
29
|
+
export const infoCommand = Command.make("info", {
|
|
30
|
+
json: Flag.boolean("json").pipe(Flag.withDescription("print machine-readable JSON")),
|
|
31
|
+
env: Flag.boolean("env").pipe(Flag.withDescription("print KEY=value env lines")),
|
|
32
|
+
config: Flag.string("config").pipe(Flag.optional, Flag.withDescription("explicit config file path")),
|
|
33
|
+
}, (flags) => Effect.gen(function* () {
|
|
34
|
+
const { ctx } = yield* resolveContext(flags.config);
|
|
35
|
+
const output = flags.json
|
|
36
|
+
? buildInfoJson(ctx)
|
|
37
|
+
: flags.env
|
|
38
|
+
? buildInfoEnv(ctx)
|
|
39
|
+
: buildInfoText(ctx);
|
|
40
|
+
yield* Console.log(output);
|
|
41
|
+
}));
|
|
42
|
+
//# sourceMappingURL=info.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"info.js","sourceRoot":"","sources":["../../src/commands/info.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import type { ResetPath } from "../core/environment.js";
|
|
3
|
+
import type { WorktreeContext } from "../core/worktree-context.js";
|
|
4
|
+
/**
|
|
5
|
+
* Semantic action names for the reset paths. The nightly real-Odoo workflow
|
|
6
|
+
* asserts these exact strings, so they are part of the CLI's contract.
|
|
7
|
+
*/
|
|
8
|
+
export declare const resetPathActions: (path: ResetPath) => ReadonlyArray<string>;
|
|
9
|
+
/** The single object a lifecycle command prints to stdout under `--json`. */
|
|
10
|
+
export type JsonReport = {
|
|
11
|
+
readonly ok: boolean;
|
|
12
|
+
readonly command: string;
|
|
13
|
+
/** null when the command failed before the context resolved */
|
|
14
|
+
readonly database: string | null;
|
|
15
|
+
readonly composeProject: string | null;
|
|
16
|
+
readonly odooUrl: string | null;
|
|
17
|
+
readonly actions: ReadonlyArray<string>;
|
|
18
|
+
readonly durationMs: number;
|
|
19
|
+
/** child exit code, when the command ran one to completion (`test`) */
|
|
20
|
+
readonly exitCode?: number;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Per-invocation reporter threaded through a lifecycle command. In text mode
|
|
24
|
+
* `say` prints and everything else is bookkeeping; in json mode `say` is
|
|
25
|
+
* recorded into `actions` instead of printed so stdout stays parseable.
|
|
26
|
+
*/
|
|
27
|
+
export interface CommandReporter {
|
|
28
|
+
readonly json: boolean;
|
|
29
|
+
/** decorative line: printed in text mode, recorded as an action in json mode */
|
|
30
|
+
readonly say: (line: string) => Effect.Effect<void>;
|
|
31
|
+
/** semantic action marker: always recorded, never printed */
|
|
32
|
+
readonly action: (name: string) => Effect.Effect<void>;
|
|
33
|
+
/** identity fields for the final report, set as soon as the context resolves */
|
|
34
|
+
readonly setContext: (ctx: WorktreeContext) => Effect.Effect<void>;
|
|
35
|
+
/** child exit code; a non-zero code makes the final report not-ok */
|
|
36
|
+
readonly setExitCode: (code: number) => Effect.Effect<void>;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Wrap a lifecycle command body with the `--json` reporting contract: collect
|
|
40
|
+
* actions while the body runs, then print ONE single-line JSON object to
|
|
41
|
+
* stdout — on success and on typed failure alike (the error still propagates,
|
|
42
|
+
* so it renders on stderr and drives the exit code). Streamed child output may
|
|
43
|
+
* precede it; consumers parse the LAST stdout line (`tail -n 1`).
|
|
44
|
+
*/
|
|
45
|
+
export declare const withJsonReport: <A, E, R>(command: string, json: boolean, body: (report: CommandReporter) => Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { Console, Effect } from "effect";
|
|
2
|
+
/**
|
|
3
|
+
* Semantic action names for the reset paths. The nightly real-Odoo workflow
|
|
4
|
+
* asserts these exact strings, so they are part of the CLI's contract.
|
|
5
|
+
*/
|
|
6
|
+
export const resetPathActions = (path) => path === "restore"
|
|
7
|
+
? ["restore-from-template"]
|
|
8
|
+
: path === "full"
|
|
9
|
+
? ["full-init"]
|
|
10
|
+
: ["full-init", "snapshot-template"];
|
|
11
|
+
/**
|
|
12
|
+
* Wrap a lifecycle command body with the `--json` reporting contract: collect
|
|
13
|
+
* actions while the body runs, then print ONE single-line JSON object to
|
|
14
|
+
* stdout — on success and on typed failure alike (the error still propagates,
|
|
15
|
+
* so it renders on stderr and drives the exit code). Streamed child output may
|
|
16
|
+
* precede it; consumers parse the LAST stdout line (`tail -n 1`).
|
|
17
|
+
*/
|
|
18
|
+
export const withJsonReport = (command, json, body) => Effect.gen(function* () {
|
|
19
|
+
const startedAt = Date.now();
|
|
20
|
+
const actions = [];
|
|
21
|
+
let ctx = null;
|
|
22
|
+
let exitCode;
|
|
23
|
+
const record = (entry) => Effect.sync(() => {
|
|
24
|
+
actions.push(entry);
|
|
25
|
+
});
|
|
26
|
+
const reporter = {
|
|
27
|
+
json,
|
|
28
|
+
say: (line) => (json ? record(line) : Console.log(line)),
|
|
29
|
+
action: record,
|
|
30
|
+
setContext: (value) => Effect.sync(() => {
|
|
31
|
+
ctx = value;
|
|
32
|
+
}),
|
|
33
|
+
setExitCode: (code) => Effect.sync(() => {
|
|
34
|
+
exitCode = code;
|
|
35
|
+
}),
|
|
36
|
+
};
|
|
37
|
+
const emit = (succeeded) => {
|
|
38
|
+
if (!json)
|
|
39
|
+
return Effect.void;
|
|
40
|
+
const resolved = ctx;
|
|
41
|
+
const report = {
|
|
42
|
+
ok: succeeded && (exitCode ?? 0) === 0,
|
|
43
|
+
command,
|
|
44
|
+
database: resolved === null ? null : resolved.databaseName,
|
|
45
|
+
composeProject: resolved === null ? null : resolved.composeProjectName,
|
|
46
|
+
odooUrl: resolved === null ? null : `${resolved.odooBaseUrl}/web?db=${resolved.databaseName}`,
|
|
47
|
+
actions,
|
|
48
|
+
durationMs: Date.now() - startedAt,
|
|
49
|
+
...(exitCode === undefined ? {} : { exitCode }),
|
|
50
|
+
};
|
|
51
|
+
return Console.log(JSON.stringify(report));
|
|
52
|
+
};
|
|
53
|
+
return yield* body(reporter).pipe(Effect.tap(() => emit(true)), Effect.tapError(() => emit(false)));
|
|
54
|
+
});
|
|
55
|
+
//# sourceMappingURL=json-report.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json-report.js","sourceRoot":"","sources":["../../src/commands/json-report.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Effect, Option } from "effect";
|
|
2
|
+
import { Command } from "effect/unstable/cli";
|
|
3
|
+
import { SourceResolverError } from "../errors/errors.js";
|
|
4
|
+
import type { CommandRunnerApi } from "../platform/command-runner.js";
|
|
5
|
+
/** The legacy script's test: an Odoo source checkout has odoo-bin, odoo/ and addons/. */
|
|
6
|
+
export declare const looksLikeOdooCheckout: (path: string) => boolean;
|
|
7
|
+
/** Extract the worktree paths from `git worktree list --porcelain` output. */
|
|
8
|
+
export declare const parseWorktreePorcelain: (stdout: string) => Array<string>;
|
|
9
|
+
/**
|
|
10
|
+
* Candidate discovery when nothing is configured: `<root>/../odoo` first,
|
|
11
|
+
* then `<dirname(worktree)>/odoo` for every git worktree of the project (a
|
|
12
|
+
* checkout cloned next to any worktree is found from all of them). The first
|
|
13
|
+
* candidate that LOOKS like an Odoo checkout wins; the full candidate list is
|
|
14
|
+
* returned for the error message. Git failure (not a repo) is not fatal —
|
|
15
|
+
* only the sibling candidate is checked then.
|
|
16
|
+
*/
|
|
17
|
+
export declare const discoverOdooCheckout: (rootDir: string) => Effect.Effect<{
|
|
18
|
+
readonly resolved: string | undefined;
|
|
19
|
+
readonly candidates: Array<string>;
|
|
20
|
+
}, never, CommandRunnerApi>;
|
|
21
|
+
export declare const performLinkSource: (options: {
|
|
22
|
+
readonly rootDir: string;
|
|
23
|
+
readonly target: string | undefined;
|
|
24
|
+
readonly name: string;
|
|
25
|
+
readonly force: boolean;
|
|
26
|
+
readonly recipeSource: string | null;
|
|
27
|
+
}) => Effect.Effect<string, SourceResolverError, CommandRunnerApi>;
|
|
28
|
+
export declare const linkSourceCommand: Command.Command<"link-source", {
|
|
29
|
+
readonly target: Option.Option<string>;
|
|
30
|
+
readonly name: string;
|
|
31
|
+
readonly force: boolean;
|
|
32
|
+
readonly config: Option.Option<string>;
|
|
33
|
+
}, {}, import("../errors/errors.js").RuntimeError, CommandRunnerApi | import("../platform/git.js").GitApi>;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { existsSync, lstatSync, statSync, symlinkSync, unlinkSync } from "node:fs";
|
|
2
|
+
import { dirname, isAbsolute, join, resolve } from "node:path";
|
|
3
|
+
import { Console, Effect, Option } from "effect";
|
|
4
|
+
import { Command, Flag } from "effect/unstable/cli";
|
|
5
|
+
import { SourceResolverError } from "../errors/errors.js";
|
|
6
|
+
import { CommandRunner } from "../platform/command-runner.js";
|
|
7
|
+
import { resolveContext } from "./resolve-context.js";
|
|
8
|
+
const tryFs = (thunk) => Effect.try({ try: thunk, catch: (cause) => new SourceResolverError({ reason: String(cause) }) });
|
|
9
|
+
const isDirectory = (path) => {
|
|
10
|
+
try {
|
|
11
|
+
return statSync(path).isDirectory();
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
/** The legacy script's test: an Odoo source checkout has odoo-bin, odoo/ and addons/. */
|
|
18
|
+
export const looksLikeOdooCheckout = (path) => existsSync(join(path, "odoo-bin")) &&
|
|
19
|
+
isDirectory(join(path, "odoo")) &&
|
|
20
|
+
isDirectory(join(path, "addons"));
|
|
21
|
+
/** Extract the worktree paths from `git worktree list --porcelain` output. */
|
|
22
|
+
export const parseWorktreePorcelain = (stdout) => {
|
|
23
|
+
const paths = [];
|
|
24
|
+
for (const line of stdout.split(/\r?\n/)) {
|
|
25
|
+
if (line.startsWith("worktree "))
|
|
26
|
+
paths.push(line.slice("worktree ".length).trim());
|
|
27
|
+
}
|
|
28
|
+
return paths;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Candidate discovery when nothing is configured: `<root>/../odoo` first,
|
|
32
|
+
* then `<dirname(worktree)>/odoo` for every git worktree of the project (a
|
|
33
|
+
* checkout cloned next to any worktree is found from all of them). The first
|
|
34
|
+
* candidate that LOOKS like an Odoo checkout wins; the full candidate list is
|
|
35
|
+
* returned for the error message. Git failure (not a repo) is not fatal —
|
|
36
|
+
* only the sibling candidate is checked then.
|
|
37
|
+
*/
|
|
38
|
+
export const discoverOdooCheckout = (rootDir) => Effect.gen(function* () {
|
|
39
|
+
const runner = yield* CommandRunner;
|
|
40
|
+
const result = yield* runner
|
|
41
|
+
.run({ command: "git", args: ["worktree", "list", "--porcelain"], cwd: rootDir })
|
|
42
|
+
.pipe(Effect.catch(() => Effect.succeed({ exitCode: -1, stdout: "", stderr: "" })));
|
|
43
|
+
const worktrees = result.exitCode === 0 ? parseWorktreePorcelain(result.stdout) : [];
|
|
44
|
+
const candidates = [];
|
|
45
|
+
for (const candidate of [
|
|
46
|
+
resolve(rootDir, "..", "odoo"),
|
|
47
|
+
...worktrees.map((worktree) => join(dirname(worktree), "odoo")),
|
|
48
|
+
]) {
|
|
49
|
+
if (!candidates.includes(candidate))
|
|
50
|
+
candidates.push(candidate);
|
|
51
|
+
}
|
|
52
|
+
return { resolved: candidates.find(looksLikeOdooCheckout), candidates };
|
|
53
|
+
});
|
|
54
|
+
export const performLinkSource = (options) => Effect.gen(function* () {
|
|
55
|
+
// explicit wins and skips validation: --target, then odoo.source
|
|
56
|
+
const configured = options.target ??
|
|
57
|
+
(options.recipeSource === "docker-only" ? undefined : (options.recipeSource ?? undefined));
|
|
58
|
+
let resolved;
|
|
59
|
+
if (configured !== undefined) {
|
|
60
|
+
resolved = isAbsolute(configured) ? configured : resolve(options.rootDir, configured);
|
|
61
|
+
if (!existsSync(resolved)) {
|
|
62
|
+
return yield* Effect.fail(new SourceResolverError({ reason: `resolved source path does not exist: ${resolved}` }));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
const discovery = yield* discoverOdooCheckout(options.rootDir);
|
|
67
|
+
if (discovery.resolved === undefined) {
|
|
68
|
+
return yield* Effect.fail(new SourceResolverError({
|
|
69
|
+
reason: "no --target given and no odoo.source configured; no Odoo checkout found at: " +
|
|
70
|
+
discovery.candidates.join(", ") +
|
|
71
|
+
" (a valid checkout contains odoo-bin, odoo/ and addons/)",
|
|
72
|
+
}));
|
|
73
|
+
}
|
|
74
|
+
resolved = discovery.resolved;
|
|
75
|
+
}
|
|
76
|
+
const linkPath = join(options.rootDir, options.name);
|
|
77
|
+
let existing;
|
|
78
|
+
try {
|
|
79
|
+
existing = lstatSync(linkPath);
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
existing = undefined;
|
|
83
|
+
}
|
|
84
|
+
if (existing !== undefined) {
|
|
85
|
+
if (!existing.isSymbolicLink()) {
|
|
86
|
+
return yield* Effect.fail(new SourceResolverError({
|
|
87
|
+
reason: `${linkPath} exists and is not a symlink; refusing to overwrite`,
|
|
88
|
+
}));
|
|
89
|
+
}
|
|
90
|
+
if (!options.force) {
|
|
91
|
+
return yield* Effect.fail(new SourceResolverError({
|
|
92
|
+
reason: `${linkPath} already exists; pass --force to replace it`,
|
|
93
|
+
}));
|
|
94
|
+
}
|
|
95
|
+
yield* tryFs(() => unlinkSync(linkPath));
|
|
96
|
+
}
|
|
97
|
+
yield* tryFs(() => symlinkSync(resolved, linkPath));
|
|
98
|
+
return linkPath;
|
|
99
|
+
});
|
|
100
|
+
export const linkSourceCommand = Command.make("link-source", {
|
|
101
|
+
target: Flag.string("target").pipe(Flag.optional),
|
|
102
|
+
name: Flag.string("name").pipe(Flag.withDefault(".odoo")),
|
|
103
|
+
force: Flag.boolean("force"),
|
|
104
|
+
config: Flag.string("config").pipe(Flag.optional),
|
|
105
|
+
}, (flags) => Effect.gen(function* () {
|
|
106
|
+
const { ctx, recipe } = yield* resolveContext(flags.config);
|
|
107
|
+
const linkPath = yield* performLinkSource({
|
|
108
|
+
rootDir: ctx.rootDir,
|
|
109
|
+
target: Option.getOrUndefined(flags.target),
|
|
110
|
+
name: flags.name,
|
|
111
|
+
force: flags.force,
|
|
112
|
+
recipeSource: recipe.odoo.source,
|
|
113
|
+
});
|
|
114
|
+
yield* Console.log(`Linked ${linkPath} -> resolved Odoo source`);
|
|
115
|
+
}));
|
|
116
|
+
//# sourceMappingURL=link-source.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"link-source.js","sourceRoot":"","sources":["../../src/commands/link-source.ts"],"names":[],"mappings":"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"}
|