@tdk-landscape/tdk-cli-core 1.3.136 → 1.3.137
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 +4 -0
- package/cli/dist/commands/doctor.js +11 -11
- package/cli/dist/commands/resource.js +15 -14
- package/cli/dist/generator/template-engine.js +10 -7
- package/cli/dist/utils/constants.js +22 -3
- package/cli/dist/utils/doctor-report.js +2 -1
- package/cli/dist/utils/doctor-runtime.js +6 -1
- package/cli/dist/utils/doctor-wiring.js +5 -4
- package/cli/dist/utils/generated-paths.js +3 -2
- package/cli/dist/utils/project-config-defaults.js +2 -1
- package/cli/dist/utils/project-features.js +5 -13
- package/cli/dist/utils/resource-features.js +2 -1
- package/cli/dist/utils/services.js +2 -2
- package/cli/dist/utils/shared-platform-postgres.js +6 -5
- package/cli/dist/utils/smoke.js +3 -2
- package/cli/dist/utils/stack-features.js +5 -2
- package/cli/dist-bundle/chunk-DUOGIS2Y.js +315 -0
- package/cli/dist-bundle/cli.js +207 -207
- package/cli/dist-bundle/{cold-preflight-NXFER2W6.js → cold-preflight-UATSRM67.js} +1 -1
- package/cli/package.json +1 -1
- package/engine/assets/docker/wake-gateway/server.ts +3 -3
- package/engine/topologies/platform/docker/compose/compose.star +4 -4
- package/engine/topologies/platform/docker/constants.star +25 -0
- package/engine/topologies/platform/docker/networking/traefik_static_routes.star +3 -1
- package/engine/topologies/tilt/generators/vite/templates.star +13 -9
- package/engine/topologies/tilt/resources/infra-loader.star +20 -14
- package/engine/topologies/tilt/resources/orchestrator/apply_compose_resource_registration.star +4 -4
- package/engine/topologies/tilt/resources/orchestrator/generators/env.star +3 -2
- package/engine/topologies/tilt/resources/orchestrator/generators/frontend.star +2 -1
- package/package.json +1 -1
- package/cli/dist-bundle/chunk-I63IBPUZ.js +0 -315
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,10 @@ Notable changes to the `tdk` CLI. Versions match [npm](https://www.npmjs.com/pac
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
- Shared platform stacks (postgres, traefik, proxy, messaging, infisical, monitoring, cdc, elk) now run in Compose projects named `<project>-<stack>` (for example `tdk-erp-system-database-management`) instead of the directory-named `database-management`, `tdk-out` and so on. Two TDK projects on one Docker daemon no longer share them.
|
|
8
|
+
|
|
9
|
+
- Two TDK projects that each have a stack with the same name (for example `people`) no longer share Docker names. Compose projects are now `<project>-<stack>-autogenerated`, containers `<project>-<stack>-autogenerated-<service>-1` and images `<project>_<stack>_<service>:dev` (migrators `<project>_<stack>-db-migrator:dev`). Before, the second project's `tdk up` removed the first project's containers as orphans and overwrote its image. Containers from before this change keep the old names and are not removed by the new engine: stop them once, see [docs/layout.md](docs/layout.md).
|
|
10
|
+
|
|
7
11
|
## 1.3.130 (2026-10-08)
|
|
8
12
|
|
|
9
13
|
- Releases now come with a signed checksum list: `checksums.txt.sigstore.json` (keyless Sigstore signature from the release workflow) is attached next to the binaries in [tdk-cli-releases](https://github.com/tdk-landscape/tdk-cli-releases/releases) and to a release with the same tag here. How to verify is in [docs/security.md](docs/security.md#releases). The binaries themselves are not signed individually (#869, #840).
|
|
@@ -8,7 +8,7 @@ import { Command } from "commander";
|
|
|
8
8
|
import { hasVerdaccioLicense } from "../generator/extension-fetch.js";
|
|
9
9
|
import { DEVCONTAINER_DOCKER_FIX, detectHost, isContainerHost, WEBCONTAINER_DOCS, WEBCONTAINER_UP_MESSAGE, } from "../utils/agent-host.js";
|
|
10
10
|
import { projectNeedsBun } from "../utils/bun-requirement.js";
|
|
11
|
-
import { MASTER_CONFIG_FILES, REQUIRED_PACKAGE_SCRIPTS } from "../utils/constants.js";
|
|
11
|
+
import { CONTAINER_RUNTIME_CHECK, MASTER_CONFIG_FILES, REQUIRED_PACKAGE_SCRIPTS, } from "../utils/constants.js";
|
|
12
12
|
import { isPathDiscovered, readDiscoveryPaths } from "../utils/discovery-paths.js";
|
|
13
13
|
import { collectDoctorChecks, createDoctorReport, getDoctorExitCode, } from "../utils/doctor-report.js";
|
|
14
14
|
import { checkHealthRoutes, checkIngressPorts, checkPrivateNpmRegistry, checkTiltResourceHealth, projectConfigEnablesVerdaccio, summarizeServiceProbes, } from "../utils/doctor-runtime.js";
|
|
@@ -169,14 +169,14 @@ export async function checkDockerRuntime() {
|
|
|
169
169
|
try {
|
|
170
170
|
await execAsync("docker ps", EXEC_TIMEOUT_MS);
|
|
171
171
|
return {
|
|
172
|
-
name:
|
|
172
|
+
name: CONTAINER_RUNTIME_CHECK,
|
|
173
173
|
didPass: true,
|
|
174
174
|
message: "Docker Desktop Linux container engine is running",
|
|
175
175
|
};
|
|
176
176
|
}
|
|
177
177
|
catch {
|
|
178
178
|
return {
|
|
179
|
-
name:
|
|
179
|
+
name: CONTAINER_RUNTIME_CHECK,
|
|
180
180
|
didPass: false,
|
|
181
181
|
message: "Docker is not running",
|
|
182
182
|
fix: DOCTOR_FIXES.dockerDaemonDown,
|
|
@@ -187,7 +187,7 @@ export async function checkDockerRuntime() {
|
|
|
187
187
|
try {
|
|
188
188
|
await execAsync("docker ps", EXEC_TIMEOUT_MS);
|
|
189
189
|
return {
|
|
190
|
-
name:
|
|
190
|
+
name: CONTAINER_RUNTIME_CHECK,
|
|
191
191
|
didPass: true,
|
|
192
192
|
message: "Docker daemon is running",
|
|
193
193
|
};
|
|
@@ -195,7 +195,7 @@ export async function checkDockerRuntime() {
|
|
|
195
195
|
catch (err) {
|
|
196
196
|
if (isTimeout(err)) {
|
|
197
197
|
return {
|
|
198
|
-
name:
|
|
198
|
+
name: CONTAINER_RUNTIME_CHECK,
|
|
199
199
|
didPass: false,
|
|
200
200
|
message: "Docker daemon is not responding",
|
|
201
201
|
fix: DOCTOR_FIXES.dockerDaemonDown,
|
|
@@ -205,14 +205,14 @@ export async function checkDockerRuntime() {
|
|
|
205
205
|
// Docker not running: check for Colima, then Podman
|
|
206
206
|
if (await succeeds("colima status")) {
|
|
207
207
|
return {
|
|
208
|
-
name:
|
|
208
|
+
name: CONTAINER_RUNTIME_CHECK,
|
|
209
209
|
didPass: true,
|
|
210
210
|
message: "Colima (Docker runtime) is running",
|
|
211
211
|
};
|
|
212
212
|
}
|
|
213
213
|
if (findOnPath("colima")) {
|
|
214
214
|
return {
|
|
215
|
-
name:
|
|
215
|
+
name: CONTAINER_RUNTIME_CHECK,
|
|
216
216
|
didPass: false,
|
|
217
217
|
message: "Colima is installed but not running",
|
|
218
218
|
fix: DOCTOR_FIXES.dockerDaemonDown,
|
|
@@ -220,21 +220,21 @@ export async function checkDockerRuntime() {
|
|
|
220
220
|
}
|
|
221
221
|
if (await succeeds("podman ps")) {
|
|
222
222
|
return {
|
|
223
|
-
name:
|
|
223
|
+
name: CONTAINER_RUNTIME_CHECK,
|
|
224
224
|
didPass: true,
|
|
225
225
|
message: "Podman is running",
|
|
226
226
|
};
|
|
227
227
|
}
|
|
228
228
|
if (hasDockerCli) {
|
|
229
229
|
return {
|
|
230
|
-
name:
|
|
230
|
+
name: CONTAINER_RUNTIME_CHECK,
|
|
231
231
|
didPass: false,
|
|
232
232
|
message: "Docker daemon is not running",
|
|
233
233
|
fix: DOCTOR_FIXES.dockerDaemonDown,
|
|
234
234
|
};
|
|
235
235
|
}
|
|
236
236
|
return {
|
|
237
|
-
name:
|
|
237
|
+
name: CONTAINER_RUNTIME_CHECK,
|
|
238
238
|
didPass: false,
|
|
239
239
|
message: "Docker is not running",
|
|
240
240
|
fix: DOCTOR_FIXES.dockerMissing,
|
|
@@ -1089,7 +1089,7 @@ export const doctorCommand = new Command("doctor")
|
|
|
1089
1089
|
const { checks: results, errors } = await collectDoctorChecks(machineChecks, inProject ? projectChecks : []);
|
|
1090
1090
|
if (isContainerHost(host.kind)) {
|
|
1091
1091
|
for (const result of results) {
|
|
1092
|
-
if (result.name ===
|
|
1092
|
+
if (result.name === CONTAINER_RUNTIME_CHECK && !result.didPass) {
|
|
1093
1093
|
result.fix = DEVCONTAINER_DOCKER_FIX;
|
|
1094
1094
|
}
|
|
1095
1095
|
}
|
|
@@ -12,6 +12,7 @@ import { hasDddLicense } from "../generator/extension-fetch.js";
|
|
|
12
12
|
import { getDockerfileTemplate, getTestTemplate, getWorkerIndexTemplate, } from "../generator/resource-templates.js";
|
|
13
13
|
import { CREATABLE_RESOURCE_TYPES } from "../types/index.js";
|
|
14
14
|
import { assertValid, confirmOrCancel } from "../utils/command-helpers.js";
|
|
15
|
+
import { BRING_YOUR_OWN_TYPE, SERVICE_JSON } from "../utils/constants.js";
|
|
15
16
|
import { chooseResourcePath, isPathDiscovered, readDiscoveryPaths, } from "../utils/discovery-paths.js";
|
|
16
17
|
import { errorFactories, requireProjectRoot, runCommand, TdkError } from "../utils/errors.js";
|
|
17
18
|
import { writeFilesWithProgress } from "../utils/file-helpers.js";
|
|
@@ -55,7 +56,7 @@ export const TYPE_SPECIFIC = {
|
|
|
55
56
|
mcp: {
|
|
56
57
|
healthCheckPath: "/health",
|
|
57
58
|
},
|
|
58
|
-
|
|
59
|
+
[BRING_YOUR_OWN_TYPE]: {
|
|
59
60
|
healthCheckPath: "/health",
|
|
60
61
|
},
|
|
61
62
|
};
|
|
@@ -123,7 +124,7 @@ export function createByoServiceJson(name, stack, port, options) {
|
|
|
123
124
|
$schema: SERVICE_MANIFEST_SCHEMA_URL,
|
|
124
125
|
schemaVersion: SERVICE_MANIFEST_SCHEMA_VERSION,
|
|
125
126
|
appName: name,
|
|
126
|
-
appType:
|
|
127
|
+
appType: BRING_YOUR_OWN_TYPE,
|
|
127
128
|
stack,
|
|
128
129
|
port,
|
|
129
130
|
healthCheckPath: options.healthCheckPath,
|
|
@@ -251,7 +252,7 @@ export const TSCONFIG_TEMPLATE = {
|
|
|
251
252
|
export { getBackendIndexTemplate };
|
|
252
253
|
/** Resource types `tdk resource --type` accepts; `byo` is an alias of `bring-your-own`. */
|
|
253
254
|
export function parseResourceType(type) {
|
|
254
|
-
const normalized = type === "byo" ?
|
|
255
|
+
const normalized = type === "byo" ? BRING_YOUR_OWN_TYPE : type;
|
|
255
256
|
const validTypes = [...CREATABLE_RESOURCE_TYPES, "sdk"];
|
|
256
257
|
if (!validTypes.includes(normalized)) {
|
|
257
258
|
throw new TdkError(`Unknown resource type "${type}". Supported types: ${validTypes.join(", ")}.`, [`Use one of: ${validTypes.join(", ")}`, "Omit --type to create a backend"]);
|
|
@@ -412,7 +413,7 @@ export const resourceCommand = new Command("resource")
|
|
|
412
413
|
frontend: `apps/${resourceName}`,
|
|
413
414
|
worker: `workers/${resourceName}`,
|
|
414
415
|
mcp: `services/${stackName}/${resourceName}`,
|
|
415
|
-
|
|
416
|
+
[BRING_YOUR_OWN_TYPE]: `services/${stackName}/${resourceName}`,
|
|
416
417
|
sdk: `packages/${resourceName}`,
|
|
417
418
|
};
|
|
418
419
|
finalResourcePath = defaultPaths[resourceType];
|
|
@@ -434,7 +435,7 @@ export const resourceCommand = new Command("resource")
|
|
|
434
435
|
}
|
|
435
436
|
// Check if resource already exists
|
|
436
437
|
const isExistingResource = existsSync(fullPath);
|
|
437
|
-
const hasServiceJson = existsSync(resolve(fullPath,
|
|
438
|
+
const hasServiceJson = existsSync(resolve(fullPath, SERVICE_JSON));
|
|
438
439
|
if (resourceType === "sdk" && !hasServiceJson) {
|
|
439
440
|
throw new TdkError(`No service.json in ${finalResourcePath}`, [
|
|
440
441
|
'Add one with "appType": "sdk", then run this again to register it',
|
|
@@ -443,14 +444,14 @@ export const resourceCommand = new Command("resource")
|
|
|
443
444
|
const shouldRegisterExisting = options.registerExisting ||
|
|
444
445
|
resourceType === "sdk" ||
|
|
445
446
|
(isExistingResource && hasServiceJson);
|
|
446
|
-
if (isExistingResource && !shouldRegisterExisting && resourceType !==
|
|
447
|
+
if (isExistingResource && !shouldRegisterExisting && resourceType !== BRING_YOUR_OWN_TYPE) {
|
|
447
448
|
errorFactories.directoryExists(fullPath).exit();
|
|
448
449
|
}
|
|
449
450
|
const nextPort = resourceType === "sdk"
|
|
450
451
|
? 0
|
|
451
452
|
: assignPort(resourceType, allResources);
|
|
452
453
|
if (options.restart !== undefined) {
|
|
453
|
-
if (resourceType !==
|
|
454
|
+
if (resourceType !== BRING_YOUR_OWN_TYPE) {
|
|
454
455
|
throw new TdkError("--restart can only be used with --type bring-your-own.", [
|
|
455
456
|
"Add --type bring-your-own, or drop --restart",
|
|
456
457
|
]);
|
|
@@ -461,7 +462,7 @@ export const resourceCommand = new Command("resource")
|
|
|
461
462
|
]);
|
|
462
463
|
}
|
|
463
464
|
}
|
|
464
|
-
const assignedPort = resourceType ===
|
|
465
|
+
const assignedPort = resourceType === BRING_YOUR_OWN_TYPE
|
|
465
466
|
? resolveByoPort(options.port, nextPort, allResources)
|
|
466
467
|
: nextPort;
|
|
467
468
|
console.log(chalk.gray("\nResource details:"));
|
|
@@ -483,7 +484,7 @@ export const resourceCommand = new Command("resource")
|
|
|
483
484
|
if (shouldRegisterExisting && hasServiceJson) {
|
|
484
485
|
// Read existing service.json
|
|
485
486
|
const { readFileSync } = await import("node:fs");
|
|
486
|
-
const existingServiceJsonPath = resolve(fullPath,
|
|
487
|
+
const existingServiceJsonPath = resolve(fullPath, SERVICE_JSON);
|
|
487
488
|
const existingContent = readFileSync(existingServiceJsonPath, "utf-8");
|
|
488
489
|
const existingServiceJson = JSON.parse(existingContent);
|
|
489
490
|
// Update with new values while preserving existing fields
|
|
@@ -506,7 +507,7 @@ export const resourceCommand = new Command("resource")
|
|
|
506
507
|
return;
|
|
507
508
|
}
|
|
508
509
|
// Handle bring-your-own type
|
|
509
|
-
if (resourceType ===
|
|
510
|
+
if (resourceType === BRING_YOUR_OWN_TYPE) {
|
|
510
511
|
console.log(chalk.blue("\n📁 Creating bring-your-own resource..."));
|
|
511
512
|
// Parse port option
|
|
512
513
|
const port = assignedPort;
|
|
@@ -528,7 +529,7 @@ export const resourceCommand = new Command("resource")
|
|
|
528
529
|
...(options.restart ? { restart: options.restart } : {}),
|
|
529
530
|
});
|
|
530
531
|
const { writeFileSync } = await import("node:fs");
|
|
531
|
-
writeFileSync(resolve(fullPath,
|
|
532
|
+
writeFileSync(resolve(fullPath, SERVICE_JSON), JSON.stringify(byoServiceJson, null, 2));
|
|
532
533
|
// Create Dockerfile stub if no image provided and dockerfile doesn't exist
|
|
533
534
|
if (!options.image && !existsSync(dockerfilePath)) {
|
|
534
535
|
mkdirSync(resolve(dockerfilePath, ".."), { recursive: true });
|
|
@@ -599,7 +600,7 @@ This file contains the resource configuration for TDK.
|
|
|
599
600
|
const tasks = [
|
|
600
601
|
{
|
|
601
602
|
type: "json",
|
|
602
|
-
filename:
|
|
603
|
+
filename: SERVICE_JSON,
|
|
603
604
|
content: serviceJson,
|
|
604
605
|
description: "Generating service.json",
|
|
605
606
|
emoji: "📝",
|
|
@@ -676,7 +677,7 @@ This file contains the resource configuration for TDK.
|
|
|
676
677
|
const languageFiles = backendLanguage?.createFiles?.(resourceName);
|
|
677
678
|
if (languageFiles) {
|
|
678
679
|
const owned = new Set(languageFiles.map((file) => file.filename));
|
|
679
|
-
const sharedTasks = tasks.filter((task) => task.filename ===
|
|
680
|
+
const sharedTasks = tasks.filter((task) => task.filename === SERVICE_JSON ||
|
|
680
681
|
!(owned.has(task.filename) ||
|
|
681
682
|
task.filename === "Dockerfile" ||
|
|
682
683
|
task.filename === "src/index.ts" ||
|
|
@@ -715,7 +716,7 @@ This file contains the resource configuration for TDK.
|
|
|
715
716
|
writeFilesWithProgress(migratorPath, [
|
|
716
717
|
{
|
|
717
718
|
type: "json",
|
|
718
|
-
filename:
|
|
719
|
+
filename: SERVICE_JSON,
|
|
719
720
|
content: migratorConfig,
|
|
720
721
|
description: "Generating Prisma migrator manifest",
|
|
721
722
|
emoji: "🗃️",
|
|
@@ -6,6 +6,7 @@ import { fileURLToPath } from "node:url";
|
|
|
6
6
|
import Handlebars from "handlebars";
|
|
7
7
|
import { PLATFORM_STANDARDS } from "../config/platform-standards.js";
|
|
8
8
|
import { writeTextFileAtomic } from "../utils/atomic-write.js";
|
|
9
|
+
import { DATABASE_MANAGEMENT_FEATURE, TILTIGNORE_FILE } from "../utils/constants.js";
|
|
9
10
|
import { writeTextFile } from "../utils/file-helpers.js";
|
|
10
11
|
import { assertTdkGeneratedPath } from "../utils/generated-paths.js";
|
|
11
12
|
import { DEFAULT_ALWAYS_ENABLED_INFRA } from "../utils/project-config-defaults.js";
|
|
@@ -52,7 +53,7 @@ let embeddedTemplatesCache = null;
|
|
|
52
53
|
function getEmbeddedTemplates() {
|
|
53
54
|
if (!embeddedTemplatesCache) {
|
|
54
55
|
embeddedTemplatesCache = {
|
|
55
|
-
|
|
56
|
+
[TILTIGNORE_FILE]: loadTemplate(".tiltignore.hbs"),
|
|
56
57
|
"TILT_RESOURCE_DEFAULTS.star": loadTemplate("TILT_RESOURCE_DEFAULTS.star.hbs"),
|
|
57
58
|
"TILT_TECH_STACK.star": loadTemplate("TILT_TECH_STACK.star.hbs"),
|
|
58
59
|
Tiltfile: loadTemplate("Tiltfile.hbs"),
|
|
@@ -66,6 +67,7 @@ function getEmbeddedTemplates() {
|
|
|
66
67
|
const RESOURCE_DESCRIPTIONS = {
|
|
67
68
|
proxy: "Traefik reverse proxy",
|
|
68
69
|
verdaccio: "Private npm registry",
|
|
70
|
+
// This key is emitted into spec.master, so it must stay the literal stack id.
|
|
69
71
|
"database-management": "PostgreSQL database",
|
|
70
72
|
};
|
|
71
73
|
const INFRA_DESCRIPTIONS = {
|
|
@@ -199,6 +201,7 @@ export class TemplateEngine {
|
|
|
199
201
|
"TILT_RESOURCE_DEFAULTS.star": this.generateServiceDefaults(context),
|
|
200
202
|
"spec.master": this.generateSpecMaster(context),
|
|
201
203
|
Tiltfile: this.generateTiltfile(context),
|
|
204
|
+
// Key must stay the literal: Record<GeneratedFileName, ...> requires it.
|
|
202
205
|
".tiltignore": this.generateTiltIgnore(context),
|
|
203
206
|
};
|
|
204
207
|
}
|
|
@@ -460,16 +463,16 @@ export async function generateMasterConfigs(projectRoot, options = {}) {
|
|
|
460
463
|
writeTextFileAtomic(filePath, content);
|
|
461
464
|
console.log(`✓ Generated: .tdk/.tdk-out/${filename}`);
|
|
462
465
|
}
|
|
463
|
-
if (isStackFeatureEnabledInStacks(projectConfig.phases,
|
|
464
|
-
const composePath = assertTdkGeneratedPath(projectRoot, path.join("services", "platform",
|
|
466
|
+
if (isStackFeatureEnabledInStacks(projectConfig.phases, DATABASE_MANAGEMENT_FEATURE)) {
|
|
467
|
+
const composePath = assertTdkGeneratedPath(projectRoot, path.join("services", "platform", DATABASE_MANAGEMENT_FEATURE, "docker-compose.yml"));
|
|
465
468
|
const composeDir = path.dirname(composePath);
|
|
466
469
|
fs.mkdirSync(composeDir, { recursive: true });
|
|
467
470
|
writeTextFileAtomic(composePath, generateDatabaseManagementCompose(projectConfig));
|
|
468
471
|
console.log("✓ Generated: services/platform/database-management/docker-compose.yml");
|
|
469
472
|
}
|
|
470
473
|
// Copy .tiltignore to project root so Tilt uses it
|
|
471
|
-
const tiltignoreSource = path.join(outputDir,
|
|
472
|
-
const tiltignoreTarget = assertTdkGeneratedPath(projectRoot,
|
|
474
|
+
const tiltignoreSource = path.join(outputDir, TILTIGNORE_FILE);
|
|
475
|
+
const tiltignoreTarget = assertTdkGeneratedPath(projectRoot, TILTIGNORE_FILE);
|
|
473
476
|
if (fs.existsSync(tiltignoreSource)) {
|
|
474
477
|
if (!fs.existsSync(tiltignoreTarget)) {
|
|
475
478
|
fs.copyFileSync(tiltignoreSource, tiltignoreTarget);
|
|
@@ -656,7 +659,7 @@ export function verifyMasterConfigs(projectRoot) {
|
|
|
656
659
|
}
|
|
657
660
|
// `tdk project` never overwrites a root .tiltignore the repo already has, so one
|
|
658
661
|
// without TDK's header belongs to the team: it is not drift.
|
|
659
|
-
const rootTiltIgnore = path.join(projectRoot,
|
|
662
|
+
const rootTiltIgnore = path.join(projectRoot, TILTIGNORE_FILE);
|
|
660
663
|
const rootTiltIgnoreText = fs.existsSync(rootTiltIgnore)
|
|
661
664
|
? fs.readFileSync(rootTiltIgnore, "utf-8")
|
|
662
665
|
: undefined;
|
|
@@ -664,7 +667,7 @@ export function verifyMasterConfigs(projectRoot) {
|
|
|
664
667
|
warnings.push(".tiltignore: not generated by TDK, left as is");
|
|
665
668
|
}
|
|
666
669
|
else {
|
|
667
|
-
addDrift(
|
|
670
|
+
addDrift(TILTIGNORE_FILE, rootTiltIgnoreText, expectedFiles[TILTIGNORE_FILE]);
|
|
668
671
|
}
|
|
669
672
|
for (const manifestPath of discoverServiceManifestPaths(projectRoot)) {
|
|
670
673
|
const displayPath = path.relative(projectRoot, manifestPath).split(path.sep).join("/");
|
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
// Name of the doctor check that verifies a container runtime is reachable.
|
|
2
|
+
// It is looked up by name in doctor-report.ts, so both sides must use this
|
|
3
|
+
// constant instead of a bare string literal.
|
|
4
|
+
export const CONTAINER_RUNTIME_CHECK = "Container Runtime";
|
|
5
|
+
// Resource type id for services the user brings themselves. Referenced by
|
|
6
|
+
// value in several modules, so a typo in one copy would compile and silently
|
|
7
|
+
// stop matching. `types/index.ts` keeps its own literal: it is the leaf module
|
|
8
|
+
// that defines ResourceType, so importing this file back would be circular.
|
|
9
|
+
// Typed as the literal (not the wider ResourceType union): computed keys built
|
|
10
|
+
// from it stay narrow enough to satisfy Record<ResourceType, ...>.
|
|
11
|
+
export const BRING_YOUR_OWN_TYPE = "bring-your-own";
|
|
12
|
+
// Compile-time check that the literal above really is a ResourceType member.
|
|
13
|
+
const _bringYourOwnIsResourceType = BRING_YOUR_OWN_TYPE;
|
|
14
|
+
// Stack feature id for the shared platform database.
|
|
15
|
+
export const DATABASE_MANAGEMENT_FEATURE = "database-management";
|
|
16
|
+
// Root .tiltignore written by the generator and checked by doctor drift.
|
|
17
|
+
export const TILTIGNORE_FILE = ".tiltignore";
|
|
18
|
+
// Manifest file name for a resource, used by discovery and the resource command.
|
|
19
|
+
export const SERVICE_JSON = "service.json";
|
|
1
20
|
export const MASTER_CONFIG_FILES = [
|
|
2
21
|
"TILT_TECH_STACK.star",
|
|
3
22
|
"TILT_RESOURCE_DEFAULTS.star",
|
|
@@ -11,7 +30,7 @@ export const VALID_RESOURCE_TYPES = [
|
|
|
11
30
|
"worker",
|
|
12
31
|
"migrator",
|
|
13
32
|
"mcp",
|
|
14
|
-
|
|
33
|
+
BRING_YOUR_OWN_TYPE,
|
|
15
34
|
];
|
|
16
35
|
// Frontend/library/sdk services are served statically or consumed as
|
|
17
36
|
// packages, so they never need a "start" script - only backend/worker/
|
|
@@ -25,7 +44,7 @@ export const REQUIRED_PACKAGE_SCRIPTS = {
|
|
|
25
44
|
frontend: ["dev", "build"],
|
|
26
45
|
library: ["dev", "build"],
|
|
27
46
|
sdk: ["dev", "build"],
|
|
28
|
-
|
|
47
|
+
[BRING_YOUR_OWN_TYPE]: [],
|
|
29
48
|
};
|
|
30
49
|
// Version specifiers that pin nothing at all - a fresh `bun install`/`npm install`
|
|
31
50
|
// can pull a different (possibly breaking) version on every run.
|
|
@@ -42,7 +61,7 @@ export const PORT_RANGES = {
|
|
|
42
61
|
backend: { base: 4000, min: 4000, max: 4999, range: "4000-4999" },
|
|
43
62
|
// An MCP server is an HTTP service, so it shares the backend-compatible range with bring-your-own.
|
|
44
63
|
mcp: { base: 4000, min: 4000, max: 5999, range: "4000-5999" },
|
|
45
|
-
|
|
64
|
+
[BRING_YOUR_OWN_TYPE]: { base: 4000, min: 4000, max: 5999, range: "4000-5999" },
|
|
46
65
|
worker: { base: 6000, min: 6000, max: 6999, range: "6000-6999" },
|
|
47
66
|
health: { base: 5000, min: 5000, max: 5999, range: "5000-5999" },
|
|
48
67
|
migrator: { base: 7000, min: 7000, max: 7999, range: "7000-7999" },
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { CONTAINER_RUNTIME_CHECK } from "./constants.js";
|
|
1
2
|
/** Wait for concurrent probes to finish even if one throws, so they can release resources. */
|
|
2
3
|
export async function collectDoctorChecks(machineChecks, projectChecks) {
|
|
3
4
|
const checks = [];
|
|
@@ -21,7 +22,7 @@ export async function collectDoctorChecks(machineChecks, projectChecks) {
|
|
|
21
22
|
return { checks, errors };
|
|
22
23
|
}
|
|
23
24
|
function containerRuntimeReachable(checks) {
|
|
24
|
-
const runtime = checks.find((check) => check.name ===
|
|
25
|
+
const runtime = checks.find((check) => check.name === CONTAINER_RUNTIME_CHECK);
|
|
25
26
|
return runtime ? runtime.didPass : null;
|
|
26
27
|
}
|
|
27
28
|
/** Produce the same readiness decision for human and machine consumers. */
|
|
@@ -646,7 +646,7 @@ export function orderTiltFailures(failures) {
|
|
|
646
646
|
* Best-effort root cause for runtime crashes when buildHistory has no error.
|
|
647
647
|
* Reads recent docker logs for the matching container name.
|
|
648
648
|
*/
|
|
649
|
-
export function probeContainerRuntimeError(resourceName, exec = execSync) {
|
|
649
|
+
export function probeContainerRuntimeError(resourceName, exec = execSync, projectName = getProjectName()) {
|
|
650
650
|
try {
|
|
651
651
|
const names = runHostCommand(exec, "docker", ["ps", "-a", "--format", "{{.Names}}"], "docker ps -a --format '{{.Names}}'", {
|
|
652
652
|
stdio: "pipe",
|
|
@@ -656,7 +656,12 @@ export function probeContainerRuntimeError(resourceName, exec = execSync) {
|
|
|
656
656
|
.split("\n")
|
|
657
657
|
.map((line) => line.trim())
|
|
658
658
|
.filter(Boolean);
|
|
659
|
+
// Compose names the container <project>-<stack>-autogenerated-<resource>-1. Prefer this project's
|
|
660
|
+
// container: another TDK project can run a stack with the same name on the same Docker daemon.
|
|
661
|
+
const composeSuffix = `-autogenerated-${resourceName}-1`;
|
|
659
662
|
const match = names.find((name) => name === resourceName) ??
|
|
663
|
+
names.find((name) => name.startsWith(`${projectName}-`) && name.endsWith(composeSuffix)) ??
|
|
664
|
+
names.find((name) => name.endsWith(composeSuffix)) ??
|
|
660
665
|
names.find((name) => name.includes(resourceName));
|
|
661
666
|
if (!match)
|
|
662
667
|
return null;
|
|
@@ -4,6 +4,7 @@ import { execFileSync, execSync } from "node:child_process";
|
|
|
4
4
|
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
5
5
|
import { basename, dirname, join, relative } from "node:path";
|
|
6
6
|
import { BACKEND_LANGUAGES } from "../backend-languages/registry.js";
|
|
7
|
+
import { BRING_YOUR_OWN_TYPE, DATABASE_MANAGEMENT_FEATURE } from "./constants.js";
|
|
7
8
|
import { execAsync } from "./exec-async.js";
|
|
8
9
|
import { formatCount } from "./formatting.js";
|
|
9
10
|
import { findProjectRoot } from "./paths.js";
|
|
@@ -123,7 +124,7 @@ export function checkPrismaConsistency(projectRoot = findProjectRoot() ?? proces
|
|
|
123
124
|
findings.push(...prismaConfigFindings(resource.path).map((finding) => `${resource.name}: ${finding}`));
|
|
124
125
|
const appType = resource.config?.appType ?? resource.type;
|
|
125
126
|
if (appType === "migrator") {
|
|
126
|
-
const postgresDependency = resource.config?.dependsOn?.some((name) => name === "postgres" || name ===
|
|
127
|
+
const postgresDependency = resource.config?.dependsOn?.some((name) => name === "postgres" || name === DATABASE_MANAGEMENT_FEATURE);
|
|
127
128
|
if (!postgresDependency)
|
|
128
129
|
findings.push(`${resource.name}: migrator must depend on postgres or database-management`);
|
|
129
130
|
}
|
|
@@ -182,7 +183,7 @@ function ownsRuntimeFiles(resource) {
|
|
|
182
183
|
* (or its `buildContext`), or pulls `image`, and never generates the golden-layer Dockerfile that COPYs a package.json.
|
|
183
184
|
*/
|
|
184
185
|
function isBringYourOwn(resource) {
|
|
185
|
-
return (resource.config?.appType ?? resource.type) ===
|
|
186
|
+
return (resource.config?.appType ?? resource.type) === BRING_YOUR_OWN_TYPE;
|
|
186
187
|
}
|
|
187
188
|
export function checkResourcePackageJson(projectRoot = findProjectRoot() ?? process.cwd()) {
|
|
188
189
|
const missing = discoverResourcesFromRoot(projectRoot)
|
|
@@ -236,7 +237,7 @@ function isRoutedToPort(resource) {
|
|
|
236
237
|
const appType = resource.config?.appType;
|
|
237
238
|
if (isApiServiceType(appType))
|
|
238
239
|
return true;
|
|
239
|
-
return appType ===
|
|
240
|
+
return appType === BRING_YOUR_OWN_TYPE && resource.config?.exposeViaProxy !== false;
|
|
240
241
|
}
|
|
241
242
|
/**
|
|
242
243
|
* Two routed resources on one `port`. This is a warning, not a failure: the engine publishes no service
|
|
@@ -343,7 +344,7 @@ export function checkSharedStackRoutes(projectRoot = findProjectRoot() ?? proces
|
|
|
343
344
|
for (const resource of discoverResourcesFromRoot(projectRoot)) {
|
|
344
345
|
const config = resource.config;
|
|
345
346
|
const routable = isApiServiceType(config?.appType) ||
|
|
346
|
-
(config?.appType ===
|
|
347
|
+
(config?.appType === BRING_YOUR_OWN_TYPE && config.exposeViaProxy !== false);
|
|
347
348
|
if (!routable)
|
|
348
349
|
continue;
|
|
349
350
|
const directory = dirname(resource.path);
|
|
@@ -2,13 +2,14 @@
|
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
import { existsSync, lstatSync } from "node:fs";
|
|
4
4
|
import { isAbsolute, normalize, relative, sep } from "node:path";
|
|
5
|
+
import { TILTIGNORE_FILE } from "./constants.js";
|
|
5
6
|
/** Reviewed TDK-owned destinations written by `tdk config regenerate` or Tilt generation. */
|
|
6
7
|
export const TDK_GENERATED_PATH_ALLOWLIST = [
|
|
7
8
|
".autogenerated/**",
|
|
8
9
|
".tdk/.tdk-out/**",
|
|
9
10
|
"Tiltfile",
|
|
10
11
|
"TILT_*.star",
|
|
11
|
-
|
|
12
|
+
TILTIGNORE_FILE,
|
|
12
13
|
"package.json (create only)",
|
|
13
14
|
"shared-platform-engineering/docker-templates/**",
|
|
14
15
|
"services/platform/database-management/docker-compose.yml",
|
|
@@ -29,7 +30,7 @@ export function isTdkGeneratedPath(path) {
|
|
|
29
30
|
}
|
|
30
31
|
return (normalized === "Tiltfile" ||
|
|
31
32
|
/^TILT_[^/]+\.star$/.test(normalized) ||
|
|
32
|
-
normalized ===
|
|
33
|
+
normalized === TILTIGNORE_FILE ||
|
|
33
34
|
normalized === "package.json" ||
|
|
34
35
|
normalized === "services/platform/database-management/docker-compose.yml");
|
|
35
36
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Copyright (c) 2026 TDK Landscape contributors
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
|
+
import { DATABASE_MANAGEMENT_FEATURE } from "./constants.js";
|
|
3
4
|
/**
|
|
4
5
|
* Defaults shared by project generation and CLI predicates.
|
|
5
6
|
*
|
|
@@ -13,5 +14,5 @@
|
|
|
13
14
|
* they report Postgres-will-start while the engine does not start it (or
|
|
14
15
|
* the reverse). See shared-platform-postgres.ts.
|
|
15
16
|
*/
|
|
16
|
-
export const DEFAULT_ALWAYS_ENABLED_INFRA = [
|
|
17
|
+
export const DEFAULT_ALWAYS_ENABLED_INFRA = [DATABASE_MANAGEMENT_FEATURE, "proxy"];
|
|
17
18
|
//# sourceMappingURL=project-config-defaults.js.map
|
|
@@ -1,14 +1,6 @@
|
|
|
1
1
|
// Copyright (c) 2026 TDK Landscape contributors
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
|
-
|
|
4
|
-
* Project-Level Features
|
|
5
|
-
*
|
|
6
|
-
* These are infrastructure and optional services enabled at the project level.
|
|
7
|
-
* They are available to all resources and are configured in project.json stacks.
|
|
8
|
-
*
|
|
9
|
-
* Default: All CORE features enabled in pre_alpha stack
|
|
10
|
-
* Optional/premium: disabled by default (can be enabled in optional_infra)
|
|
11
|
-
*/
|
|
3
|
+
import { DATABASE_MANAGEMENT_FEATURE } from "./constants.js";
|
|
12
4
|
export const PROJECT_FEATURES = {
|
|
13
5
|
// ============================================================================
|
|
14
6
|
// CORE INFRASTRUCTURE (always enabled in pre_alpha)
|
|
@@ -33,8 +25,8 @@ export const PROJECT_FEATURES = {
|
|
|
33
25
|
enabled_by_default: false,
|
|
34
26
|
dependsOn: [],
|
|
35
27
|
},
|
|
36
|
-
|
|
37
|
-
name:
|
|
28
|
+
[DATABASE_MANAGEMENT_FEATURE]: {
|
|
29
|
+
name: DATABASE_MANAGEMENT_FEATURE,
|
|
38
30
|
description: "PostgreSQL database for data persistence and SQL operations",
|
|
39
31
|
category: "core",
|
|
40
32
|
phase: "pre_alpha",
|
|
@@ -50,7 +42,7 @@ export const PROJECT_FEATURES = {
|
|
|
50
42
|
category: "optional",
|
|
51
43
|
phase: "pre_alpha",
|
|
52
44
|
enabled_by_default: false,
|
|
53
|
-
dependsOn: [
|
|
45
|
+
dependsOn: [DATABASE_MANAGEMENT_FEATURE],
|
|
54
46
|
},
|
|
55
47
|
elk: {
|
|
56
48
|
name: "elk",
|
|
@@ -66,7 +58,7 @@ export const PROJECT_FEATURES = {
|
|
|
66
58
|
category: "optional",
|
|
67
59
|
phase: "pre_alpha",
|
|
68
60
|
enabled_by_default: false,
|
|
69
|
-
dependsOn: [
|
|
61
|
+
dependsOn: [DATABASE_MANAGEMENT_FEATURE],
|
|
70
62
|
},
|
|
71
63
|
"golden-image": {
|
|
72
64
|
name: "golden-image",
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* Frontend services: api-client, env-config, api-index (enabled by default)
|
|
10
10
|
* Backend services: prisma (enabled by default)
|
|
11
11
|
*/
|
|
12
|
+
import { BRING_YOUR_OWN_TYPE } from "./constants.js";
|
|
12
13
|
export const RESOURCE_FEATURES = {
|
|
13
14
|
// ============================================================================
|
|
14
15
|
// FRONTEND GENERATORS
|
|
@@ -161,7 +162,7 @@ export const RESOURCE_FEATURES = {
|
|
|
161
162
|
*/
|
|
162
163
|
export function getDefaultFeaturesForResourceType(appType) {
|
|
163
164
|
// bring-your-own doesn't generate application code, so no features
|
|
164
|
-
if (appType ===
|
|
165
|
+
if (appType === BRING_YOUR_OWN_TYPE) {
|
|
165
166
|
return [];
|
|
166
167
|
}
|
|
167
168
|
const defaults = [];
|
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
4
4
|
import { dirname, join } from "node:path";
|
|
5
5
|
import { Cache, createCacheValidator } from "./cache.js";
|
|
6
|
-
import { AUTOGENERATED_FILE_PATTERNS, SKIP_DIRECTORIES } from "./constants.js";
|
|
6
|
+
import { AUTOGENERATED_FILE_PATTERNS, SERVICE_JSON, SKIP_DIRECTORIES } from "./constants.js";
|
|
7
7
|
import { errorFactories } from "./errors.js";
|
|
8
8
|
import { formatCount } from "./formatting.js";
|
|
9
9
|
import { findProjectRoot } from "./paths.js";
|
|
10
10
|
import { getInvalidServiceRoutePathFields, validateGeneratedStringFields, validateServiceManifest, } from "./service-manifest.js";
|
|
11
11
|
import { deriveStackStatus } from "./stack-status.js";
|
|
12
12
|
import { includes } from "./validation.js";
|
|
13
|
-
const RESOURCE_JSON_FILENAME =
|
|
13
|
+
const RESOURCE_JSON_FILENAME = SERVICE_JSON;
|
|
14
14
|
function findServiceJsonFiles(dir, maxDepth = 5, currentDepth = 0) {
|
|
15
15
|
const results = [];
|
|
16
16
|
if (currentDepth > maxDepth) {
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
import { existsSync, readFileSync } from "node:fs";
|
|
4
4
|
import { join } from "node:path";
|
|
5
|
+
import { DATABASE_MANAGEMENT_FEATURE } from "./constants.js";
|
|
5
6
|
import { DEFAULT_ALWAYS_ENABLED_INFRA } from "./project-config-defaults.js";
|
|
6
7
|
import { discoverResourcesFromRoot } from "./services.js";
|
|
7
8
|
/**
|
|
@@ -18,7 +19,7 @@ import { discoverResourcesFromRoot } from "./services.js";
|
|
|
18
19
|
*
|
|
19
20
|
* Spec: openspec/changes/dependson-starts-platform-postgres
|
|
20
21
|
*/
|
|
21
|
-
export const POSTGRES_DEPENDENCY_NAMES = ["postgres",
|
|
22
|
+
export const POSTGRES_DEPENDENCY_NAMES = ["postgres", DATABASE_MANAGEMENT_FEATURE];
|
|
22
23
|
const PHASE_KEYS = ["pre_alpha", "alpha", "beta", "out_of_scope"];
|
|
23
24
|
export function isSharedPlatformPostgresDependency(name) {
|
|
24
25
|
return POSTGRES_DEPENDENCY_NAMES.includes(name);
|
|
@@ -105,12 +106,12 @@ function projectJsonFeatureOn(projectRoot) {
|
|
|
105
106
|
const phases = (parsed.phases ?? {});
|
|
106
107
|
for (const phase of PHASE_KEYS) {
|
|
107
108
|
const stacks = phases[phase]?.enabledStacks;
|
|
108
|
-
if (Array.isArray(stacks) && stacks.includes(
|
|
109
|
+
if (Array.isArray(stacks) && stacks.includes(DATABASE_MANAGEMENT_FEATURE)) {
|
|
109
110
|
return true;
|
|
110
111
|
}
|
|
111
112
|
}
|
|
112
113
|
const alwaysEnabled = parsed.always_enabled_infra ?? DEFAULT_ALWAYS_ENABLED_INFRA;
|
|
113
|
-
return Array.isArray(alwaysEnabled) && alwaysEnabled.includes(
|
|
114
|
+
return Array.isArray(alwaysEnabled) && alwaysEnabled.includes(DATABASE_MANAGEMENT_FEATURE);
|
|
114
115
|
}
|
|
115
116
|
/**
|
|
116
117
|
* True when database-management is on for this project from project.json
|
|
@@ -120,7 +121,7 @@ export function databaseManagementEnabled(projectRoot) {
|
|
|
120
121
|
if (projectJsonFeatureOn(projectRoot))
|
|
121
122
|
return true;
|
|
122
123
|
const fromTilt = tiltfileAlwaysEnabledInfra(projectRoot);
|
|
123
|
-
if (Array.isArray(fromTilt) && fromTilt.includes(
|
|
124
|
+
if (Array.isArray(fromTilt) && fromTilt.includes(DATABASE_MANAGEMENT_FEATURE))
|
|
124
125
|
return true;
|
|
125
126
|
if (specMasterHasDatabaseManagement(projectRoot) === true)
|
|
126
127
|
return true;
|
|
@@ -172,7 +173,7 @@ function knownDependsonNames(projectRoot) {
|
|
|
172
173
|
export function evaluateSharedPlatformPostgres(projectRoot) {
|
|
173
174
|
const featureOnFromProjectJson = projectJsonFeatureOn(projectRoot);
|
|
174
175
|
const fromTilt = tiltfileAlwaysEnabledInfra(projectRoot);
|
|
175
|
-
const featureOnFromTiltfile = Array.isArray(fromTilt) && fromTilt.includes(
|
|
176
|
+
const featureOnFromTiltfile = Array.isArray(fromTilt) && fromTilt.includes(DATABASE_MANAGEMENT_FEATURE);
|
|
176
177
|
const featureOnFromSpecMaster = specMasterHasDatabaseManagement(projectRoot) === true;
|
|
177
178
|
const featureOn = featureOnFromProjectJson || featureOnFromTiltfile || featureOnFromSpecMaster;
|
|
178
179
|
const focusWouldEnableDatabaseManagement = !featureOnFromProjectJson && generatedDefaultFocusEnablesDatabaseManagement(projectRoot);
|
package/cli/dist/utils/smoke.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
import { copyFileSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { join } from "node:path";
|
|
5
|
+
import { BRING_YOUR_OWN_TYPE } from "./constants.js";
|
|
5
6
|
import { isApiServiceType } from "./resource-kind.js";
|
|
6
7
|
import { resolveServicePath, resolveSubdomainBases } from "./service-urls.js";
|
|
7
8
|
export const DEFAULT_SMOKE_TIMEOUT_SECONDS = 60;
|
|
@@ -87,14 +88,14 @@ export function buildSmokePlans(resources, ingressPort) {
|
|
|
87
88
|
if (!config?.smoke)
|
|
88
89
|
continue;
|
|
89
90
|
const appType = config.appType;
|
|
90
|
-
const isApi = isApiServiceType(appType) || appType ===
|
|
91
|
+
const isApi = isApiServiceType(appType) || appType === BRING_YOUR_OWN_TYPE;
|
|
91
92
|
if (!isApi && appType !== "frontend")
|
|
92
93
|
continue;
|
|
93
94
|
if (config.exposeViaProxy === false)
|
|
94
95
|
continue;
|
|
95
96
|
const base = isApi ? apiBase : appBase;
|
|
96
97
|
// The container healthcheck path (default /health for APIs) is the one request that is safe to repeat while the route comes up.
|
|
97
|
-
const readyPath = config.healthCheckPath || (isApi && appType !==
|
|
98
|
+
const readyPath = config.healthCheckPath || (isApi && appType !== BRING_YOUR_OWN_TYPE ? "/health" : undefined);
|
|
98
99
|
plans.push({
|
|
99
100
|
name: resource.name,
|
|
100
101
|
baseUrl: `${base}${resolveServicePath(resource).replace(/\/+$/, "")}`,
|