@treeseed/cli 0.6.45 → 0.6.47
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.
|
@@ -44,9 +44,9 @@ function formatSavePlanSections(repositoryPlan) {
|
|
|
44
44
|
}
|
|
45
45
|
const handleSave = async (invocation, context) => {
|
|
46
46
|
try {
|
|
47
|
+
const progressWrite = context.outputFormat === "json" ? ((output) => context.write(output, "stderr")) : context.write;
|
|
47
48
|
const result = await createWorkflowSdk(context, {
|
|
48
|
-
write:
|
|
49
|
-
}) : context.write
|
|
49
|
+
write: progressWrite
|
|
50
50
|
}).save({
|
|
51
51
|
message: invocation.positionals.join(" ").trim(),
|
|
52
52
|
hotfix: invocation.args.hotfix === true,
|
|
@@ -3,6 +3,9 @@ import {
|
|
|
3
3
|
resolveTreeseedLaunchEnvironment,
|
|
4
4
|
resolveTreeseedToolCommand
|
|
5
5
|
} from "@treeseed/sdk/workflow-support";
|
|
6
|
+
import { existsSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
|
|
7
|
+
import { tmpdir } from "node:os";
|
|
8
|
+
import { join } from "node:path";
|
|
6
9
|
import { workflowErrorResult } from "./workflow.js";
|
|
7
10
|
const WRAPPED_TOOLS = /* @__PURE__ */ new Set(["gh", "railway", "wrangler"]);
|
|
8
11
|
const ENVIRONMENT_SCOPES = /* @__PURE__ */ new Set(["local", "staging", "prod"]);
|
|
@@ -21,7 +24,84 @@ function wrapperScope(value) {
|
|
|
21
24
|
function railwayEnvironmentName(scope) {
|
|
22
25
|
return scope === "prod" ? "production" : scope;
|
|
23
26
|
}
|
|
27
|
+
function findRailwayProjectId(value) {
|
|
28
|
+
if (!value || typeof value !== "object") {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
if (Array.isArray(value)) {
|
|
32
|
+
for (const entry of value) {
|
|
33
|
+
const found = findRailwayProjectId(entry);
|
|
34
|
+
if (found) {
|
|
35
|
+
return found;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
const record = value;
|
|
41
|
+
if (typeof record.projectId === "string" && /^[0-9a-f]{8}-[0-9a-f-]{27,}$/iu.test(record.projectId.trim())) {
|
|
42
|
+
return record.projectId.trim();
|
|
43
|
+
}
|
|
44
|
+
if (typeof record.lastDeploymentCommand === "string") {
|
|
45
|
+
const match = record.lastDeploymentCommand.match(/--project\s+([0-9a-f]{8}-[0-9a-f-]{27,})/iu);
|
|
46
|
+
if (match?.[1]) {
|
|
47
|
+
return match[1];
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
for (const entry of Object.values(record)) {
|
|
51
|
+
const found = findRailwayProjectId(entry);
|
|
52
|
+
if (found) {
|
|
53
|
+
return found;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
function findRailwayProjectIdFromCommand(value) {
|
|
59
|
+
if (!value || typeof value !== "object") {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
if (Array.isArray(value)) {
|
|
63
|
+
for (const entry of value) {
|
|
64
|
+
const found = findRailwayProjectIdFromCommand(entry);
|
|
65
|
+
if (found) {
|
|
66
|
+
return found;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
const record = value;
|
|
72
|
+
if (typeof record.lastDeploymentCommand === "string") {
|
|
73
|
+
const match = record.lastDeploymentCommand.match(/--project\s+([0-9a-f]{8}-[0-9a-f-]{27,})/iu);
|
|
74
|
+
if (match?.[1]) {
|
|
75
|
+
return match[1];
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
for (const entry of Object.values(record)) {
|
|
79
|
+
const found = findRailwayProjectIdFromCommand(entry);
|
|
80
|
+
if (found) {
|
|
81
|
+
return found;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
function railwayProjectIdFromDeployState(cwd, scope) {
|
|
87
|
+
const stateScope = scope === "prod" ? "prod" : scope;
|
|
88
|
+
const statePath = join(cwd, ".treeseed", "state", "environments", stateScope, "deploy.json");
|
|
89
|
+
if (!existsSync(statePath)) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
try {
|
|
93
|
+
const state = JSON.parse(readFileSync(statePath, "utf8"));
|
|
94
|
+
return findRailwayProjectIdFromCommand(state) ?? findRailwayProjectId(state);
|
|
95
|
+
} catch {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
function railwayCommandUsesProjectFiles(args) {
|
|
100
|
+
const command = args[0] ?? "";
|
|
101
|
+
return ["up", "dev", "develop", "run", "local", "shell"].includes(command);
|
|
102
|
+
}
|
|
24
103
|
const handleToolWrapper = (invocation, context) => {
|
|
104
|
+
let isolatedRailwayCwd = null;
|
|
25
105
|
try {
|
|
26
106
|
const toolName = wrappedToolName(invocation.commandName);
|
|
27
107
|
const scope = wrapperScope(invocation.args.environment);
|
|
@@ -53,40 +133,91 @@ const handleToolWrapper = (invocation, context) => {
|
|
|
53
133
|
};
|
|
54
134
|
}
|
|
55
135
|
const targetArgs = invocation.positionals;
|
|
56
|
-
if (toolName === "railway" && scope !== "local") {
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
136
|
+
if (toolName === "railway" && scope !== "local" && targetArgs[0] !== "link") {
|
|
137
|
+
const environmentName = railwayEnvironmentName(scope);
|
|
138
|
+
const projectId = managedEnv.TREESEED_RAILWAY_PROJECT_ID || railwayProjectIdFromDeployState(context.cwd, scope);
|
|
139
|
+
const railwayCwd = railwayCommandUsesProjectFiles(targetArgs) ? context.cwd : isolatedRailwayCwd = mkdtempSync(join(tmpdir(), `treeseed-railway-${scope}-`));
|
|
140
|
+
const railwayEnv = isolatedRailwayCwd ? {
|
|
141
|
+
...managedEnv,
|
|
142
|
+
HOME: isolatedRailwayCwd,
|
|
143
|
+
XDG_CONFIG_HOME: join(isolatedRailwayCwd, ".config")
|
|
144
|
+
} : managedEnv;
|
|
145
|
+
if (projectId) {
|
|
146
|
+
const linkResult = context.spawn(resolved.command, [
|
|
147
|
+
...resolved.argsPrefix,
|
|
148
|
+
"link",
|
|
149
|
+
"--project",
|
|
150
|
+
projectId,
|
|
151
|
+
"--environment",
|
|
152
|
+
environmentName,
|
|
153
|
+
"--json"
|
|
154
|
+
], {
|
|
155
|
+
cwd: railwayCwd,
|
|
156
|
+
env: railwayEnv,
|
|
157
|
+
stdio: "pipe"
|
|
158
|
+
});
|
|
159
|
+
if ((linkResult.status ?? 1) !== 0) {
|
|
160
|
+
return {
|
|
161
|
+
exitCode: linkResult.status ?? 1,
|
|
162
|
+
stderr: [`Failed to link Railway project ${projectId} for ${environmentName} before running ${targetArgs.join(" ") || "railway"}.`],
|
|
163
|
+
report: {
|
|
164
|
+
command: toolName,
|
|
165
|
+
ok: false,
|
|
166
|
+
scope,
|
|
167
|
+
executable: resolved.command,
|
|
168
|
+
binaryPath: resolved.binaryPath,
|
|
169
|
+
argsPrefix: resolved.argsPrefix,
|
|
170
|
+
args: targetArgs,
|
|
171
|
+
projectLink: {
|
|
172
|
+
projectId,
|
|
173
|
+
environment: environmentName,
|
|
174
|
+
status: linkResult.status ?? 1
|
|
175
|
+
}
|
|
82
176
|
}
|
|
83
|
-
}
|
|
84
|
-
}
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
if (!(isolatedRailwayCwd && projectId)) {
|
|
181
|
+
const environmentResult = context.spawn(resolved.command, [
|
|
182
|
+
...resolved.argsPrefix,
|
|
183
|
+
"environment",
|
|
184
|
+
environmentName,
|
|
185
|
+
"--json"
|
|
186
|
+
], {
|
|
187
|
+
cwd: railwayCwd,
|
|
188
|
+
env: railwayEnv,
|
|
189
|
+
stdio: "pipe"
|
|
190
|
+
});
|
|
191
|
+
if ((environmentResult.status ?? 1) !== 0) {
|
|
192
|
+
return {
|
|
193
|
+
exitCode: environmentResult.status ?? 1,
|
|
194
|
+
stderr: [`Failed to select Railway environment ${railwayEnvironmentName(scope)} before running ${targetArgs.join(" ") || "railway"}.`],
|
|
195
|
+
report: {
|
|
196
|
+
command: toolName,
|
|
197
|
+
ok: false,
|
|
198
|
+
scope,
|
|
199
|
+
executable: resolved.command,
|
|
200
|
+
binaryPath: resolved.binaryPath,
|
|
201
|
+
argsPrefix: resolved.argsPrefix,
|
|
202
|
+
args: targetArgs,
|
|
203
|
+
environmentSelection: {
|
|
204
|
+
environment: environmentName,
|
|
205
|
+
status: environmentResult.status ?? 1
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
}
|
|
85
210
|
}
|
|
86
211
|
}
|
|
212
|
+
const railwayTargetCwd = toolName === "railway" && isolatedRailwayCwd ? isolatedRailwayCwd : context.cwd;
|
|
213
|
+
const targetEnv = toolName === "railway" && isolatedRailwayCwd ? {
|
|
214
|
+
...managedEnv,
|
|
215
|
+
HOME: isolatedRailwayCwd,
|
|
216
|
+
XDG_CONFIG_HOME: join(isolatedRailwayCwd, ".config")
|
|
217
|
+
} : managedEnv;
|
|
87
218
|
const result = context.spawn(resolved.command, [...resolved.argsPrefix, ...targetArgs], {
|
|
88
|
-
cwd:
|
|
89
|
-
env:
|
|
219
|
+
cwd: railwayTargetCwd,
|
|
220
|
+
env: targetEnv,
|
|
90
221
|
stdio: "inherit"
|
|
91
222
|
});
|
|
92
223
|
return {
|
|
@@ -104,6 +235,13 @@ const handleToolWrapper = (invocation, context) => {
|
|
|
104
235
|
};
|
|
105
236
|
} catch (error) {
|
|
106
237
|
return workflowErrorResult(error);
|
|
238
|
+
} finally {
|
|
239
|
+
if (isolatedRailwayCwd) {
|
|
240
|
+
try {
|
|
241
|
+
rmSync(isolatedRailwayCwd, { recursive: true, force: true });
|
|
242
|
+
} catch {
|
|
243
|
+
}
|
|
244
|
+
}
|
|
107
245
|
}
|
|
108
246
|
};
|
|
109
247
|
export {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@treeseed/cli",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.47",
|
|
4
4
|
"description": "Operator-facing Treeseed CLI package.",
|
|
5
5
|
"license": "AGPL-3.0-only",
|
|
6
6
|
"repository": {
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"release:publish": "node ./scripts/run-ts.mjs ./scripts/publish-package.ts"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@treeseed/sdk": "0.6.
|
|
48
|
+
"@treeseed/sdk": "0.6.51",
|
|
49
49
|
"ink": "^7.0.0",
|
|
50
50
|
"react": "^19.2.5"
|
|
51
51
|
},
|