@tdk-landscape/tdk-cli-core 1.3.75 → 1.3.77
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/CHANGELOG.md +6 -0
- package/README.md +59 -13
- package/cli/bin/tdk.js +22 -2
- package/cli/dist/cli.js +9 -2
- package/cli/dist/commands/completion.js +30 -3
- package/cli/dist/commands/config.js +59 -1
- package/cli/dist/commands/doctor.js +295 -98
- package/cli/dist/commands/eject.js +77 -0
- package/cli/dist/commands/networks.js +17 -4
- package/cli/dist/commands/project.js +28 -5
- package/cli/dist/commands/resource.js +118 -10
- package/cli/dist/commands/up.js +93 -21
- package/cli/dist/commands/upgrade.js +163 -68
- package/cli/dist/generator/template-engine.js +77 -13
- package/cli/dist/types/index.js +6 -1
- package/cli/dist/utils/cold-preflight.js +198 -0
- package/cli/dist/utils/constants.js +4 -0
- package/cli/dist/utils/docker.js +15 -1
- package/cli/dist/utils/doctor-runtime.js +48 -16
- package/cli/dist/utils/doctor-wiring.js +269 -0
- package/cli/dist/utils/errors.js +5 -5
- package/cli/dist/utils/exec-async.js +30 -0
- package/cli/dist/utils/generated-paths.js +49 -0
- package/cli/dist/utils/platform.js +53 -0
- package/cli/dist/utils/port-assignment.js +13 -20
- package/cli/dist/utils/project-templates.js +7 -2
- package/cli/dist/utils/resource-features.js +6 -2
- package/cli/dist/utils/service-manifest.js +73 -0
- package/cli/dist/utils/services.js +8 -1
- package/cli/dist/utils/tar.js +27 -5
- package/cli/dist/utils/tilt-process.js +53 -0
- package/cli/dist/utils/tilt.js +3 -1
- package/cli/dist/utils/which.js +26 -0
- package/cli/dist/utils/windows-doctor.js +175 -0
- package/cli/package.json +1 -1
- package/cli/templates/TILT_RESOURCE_DEFAULTS.star.hbs +1 -2
- package/cli/templates/TILT_TECH_STACK.star.hbs +0 -1
- package/cli/templates/Tiltfile.hbs +24 -4
- package/cli/templates/spec.master.hbs +1 -2
- package/discovery/AGENTS.md +1 -1
- package/discovery/constants.star +2 -2
- package/discovery/discovery_orchestrator.star +2 -2
- package/discovery/manifest/constants.star +1 -1
- package/discovery/manifest/loading.star +7 -3
- package/discovery/registry.star +17 -3
- package/engine/README.md +2 -0
- package/engine/schemas/service-schema.json +47 -21
- package/engine/scripts/generate-manifest.sh +2 -1
- package/engine/topologies/platform/docker/compose/compose.star +37 -5
- package/engine/topologies/tilt/common/utils.star +54 -11
- package/engine/topologies/tilt/manifest/AGENTS.md +1 -1
- package/engine/topologies/tilt/manifest/constants.star +3 -0
- package/engine/topologies/tilt/manifest/schema.star +2 -4
- package/engine/topologies/tilt/manifest/validator.star +1 -1
- package/engine/topologies/tilt/resources/infra-loader.star +14 -3
- package/engine/topologies/tilt/resources/orchestrator/apply_compose_resource_registration.star +15 -11
- package/engine/topologies/tilt/resources/orchestrator/generators/manifest_resource.star +15 -6
- package/examples/tdk-example/LICENSE +21 -0
- package/examples/tdk-example/README.md +23 -0
- package/examples/tdk-example/TILT_SERVICE_DEFAULTS.star +42 -0
- package/examples/tdk-example/TILT_TECH_STACK.star +67 -0
- package/examples/tdk-example/Tiltfile +25 -0
- package/examples/tdk-example/services/platform/messaging/docker-compose.yml +26 -0
- package/examples/tdk-example/services/shop/orders-api/package.json +20 -0
- package/examples/tdk-example/services/shop/orders-api/service.json +14 -0
- package/examples/tdk-example/services/shop/orders-api/src/index.ts +81 -0
- package/examples/tdk-example/services/shop/orders-app/index.html +12 -0
- package/examples/tdk-example/services/shop/orders-app/package.json +20 -0
- package/examples/tdk-example/services/shop/orders-app/public/.gitkeep +0 -0
- package/examples/tdk-example/services/shop/orders-app/service.json +9 -0
- package/examples/tdk-example/services/shop/orders-app/src/main.tsx +9 -0
- package/examples/tdk-example/services/shop/orders-app/src/orders.tsx +67 -0
- package/examples/tdk-example/services/shop/orders-app/src/style.css +15 -0
- package/examples/tdk-example/services/shop/orders-worker/package.json +18 -0
- package/examples/tdk-example/services/shop/orders-worker/service.json +13 -0
- package/examples/tdk-example/services/shop/orders-worker/src/index.ts +33 -0
- package/package.json +2 -1
|
@@ -1,18 +1,23 @@
|
|
|
1
|
-
import { execSync } from "node:child_process";
|
|
1
|
+
import { execFileSync, execSync } from "node:child_process";
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
|
-
import { accessSync, constants, existsSync, readFileSync, realpathSync } from "node:fs";
|
|
4
|
-
import { dirname, join, resolve } from "node:path";
|
|
3
|
+
import { accessSync, constants, existsSync, mkdirSync, readFileSync, realpathSync, renameSync, rmSync, writeFileSync, } from "node:fs";
|
|
4
|
+
import { basename, dirname, join, resolve, win32 } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import chalk from "chalk";
|
|
7
7
|
import { Command } from "commander";
|
|
8
8
|
import { getErrorMessage, logVerbose, showErrorAndExit } from "../utils/errors.js";
|
|
9
9
|
import { showCancelled } from "../utils/formatting.js";
|
|
10
10
|
import { getPackageVersion } from "../utils/paths.js";
|
|
11
|
+
import { binaryAssetName, executableName, isWindows } from "../utils/platform.js";
|
|
11
12
|
import { promptConfirm } from "../utils/prompt.js";
|
|
12
13
|
import { startSpinner } from "../utils/spinner.js";
|
|
14
|
+
import { extractTarball } from "../utils/tar.js";
|
|
15
|
+
import { findOnPath } from "../utils/which.js";
|
|
13
16
|
function isStandaloneBinary(tdkPath) {
|
|
17
|
+
if (isWindows() && tdkPath.toLowerCase().endsWith(".exe"))
|
|
18
|
+
return true;
|
|
14
19
|
try {
|
|
15
|
-
const fileInfo =
|
|
20
|
+
const fileInfo = execFileSync("file", ["-b", tdkPath], { encoding: "utf-8" });
|
|
16
21
|
return /Mach-O|ELF/.test(fileInfo);
|
|
17
22
|
}
|
|
18
23
|
catch (err) {
|
|
@@ -20,9 +25,33 @@ function isStandaloneBinary(tdkPath) {
|
|
|
20
25
|
return false;
|
|
21
26
|
}
|
|
22
27
|
}
|
|
28
|
+
export function classifyRunningInstall(execPath, scriptPath, platformName = process.platform) {
|
|
29
|
+
const runningName = (platformName === "win32" ? win32.basename(execPath) : basename(execPath)).toLowerCase();
|
|
30
|
+
if (runningName === "tdk" || runningName === "tdk.exe") {
|
|
31
|
+
return { method: "binary", path: execPath };
|
|
32
|
+
}
|
|
33
|
+
if (runningName === "node" || runningName === "node.exe") {
|
|
34
|
+
const normalizedScriptPath = scriptPath.toLowerCase();
|
|
35
|
+
if (normalizedScriptPath.includes("node_modules") || normalizedScriptPath.includes(".npm")) {
|
|
36
|
+
return { method: "npm", path: scriptPath };
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
export function windowsNpmUpgradeMessage(platformName, installInfo) {
|
|
42
|
+
return platformName === "win32" && installInfo.method === "npm"
|
|
43
|
+
? "use npm install -g @tdk-landscape/tdk-cli-core@latest"
|
|
44
|
+
: null;
|
|
45
|
+
}
|
|
23
46
|
function detectInstallation() {
|
|
47
|
+
const runningPath = process.execPath;
|
|
48
|
+
const runningInstall = classifyRunningInstall(runningPath, process.argv[1] ?? "");
|
|
49
|
+
if (runningInstall)
|
|
50
|
+
return runningInstall;
|
|
24
51
|
try {
|
|
25
|
-
const tdkPath =
|
|
52
|
+
const tdkPath = findOnPath(executableName());
|
|
53
|
+
if (!tdkPath)
|
|
54
|
+
return { method: "unknown" };
|
|
26
55
|
// `readlink -f` is not available on macOS. Use Node's cross-platform
|
|
27
56
|
// realpath implementation so standalone binaries are detected there too.
|
|
28
57
|
const realPath = realpathSync(tdkPath);
|
|
@@ -57,13 +86,6 @@ function detectInstallation() {
|
|
|
57
86
|
}
|
|
58
87
|
}
|
|
59
88
|
const BINARY_RELEASE_REPO = "tdk-landscape/tdk-cli-releases";
|
|
60
|
-
function binaryAssetName() {
|
|
61
|
-
const osName = process.platform === "darwin" ? "darwin" : process.platform === "linux" ? "linux" : null;
|
|
62
|
-
const archName = process.arch === "arm64" ? "arm64" : process.arch === "x64" ? "amd64" : null;
|
|
63
|
-
if (!osName || !archName)
|
|
64
|
-
return null;
|
|
65
|
-
return `tdk-${osName}-${archName}`;
|
|
66
|
-
}
|
|
67
89
|
const ENGINE_ASSET_NAME = "tdk-cli-engine.tar.gz";
|
|
68
90
|
const CHECKSUMS_ASSET_NAME = "checksums.txt";
|
|
69
91
|
/** Parses `shasum -a 256` / `sha256sum` output ("<hash> <name>" or "<hash> *<name>"). */
|
|
@@ -92,7 +114,7 @@ function verifyChecksum(filePath, assetName, sums, required) {
|
|
|
92
114
|
}
|
|
93
115
|
const actual = createHash("sha256").update(readFileSync(filePath)).digest("hex");
|
|
94
116
|
if (actual !== expected) {
|
|
95
|
-
throw new Error(
|
|
117
|
+
throw new Error(`${isWindows() ? "checksum mismatch" : "Checksum mismatch"} for ${assetName} (expected ${expected}, got ${actual})`);
|
|
96
118
|
}
|
|
97
119
|
}
|
|
98
120
|
async function getLatestBinaryRelease() {
|
|
@@ -100,8 +122,12 @@ async function getLatestBinaryRelease() {
|
|
|
100
122
|
if (!assetName)
|
|
101
123
|
return null;
|
|
102
124
|
try {
|
|
103
|
-
const
|
|
104
|
-
|
|
125
|
+
const response = await fetch(`https://api.github.com/repos/${BINARY_RELEASE_REPO}/releases/latest`, {
|
|
126
|
+
signal: AbortSignal.timeout(10_000),
|
|
127
|
+
});
|
|
128
|
+
if (!response.ok)
|
|
129
|
+
return null;
|
|
130
|
+
const data = (await response.json());
|
|
105
131
|
if (!data.tag_name)
|
|
106
132
|
return null;
|
|
107
133
|
return {
|
|
@@ -134,22 +160,31 @@ export async function upgradeViaBinary(tdkPath, release) {
|
|
|
134
160
|
if (!isWritable(installDir)) {
|
|
135
161
|
console.log(chalk.yellow(`\n🔒 ${installDir} isn't writable by your user.`));
|
|
136
162
|
console.log(chalk.yellow("💡 Upgrade manually instead:"));
|
|
137
|
-
|
|
138
|
-
|
|
163
|
+
if (isWindows()) {
|
|
164
|
+
console.log(chalk.cyan(" Install TDK in a user-writable directory, then retry the upgrade."));
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
console.log(chalk.cyan(` curl -fsSL -o ${tdkPath} ${release.downloadUrl}`));
|
|
168
|
+
console.log(chalk.cyan(` chmod +x ${tdkPath}`));
|
|
169
|
+
}
|
|
139
170
|
return false;
|
|
140
171
|
}
|
|
141
172
|
const spinner = startSpinner(`Downloading ${release.assetName} (${release.tag})...`);
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
const
|
|
145
|
-
const engineTmpPath = `/tmp/tdk-upgrade-engine-${release.tag}.tar.gz`;
|
|
173
|
+
const tempDir = join(installDir, `.tdk-upgrade-${process.pid}-${Date.now()}`);
|
|
174
|
+
const tmpPath = join(tempDir, release.assetName);
|
|
175
|
+
const engineTmpPath = join(tempDir, "engine.tar.gz");
|
|
146
176
|
const engineDir = join(installDir, "tdk-cli");
|
|
177
|
+
const engineStageDir = join(tempDir, "tdk-cli");
|
|
178
|
+
const binaryBackup = join(tempDir, `${basename(tdkPath)}.previous`);
|
|
179
|
+
const engineBackup = join(tempDir, "tdk-cli.previous");
|
|
147
180
|
try {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
181
|
+
mkdirSync(tempDir, { recursive: true });
|
|
182
|
+
const binaryResponse = await fetch(release.downloadUrl, {
|
|
183
|
+
signal: AbortSignal.timeout(120_000),
|
|
151
184
|
});
|
|
152
|
-
|
|
185
|
+
if (!binaryResponse.ok)
|
|
186
|
+
throw new Error(`Download failed (${binaryResponse.status})`);
|
|
187
|
+
writeFileSync(tmpPath, Buffer.from(await binaryResponse.arrayBuffer()));
|
|
153
188
|
// The compiled binary has no source checkout to find engine/ or
|
|
154
189
|
// cli/templates/ in, so it looks for a tdk-cli/ folder next to itself
|
|
155
190
|
// (see template-engine.ts loadTemplate() / vendorTdkExtension()).
|
|
@@ -157,41 +192,63 @@ export async function upgradeViaBinary(tdkPath, release) {
|
|
|
157
192
|
// stale/missing after an upgrade - refresh it here too, every time,
|
|
158
193
|
// the same way install.sh does on a fresh install.
|
|
159
194
|
spinner.text = `Downloading bundled engine (${release.tag})...`;
|
|
160
|
-
|
|
195
|
+
const engineResponse = await fetch(release.engineDownloadUrl, {
|
|
196
|
+
signal: AbortSignal.timeout(120_000),
|
|
197
|
+
});
|
|
198
|
+
if (!engineResponse.ok)
|
|
199
|
+
throw new Error(`Engine download failed (${engineResponse.status})`);
|
|
200
|
+
const engineBuffer = Buffer.from(await engineResponse.arrayBuffer());
|
|
201
|
+
writeFileSync(engineTmpPath, engineBuffer);
|
|
161
202
|
// Verify both downloads against the release's checksums.txt before
|
|
162
203
|
// touching the installed binary or engine.
|
|
163
204
|
spinner.text = `Verifying checksums (${release.tag})...`;
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
205
|
+
const checksumResponse = await fetch(release.checksumsUrl, {
|
|
206
|
+
signal: AbortSignal.timeout(30_000),
|
|
207
|
+
});
|
|
208
|
+
if (!checksumResponse.ok)
|
|
209
|
+
throw new Error(`Checksum download failed (${checksumResponse.status})`);
|
|
210
|
+
const sums = parseChecksums(await checksumResponse.text());
|
|
169
211
|
verifyChecksum(tmpPath, release.assetName, sums, true);
|
|
170
212
|
verifyChecksum(engineTmpPath, ENGINE_ASSET_NAME, sums, false);
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
213
|
+
mkdirSync(engineStageDir, { recursive: true });
|
|
214
|
+
extractTarball(engineBuffer, engineStageDir);
|
|
215
|
+
if (existsSync(tdkPath))
|
|
216
|
+
renameSync(tdkPath, binaryBackup);
|
|
217
|
+
try {
|
|
218
|
+
renameSync(tmpPath, tdkPath);
|
|
219
|
+
}
|
|
220
|
+
catch (err) {
|
|
221
|
+
if (existsSync(binaryBackup))
|
|
222
|
+
renameSync(binaryBackup, tdkPath);
|
|
223
|
+
throw err;
|
|
224
|
+
}
|
|
225
|
+
if (existsSync(engineDir))
|
|
226
|
+
renameSync(engineDir, engineBackup);
|
|
227
|
+
try {
|
|
228
|
+
renameSync(engineStageDir, engineDir);
|
|
229
|
+
}
|
|
230
|
+
catch (err) {
|
|
231
|
+
if (existsSync(engineBackup))
|
|
232
|
+
renameSync(engineBackup, engineDir);
|
|
233
|
+
if (existsSync(binaryBackup)) {
|
|
234
|
+
rmSync(tdkPath, { force: true });
|
|
235
|
+
renameSync(binaryBackup, tdkPath);
|
|
236
|
+
}
|
|
237
|
+
throw err;
|
|
238
|
+
}
|
|
239
|
+
rmSync(binaryBackup, { force: true });
|
|
240
|
+
rmSync(engineBackup, { recursive: true, force: true });
|
|
175
241
|
spinner.succeed(`Upgraded to ${release.tag}`);
|
|
176
242
|
return true;
|
|
177
243
|
}
|
|
178
244
|
catch (err) {
|
|
179
245
|
spinner.fail(`Binary upgrade failed: ${getErrorMessage(err)}`);
|
|
180
246
|
logVerbose("Binary upgrade error", err);
|
|
181
|
-
console.log(chalk.yellow("\n💡 If this failed due to permissions,
|
|
182
|
-
console.log(chalk.cyan(` curl -fsSL -o ${tdkPath} ${release.downloadUrl}`));
|
|
183
|
-
console.log(chalk.cyan(` chmod +x ${tdkPath}`));
|
|
184
|
-
console.log(chalk.cyan(` rm -rf ${engineDir} && mkdir -p ${engineDir}`));
|
|
185
|
-
console.log(chalk.cyan(` curl -fsSL ${release.engineDownloadUrl} | tar -xzf - -C ${engineDir} --strip-components=1`));
|
|
247
|
+
console.log(chalk.yellow("\n💡 If this failed due to permissions, use the platform installer or install command."));
|
|
186
248
|
return false;
|
|
187
249
|
}
|
|
188
250
|
finally {
|
|
189
|
-
|
|
190
|
-
execSync(`rm -f ${JSON.stringify(tmpPath)} ${JSON.stringify(engineTmpPath)}`);
|
|
191
|
-
}
|
|
192
|
-
catch {
|
|
193
|
-
// best-effort cleanup
|
|
194
|
-
}
|
|
251
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
195
252
|
}
|
|
196
253
|
}
|
|
197
254
|
function getCurrentVersion() {
|
|
@@ -206,10 +263,24 @@ function getCurrentVersion() {
|
|
|
206
263
|
async function getLatestVersion() {
|
|
207
264
|
const spinner = startSpinner("Checking for latest version...");
|
|
208
265
|
try {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
266
|
+
let result;
|
|
267
|
+
if (isWindows()) {
|
|
268
|
+
const response = await fetch("https://registry.npmjs.org/@tdk-landscape%2ftdk-cli-core/latest", {
|
|
269
|
+
signal: AbortSignal.timeout(10_000),
|
|
270
|
+
});
|
|
271
|
+
if (!response.ok)
|
|
272
|
+
throw new Error(`npm registry returned ${response.status}`);
|
|
273
|
+
const latest = (await response.json());
|
|
274
|
+
result = latest.version ?? "";
|
|
275
|
+
if (!result)
|
|
276
|
+
throw new Error("npm registry response did not include a version");
|
|
277
|
+
}
|
|
278
|
+
else {
|
|
279
|
+
result = execSync("npm view @tdk-landscape/tdk-cli-core version", {
|
|
280
|
+
encoding: "utf-8",
|
|
281
|
+
timeout: 10000,
|
|
282
|
+
}).trim();
|
|
283
|
+
}
|
|
213
284
|
spinner.succeed(`Latest version: ${chalk.green(result)}`);
|
|
214
285
|
return result;
|
|
215
286
|
}
|
|
@@ -254,7 +325,7 @@ async function upgradeViaNpm() {
|
|
|
254
325
|
async function upgradeViaBun() {
|
|
255
326
|
const spinner = startSpinner("Upgrading via bun...");
|
|
256
327
|
try {
|
|
257
|
-
|
|
328
|
+
execFileSync(findOnPath("bun") ?? "bun", ["install", "-g", "@tdk-landscape/tdk-cli-core@latest"], {
|
|
258
329
|
stdio: "inherit",
|
|
259
330
|
timeout: 120000,
|
|
260
331
|
});
|
|
@@ -266,7 +337,7 @@ async function upgradeViaBun() {
|
|
|
266
337
|
spinner.text = "bun registry failed, trying GitHub...";
|
|
267
338
|
logVerbose("bun registry error", err);
|
|
268
339
|
try {
|
|
269
|
-
|
|
340
|
+
execFileSync(findOnPath("bun") ?? "bun", ["install", "-g", "github:tdk-landscape/tdk-cli-core"], {
|
|
270
341
|
stdio: "inherit",
|
|
271
342
|
timeout: 120000,
|
|
272
343
|
});
|
|
@@ -282,36 +353,41 @@ async function upgradeViaBun() {
|
|
|
282
353
|
async function upgradeViaGit(path) {
|
|
283
354
|
const spinner = startSpinner("Pulling latest changes from git...");
|
|
284
355
|
try {
|
|
285
|
-
|
|
356
|
+
execFileSync("git", ["rev-parse", "--git-dir"], {
|
|
286
357
|
cwd: path,
|
|
287
358
|
stdio: "pipe",
|
|
288
359
|
});
|
|
289
360
|
spinner.text = "Fetching from origin...";
|
|
290
|
-
|
|
361
|
+
execFileSync("git", ["fetch", "origin"], {
|
|
291
362
|
cwd: path,
|
|
292
363
|
stdio: "pipe",
|
|
293
364
|
timeout: 30000,
|
|
294
365
|
});
|
|
295
|
-
const branch =
|
|
366
|
+
const branch = execFileSync("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
|
|
296
367
|
cwd: path,
|
|
297
368
|
encoding: "utf-8",
|
|
298
369
|
}).trim();
|
|
299
370
|
spinner.text = `Pulling latest on ${branch}...`;
|
|
300
|
-
|
|
371
|
+
execFileSync("git", ["pull", "origin", branch], {
|
|
301
372
|
cwd: path,
|
|
302
373
|
stdio: "pipe",
|
|
303
374
|
timeout: 30000,
|
|
304
375
|
});
|
|
305
376
|
if (existsSync(join(path, "cli", "package.json"))) {
|
|
306
377
|
spinner.text = "Rebuilding CLI...";
|
|
307
|
-
|
|
378
|
+
execFileSync(findOnPath("bun") ?? "bun", ["install"], {
|
|
379
|
+
cwd: join(path, "cli"),
|
|
380
|
+
stdio: "pipe",
|
|
381
|
+
timeout: 60000,
|
|
382
|
+
});
|
|
383
|
+
execFileSync(findOnPath("bun") ?? "bun", ["run", "build"], {
|
|
308
384
|
cwd: join(path, "cli"),
|
|
309
385
|
stdio: "pipe",
|
|
310
386
|
timeout: 60000,
|
|
311
387
|
});
|
|
312
388
|
}
|
|
313
389
|
spinner.text = "Re-linking CLI...";
|
|
314
|
-
|
|
390
|
+
execFileSync(findOnPath("bun") ?? "bun", ["link", "--force"], {
|
|
315
391
|
cwd: join(path, "cli"),
|
|
316
392
|
stdio: "pipe",
|
|
317
393
|
timeout: 30000,
|
|
@@ -337,6 +413,11 @@ export const upgradeCommand = new Command("upgrade")
|
|
|
337
413
|
const installInfo = detectInstallation();
|
|
338
414
|
console.log(chalk.gray(`Installation method: ${installInfo.method}`));
|
|
339
415
|
console.log();
|
|
416
|
+
const windowsNpmMessage = windowsNpmUpgradeMessage(process.platform, installInfo);
|
|
417
|
+
if (windowsNpmMessage) {
|
|
418
|
+
console.log(windowsNpmMessage);
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
340
421
|
if (installInfo.method === "unknown") {
|
|
341
422
|
console.error(chalk.red("❌ Could not detect installation method"));
|
|
342
423
|
console.log(chalk.yellow("\n💡 Manual upgrade (package not on npm yet, use GitHub):"));
|
|
@@ -351,7 +432,13 @@ export const upgradeCommand = new Command("upgrade")
|
|
|
351
432
|
console.log(chalk.blue("📦 Standalone binary installation detected"));
|
|
352
433
|
binaryRelease = await getLatestBinaryRelease();
|
|
353
434
|
if (!binaryRelease) {
|
|
354
|
-
|
|
435
|
+
if (process.platform === "win32" && process.arch === "arm64") {
|
|
436
|
+
showErrorAndExit("TDK Windows v1 supports AMD64 only. Use WSL2 Ubuntu or a 64-bit Intel/AMD PC.");
|
|
437
|
+
}
|
|
438
|
+
if (process.platform === "win32") {
|
|
439
|
+
showErrorAndExit("No Windows AMD64 binary in this TDK release. Need asset tdk-windows-amd64.exe.");
|
|
440
|
+
}
|
|
441
|
+
showErrorAndExit(`Unsupported OS: TDK does not support this OS: ${process.platform}. Supported: linux, darwin, win32.`);
|
|
355
442
|
}
|
|
356
443
|
latestVersion = binaryRelease.tag.replace(/^v/, "").split("-")[0];
|
|
357
444
|
if (latestVersion === currentVersion && !options.force) {
|
|
@@ -369,12 +456,12 @@ export const upgradeCommand = new Command("upgrade")
|
|
|
369
456
|
else if (installInfo.method === "git" && installInfo.path) {
|
|
370
457
|
console.log(chalk.blue("📦 Git installation detected - will pull latest from origin"));
|
|
371
458
|
try {
|
|
372
|
-
|
|
373
|
-
const localHash =
|
|
459
|
+
execFileSync("git", ["fetch", "origin"], { cwd: installInfo.path, stdio: "pipe" });
|
|
460
|
+
const localHash = execFileSync("git", ["rev-parse", "HEAD"], {
|
|
374
461
|
cwd: installInfo.path,
|
|
375
462
|
encoding: "utf-8",
|
|
376
463
|
}).trim();
|
|
377
|
-
const remoteHash =
|
|
464
|
+
const remoteHash = execFileSync("git", ["rev-parse", "origin/main"], {
|
|
378
465
|
cwd: installInfo.path,
|
|
379
466
|
encoding: "utf-8",
|
|
380
467
|
}).trim();
|
|
@@ -484,17 +571,25 @@ export const upgradeCommand = new Command("upgrade")
|
|
|
484
571
|
}
|
|
485
572
|
else if (installInfo.method === "binary" && binaryRelease && installInfo.path) {
|
|
486
573
|
const engineDir = join(dirname(installInfo.path), "tdk-cli");
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
574
|
+
if (isWindows()) {
|
|
575
|
+
console.log(chalk.cyan(" irm https://tdk-landscape.github.io/install.ps1 | iex"));
|
|
576
|
+
}
|
|
577
|
+
else {
|
|
578
|
+
console.log(chalk.cyan(` curl -fsSL -o ${installInfo.path} ${binaryRelease.downloadUrl}`));
|
|
579
|
+
console.log(chalk.cyan(` chmod +x ${installInfo.path}`));
|
|
580
|
+
console.log(chalk.cyan(` rm -rf ${engineDir} && mkdir -p ${engineDir}`));
|
|
581
|
+
console.log(chalk.cyan(` curl -fsSL ${binaryRelease.engineDownloadUrl} | tar -xzf - -C ${engineDir} --strip-components=1`));
|
|
582
|
+
}
|
|
491
583
|
}
|
|
492
584
|
process.exit(1);
|
|
493
585
|
}
|
|
494
586
|
console.log();
|
|
495
587
|
const verifySpinner = startSpinner("Verifying upgrade...");
|
|
496
588
|
try {
|
|
497
|
-
const newVersion =
|
|
589
|
+
const newVersion = execFileSync(findOnPath(executableName()) ?? executableName(), ["version"], {
|
|
590
|
+
encoding: "utf-8",
|
|
591
|
+
windowsHide: isWindows(),
|
|
592
|
+
}).trim();
|
|
498
593
|
verifySpinner.succeed(`Verified: now running ${chalk.green(newVersion)}`);
|
|
499
594
|
console.log();
|
|
500
595
|
console.log(chalk.green.bold("✨ Upgrade complete!"));
|
|
@@ -508,7 +603,7 @@ export const upgradeCommand = new Command("upgrade")
|
|
|
508
603
|
else {
|
|
509
604
|
console.log(chalk.gray(` Method: ${installInfo.method}`));
|
|
510
605
|
}
|
|
511
|
-
console.log(chalk.gray(` Binary: ${
|
|
606
|
+
console.log(chalk.gray(` Binary: ${installInfo.path ?? findOnPath(executableName()) ?? "unknown"}`));
|
|
512
607
|
console.log();
|
|
513
608
|
console.log(chalk.cyan.bold("🚀 Quick Start:"));
|
|
514
609
|
console.log(chalk.white(` tdk --help Show all commands`));
|
|
@@ -4,7 +4,9 @@ import { fileURLToPath } from "node:url";
|
|
|
4
4
|
import Handlebars from "handlebars";
|
|
5
5
|
import { PLATFORM_STANDARDS } from "../config/platform-standards.js";
|
|
6
6
|
import { writeTextFile } from "../utils/file-helpers.js";
|
|
7
|
-
import {
|
|
7
|
+
import { assertTdkGeneratedPath } from "../utils/generated-paths.js";
|
|
8
|
+
import { validateServiceManifestFile } from "../utils/service-manifest.js";
|
|
9
|
+
import { discoverResourcesFromRoot, discoverServiceManifestPaths } from "../utils/services.js";
|
|
8
10
|
import { isStackFeatureEnabledInStacks } from "../utils/stack-features.js";
|
|
9
11
|
import { hasSablierLicense, hasVerdaccioLicense } from "./extension-fetch.js";
|
|
10
12
|
const __filename = fileURLToPath(import.meta.url);
|
|
@@ -129,7 +131,6 @@ export class TemplateEngine {
|
|
|
129
131
|
buildContext(projectConfig) {
|
|
130
132
|
return {
|
|
131
133
|
version: PLATFORM_STANDARDS.version,
|
|
132
|
-
timestamp: new Date().toISOString(),
|
|
133
134
|
tech: PLATFORM_STANDARDS.tech,
|
|
134
135
|
ports: PLATFORM_STANDARDS.ports,
|
|
135
136
|
health: PLATFORM_STANDARDS.health,
|
|
@@ -351,7 +352,7 @@ function normalizeProjectConfig(value) {
|
|
|
351
352
|
export function readProjectConfig(projectRoot) {
|
|
352
353
|
const projectJsonPath = path.join(projectRoot, ".tdk", "project.json");
|
|
353
354
|
if (!fs.existsSync(projectJsonPath)) {
|
|
354
|
-
throw new Error(`Project config not found: ${projectJsonPath}. Run 'tdk project
|
|
355
|
+
throw new Error(`Project config not found: ${projectJsonPath}. Run 'tdk project --yes' first.`);
|
|
355
356
|
}
|
|
356
357
|
const jsonContent = fs.readFileSync(projectJsonPath, "utf-8");
|
|
357
358
|
const parsed = JSON.parse(jsonContent);
|
|
@@ -389,7 +390,7 @@ async function warnIfSablierUnlicensed(projectRoot) {
|
|
|
389
390
|
"or remove the `sablier` block from the affected service.json file(s) to silence this warning.");
|
|
390
391
|
}
|
|
391
392
|
function ensureRootWorkspaceManifest(projectRoot, projectConfig) {
|
|
392
|
-
const packageJsonPath =
|
|
393
|
+
const packageJsonPath = assertTdkGeneratedPath(projectRoot, "package.json");
|
|
393
394
|
if (fs.existsSync(packageJsonPath)) {
|
|
394
395
|
return false;
|
|
395
396
|
}
|
|
@@ -437,21 +438,21 @@ export async function generateMasterConfigs(projectRoot) {
|
|
|
437
438
|
const files = engine.generateAll(projectConfig);
|
|
438
439
|
for (const filename of ALL_GENERATED_FILES) {
|
|
439
440
|
const content = files[filename];
|
|
440
|
-
const filePath = path.join(
|
|
441
|
+
const filePath = assertTdkGeneratedPath(projectRoot, path.join(".tdk", ".tdk-out", filename));
|
|
441
442
|
writeTextFile(filePath, content);
|
|
442
443
|
console.log(`✓ Generated: .tdk/.tdk-out/${filename}`);
|
|
443
444
|
}
|
|
444
445
|
if (isStackFeatureEnabledInStacks(projectConfig.phases, "database-management")) {
|
|
445
|
-
const
|
|
446
|
+
const composePath = assertTdkGeneratedPath(projectRoot, path.join("services", "platform", "database-management", "docker-compose.yml"));
|
|
447
|
+
const composeDir = path.dirname(composePath);
|
|
446
448
|
fs.mkdirSync(composeDir, { recursive: true });
|
|
447
|
-
const composePath = path.join(composeDir, "docker-compose.yml");
|
|
448
449
|
writeTextFile(composePath, generateDatabaseManagementCompose(projectConfig));
|
|
449
450
|
console.log("✓ Generated: services/platform/database-management/docker-compose.yml");
|
|
450
451
|
}
|
|
451
452
|
// Copy .tiltignore to project root so Tilt uses it
|
|
452
453
|
const tiltignoreSource = path.join(outputDir, ".tiltignore");
|
|
453
|
-
const tiltignoreTarget =
|
|
454
|
-
if (fs.existsSync(tiltignoreSource)) {
|
|
454
|
+
const tiltignoreTarget = assertTdkGeneratedPath(projectRoot, ".tiltignore");
|
|
455
|
+
if (fs.existsSync(tiltignoreSource) && !fs.existsSync(tiltignoreTarget)) {
|
|
455
456
|
fs.copyFileSync(tiltignoreSource, tiltignoreTarget);
|
|
456
457
|
console.log(`✓ Copied: .tiltignore → project root`);
|
|
457
458
|
}
|
|
@@ -521,7 +522,7 @@ export function ensureProjectRuntimeAssets(projectRoot) {
|
|
|
521
522
|
}
|
|
522
523
|
for (const parts of PROJECT_RUNTIME_ASSET_DIRS) {
|
|
523
524
|
const source = path.join(sourceRoot, ...parts);
|
|
524
|
-
const target = path.join(
|
|
525
|
+
const target = assertTdkGeneratedPath(projectRoot, path.join(...parts));
|
|
525
526
|
if (!fs.existsSync(source)) {
|
|
526
527
|
continue;
|
|
527
528
|
}
|
|
@@ -538,8 +539,7 @@ export function ensureProjectRuntimeAssets(projectRoot) {
|
|
|
538
539
|
return copied;
|
|
539
540
|
}
|
|
540
541
|
async function vendorTdkExtension(projectRoot) {
|
|
541
|
-
const
|
|
542
|
-
const vendoredDir = path.join(outputDir, "tdk-cli-ext");
|
|
542
|
+
const vendoredDir = assertTdkGeneratedPath(projectRoot, path.join(".tdk", ".tdk-out", "tdk-cli-ext"));
|
|
543
543
|
// Source resolution order:
|
|
544
544
|
// 1. $TDK_EXTENSION_SOURCE (explicit override)
|
|
545
545
|
// 2. Self-contained repo (engine/ ships next to cli/ - public tdk-cli-core)
|
|
@@ -602,6 +602,7 @@ async function vendorTdkExtension(projectRoot) {
|
|
|
602
602
|
}
|
|
603
603
|
export function verifyMasterConfigs(projectRoot) {
|
|
604
604
|
const errors = [];
|
|
605
|
+
const warnings = [];
|
|
605
606
|
const projectConfig = readProjectConfig(projectRoot);
|
|
606
607
|
const engine = new TemplateEngine();
|
|
607
608
|
const expectedFiles = engine.generateAll(projectConfig);
|
|
@@ -618,6 +619,69 @@ export function verifyMasterConfigs(projectRoot) {
|
|
|
618
619
|
errors.push(`Out of sync: .tdk/.tdk-out/${filename} (run 'tdk config regenerate')`);
|
|
619
620
|
}
|
|
620
621
|
}
|
|
621
|
-
|
|
622
|
+
const rootTiltIgnore = path.join(projectRoot, ".tiltignore");
|
|
623
|
+
if (!fs.existsSync(rootTiltIgnore)) {
|
|
624
|
+
errors.push("Missing file: .tiltignore");
|
|
625
|
+
}
|
|
626
|
+
else if (fs.readFileSync(rootTiltIgnore, "utf-8") !== expectedFiles[".tiltignore"]) {
|
|
627
|
+
errors.push("Out of sync: .tiltignore (run 'tdk config regenerate')");
|
|
628
|
+
}
|
|
629
|
+
for (const manifestPath of discoverServiceManifestPaths(projectRoot)) {
|
|
630
|
+
const displayPath = path.relative(projectRoot, manifestPath).split(path.sep).join("/");
|
|
631
|
+
const validation = validateServiceManifestFile(manifestPath, displayPath);
|
|
632
|
+
errors.push(...validation.errors);
|
|
633
|
+
warnings.push(...validation.warnings);
|
|
634
|
+
errors.push(...verifyGeneratedResourceFiles(projectRoot, manifestPath));
|
|
635
|
+
}
|
|
636
|
+
return { valid: errors.length === 0, errors, warnings };
|
|
637
|
+
}
|
|
638
|
+
function verifyGeneratedResourceFiles(projectRoot, serviceJsonPath) {
|
|
639
|
+
const serviceDir = path.dirname(serviceJsonPath);
|
|
640
|
+
const generatedDir = path.join(serviceDir, ".autogenerated");
|
|
641
|
+
if (!fs.existsSync(generatedDir))
|
|
642
|
+
return [];
|
|
643
|
+
const generatedFiles = [];
|
|
644
|
+
const collect = (directory, relativeDir = "") => {
|
|
645
|
+
for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
|
|
646
|
+
if (relativeDir === "" && entry.name === ".tdk-contract")
|
|
647
|
+
continue;
|
|
648
|
+
const relativeFile = path.join(relativeDir, entry.name);
|
|
649
|
+
const fullPath = path.join(directory, entry.name);
|
|
650
|
+
if (entry.isDirectory())
|
|
651
|
+
collect(fullPath, relativeFile);
|
|
652
|
+
else if (entry.isFile())
|
|
653
|
+
generatedFiles.push({ fullPath, relativeFile });
|
|
654
|
+
}
|
|
655
|
+
};
|
|
656
|
+
collect(generatedDir);
|
|
657
|
+
if (generatedFiles.length === 0)
|
|
658
|
+
return [];
|
|
659
|
+
const contractDir = path.join(generatedDir, ".tdk-contract");
|
|
660
|
+
const sourceSnapshot = path.join(contractDir, "service.json.source");
|
|
661
|
+
const errors = [];
|
|
662
|
+
if (!fs.existsSync(sourceSnapshot)) {
|
|
663
|
+
errors.push(path.relative(projectRoot, sourceSnapshot) +
|
|
664
|
+
": generated-file contract snapshot is missing (run tdk up to regenerate)");
|
|
665
|
+
}
|
|
666
|
+
else if (fs.readFileSync(sourceSnapshot, "utf-8") !== fs.readFileSync(serviceJsonPath, "utf-8")) {
|
|
667
|
+
errors.push(path.relative(projectRoot, serviceJsonPath) +
|
|
668
|
+
": service.json changed since generated outputs were written (run tdk up)");
|
|
669
|
+
}
|
|
670
|
+
for (const generated of generatedFiles) {
|
|
671
|
+
// service.yaml is a runtime discovery shadow generated by the Tilt
|
|
672
|
+
// extension. It is not produced by `tdk config regenerate`, and its
|
|
673
|
+
// timestamp/UID content has no committed TDK snapshot to verify.
|
|
674
|
+
if (generated.relativeFile === "service.yaml")
|
|
675
|
+
continue;
|
|
676
|
+
const snapshotPath = path.join(contractDir, generated.relativeFile);
|
|
677
|
+
const display = path.relative(projectRoot, generated.fullPath);
|
|
678
|
+
if (!fs.existsSync(snapshotPath)) {
|
|
679
|
+
errors.push(`${display}: generated-file snapshot is missing (run tdk up to regenerate)`);
|
|
680
|
+
}
|
|
681
|
+
else if (fs.readFileSync(generated.fullPath, "utf-8") !== fs.readFileSync(snapshotPath, "utf-8")) {
|
|
682
|
+
errors.push(`${display}: generated file differs from its TDK snapshot (run tdk up to regenerate)`);
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
return errors;
|
|
622
686
|
}
|
|
623
687
|
//# sourceMappingURL=template-engine.js.map
|
package/cli/dist/types/index.js
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
export const CREATABLE_RESOURCE_TYPES = [
|
|
1
|
+
export const CREATABLE_RESOURCE_TYPES = [
|
|
2
|
+
"backend",
|
|
3
|
+
"frontend",
|
|
4
|
+
"worker",
|
|
5
|
+
"bring-your-own",
|
|
6
|
+
];
|
|
2
7
|
export function isCreatableResourceType(value) {
|
|
3
8
|
return (typeof value === "string" && CREATABLE_RESOURCE_TYPES.includes(value));
|
|
4
9
|
}
|