@tdk-landscape/tdk-cli-core 1.3.117 → 1.3.118
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 +5 -0
- package/cli/dist/commands/config.js +43 -5
- package/cli/dist/commands/doctor.js +4 -1
- package/cli/dist/commands/resource.js +103 -3
- package/cli/dist/commands/ui.js +108 -44
- package/cli/dist/components/BaseTooltip.js +3 -2
- package/cli/dist/components/DetailPanel.js +19 -5
- package/cli/dist/components/FileTree.js +20 -17
- package/cli/dist/components/ResourceSelectInput.js +16 -11
- package/cli/dist/components/ResourceTable.js +12 -5
- package/cli/dist/components/TUIHeader.js +25 -0
- package/cli/dist/components/TabBar.js +12 -7
- package/cli/dist/components/index.js +1 -0
- package/cli/dist/components/ui-theme.js +114 -0
- package/cli/dist/generator/template-engine.js +2 -1
- package/cli/dist/utils/doctor-wiring.js +192 -0
- package/cli/dist/utils/project-config-defaults.js +15 -0
- package/cli/dist/utils/shared-platform-postgres.js +211 -0
- package/cli/package.json +1 -1
- package/cli/templates/Tiltfile.hbs +5 -1
- package/engine/topologies/tilt/common/utils.star +2 -0
- package/engine/topologies/tilt/generators/validators.star +6 -1
- package/engine/topologies/tilt/manifest/integration.star +11 -4
- package/engine/topologies/tilt/manifest/validator.star +4 -0
- package/engine/topologies/tilt/resources/infra-loader.star +68 -13
- package/engine/topologies/tilt/resources/orchestrator/apply_compose_resource_registration.star +135 -8
- package/engine/topologies/tilt/resources/shared-platform-postgres.star +28 -0
- package/package.json +1 -1
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { DEFAULT_ALWAYS_ENABLED_INFRA } from "./project-config-defaults.js";
|
|
4
|
+
import { discoverResourcesFromRoot } from "./services.js";
|
|
5
|
+
/**
|
|
6
|
+
* Shared platform Postgres start predicate.
|
|
7
|
+
*
|
|
8
|
+
* Both names mean the one shared platform Postgres Tilt resource `postgres`.
|
|
9
|
+
* The engine start path is selection-bounded (tdk up --only); verify/doctor
|
|
10
|
+
* evaluate the project resource set they already inspect (no tdk up filter).
|
|
11
|
+
* Both surfaces use this same predicate shape over their own resource set.
|
|
12
|
+
*
|
|
13
|
+
* Feature state is read from project.json **and** the generated Tiltfile /
|
|
14
|
+
* spec.master when present, because a stock `tdk up` focus pass expands
|
|
15
|
+
* CORE_INFRA → database-management even when project.json omits it.
|
|
16
|
+
*
|
|
17
|
+
* Spec: openspec/changes/dependson-starts-platform-postgres
|
|
18
|
+
*/
|
|
19
|
+
export const POSTGRES_DEPENDENCY_NAMES = ["postgres", "database-management"];
|
|
20
|
+
const PHASE_KEYS = ["pre_alpha", "alpha", "beta", "out_of_scope"];
|
|
21
|
+
export function isSharedPlatformPostgresDependency(name) {
|
|
22
|
+
return POSTGRES_DEPENDENCY_NAMES.includes(name);
|
|
23
|
+
}
|
|
24
|
+
function readProjectJson(projectRoot) {
|
|
25
|
+
try {
|
|
26
|
+
return JSON.parse(readFileSync(join(projectRoot, ".tdk", "project.json"), "utf-8"));
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function readTextIfExists(path) {
|
|
33
|
+
try {
|
|
34
|
+
return existsSync(path) ? readFileSync(path, "utf-8") : undefined;
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function parseStarlarkStringList(source, name) {
|
|
41
|
+
const re = new RegExp(`${name}\\s*=\\s*\\[([^\\]]*)\\]`);
|
|
42
|
+
const m = source.match(re);
|
|
43
|
+
if (!m)
|
|
44
|
+
return undefined;
|
|
45
|
+
return [...m[1].matchAll(/"([^"]+)"|'([^']+)'/g)].map((x) => x[1] ?? x[2] ?? "");
|
|
46
|
+
}
|
|
47
|
+
function tiltfileAlwaysEnabledInfra(projectRoot) {
|
|
48
|
+
const path = join(projectRoot, ".tdk", ".tdk-out", "Tiltfile");
|
|
49
|
+
const text = readTextIfExists(path);
|
|
50
|
+
if (!text)
|
|
51
|
+
return undefined;
|
|
52
|
+
return parseStarlarkStringList(text, "ALWAYS_ENABLED_INFRA");
|
|
53
|
+
}
|
|
54
|
+
function specMasterHasDatabaseManagement(projectRoot) {
|
|
55
|
+
const path = join(projectRoot, ".tdk", ".tdk-out", "spec.master");
|
|
56
|
+
const text = readTextIfExists(path);
|
|
57
|
+
if (text === undefined)
|
|
58
|
+
return undefined;
|
|
59
|
+
// PRE_ALPHA/DEFAULTS style: "database-management": True
|
|
60
|
+
return /"database-management"\s*:\s*True/.test(text);
|
|
61
|
+
}
|
|
62
|
+
function generatedDefaultFocusEnablesDatabaseManagement(projectRoot) {
|
|
63
|
+
const output = join(projectRoot, ".tdk", ".tdk-out");
|
|
64
|
+
const tiltfile = readTextIfExists(join(output, "Tiltfile"));
|
|
65
|
+
const spec = readTextIfExists(join(output, "spec.master"));
|
|
66
|
+
const config = readTextIfExists(join(output, "tdk-cli-ext", "discovery", "config.star"));
|
|
67
|
+
const profiles = readTextIfExists(join(output, "tdk-cli-ext", "engine", "topologies", "tilt", "config", "profiles.star"));
|
|
68
|
+
if (!tiltfile || !spec || !config || !profiles)
|
|
69
|
+
return false;
|
|
70
|
+
const preAlpha = spec.match(/PRE_ALPHA_RESOURCES\s*=\s*\{([^}]*)\}/)?.[1];
|
|
71
|
+
const coreInfra = parseStarlarkStringList(config, "CORE_INFRA");
|
|
72
|
+
return (/FOCUS_MODE\s*,\s*FOCUS_ENABLED_ALL\s*,\s*FOCUS_ENABLED_RESOURCES\s*=\s*Config\.apply_focus\(cfg\)/.test(tiltfile) &&
|
|
73
|
+
/if FOCUS_MODE and FOCUS_ENABLED_ALL\s*:/.test(tiltfile) &&
|
|
74
|
+
/"[^"]+"\s*:\s*True|'[^']+'\s*:\s*True/.test(preAlpha ?? "") &&
|
|
75
|
+
(coreInfra?.includes("postgres") ?? false) &&
|
|
76
|
+
/["']postgres["']\s*:\s*["']database-management["']/.test(config) &&
|
|
77
|
+
/for infra in CORE_INFRA_EXPORT\s*:/.test(profiles) &&
|
|
78
|
+
/needed\[INFRA_STACK_MAP_EXPORT\[infra\]\]\s*=\s*True/.test(profiles));
|
|
79
|
+
}
|
|
80
|
+
function projectJsonFeatureOn(projectRoot) {
|
|
81
|
+
const parsed = readProjectJson(projectRoot);
|
|
82
|
+
if (!parsed)
|
|
83
|
+
return false;
|
|
84
|
+
const phases = (parsed.phases ?? {});
|
|
85
|
+
for (const phase of PHASE_KEYS) {
|
|
86
|
+
const stacks = phases[phase]?.enabledStacks;
|
|
87
|
+
if (Array.isArray(stacks) && stacks.includes("database-management")) {
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
const alwaysEnabled = parsed.always_enabled_infra ?? DEFAULT_ALWAYS_ENABLED_INFRA;
|
|
92
|
+
return Array.isArray(alwaysEnabled) && alwaysEnabled.includes("database-management");
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* True when database-management is on for this project from project.json
|
|
96
|
+
* **or** the generated Tiltfile/spec.master when those files exist.
|
|
97
|
+
*/
|
|
98
|
+
export function databaseManagementEnabled(projectRoot) {
|
|
99
|
+
if (projectJsonFeatureOn(projectRoot))
|
|
100
|
+
return true;
|
|
101
|
+
const fromTilt = tiltfileAlwaysEnabledInfra(projectRoot);
|
|
102
|
+
if (Array.isArray(fromTilt) && fromTilt.includes("database-management"))
|
|
103
|
+
return true;
|
|
104
|
+
if (specMasterHasDatabaseManagement(projectRoot) === true)
|
|
105
|
+
return true;
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
/** Known names for unknown-dependsOn detection: discovered services/stacks + project stacks/infra. */
|
|
109
|
+
function knownDependsonNames(projectRoot) {
|
|
110
|
+
const known = new Set();
|
|
111
|
+
for (const name of POSTGRES_DEPENDENCY_NAMES)
|
|
112
|
+
known.add(name);
|
|
113
|
+
for (const resource of discoverResourcesFromRoot(projectRoot)) {
|
|
114
|
+
known.add(resource.name);
|
|
115
|
+
if (resource.stack)
|
|
116
|
+
known.add(resource.stack);
|
|
117
|
+
}
|
|
118
|
+
const parsed = readProjectJson(projectRoot);
|
|
119
|
+
if (parsed) {
|
|
120
|
+
const phases = (parsed.phases ?? {});
|
|
121
|
+
for (const phase of PHASE_KEYS) {
|
|
122
|
+
const stacks = phases[phase]?.enabledStacks;
|
|
123
|
+
if (Array.isArray(stacks)) {
|
|
124
|
+
for (const stack of stacks)
|
|
125
|
+
known.add(stack);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
const alwaysEnabled = parsed.always_enabled_infra ?? DEFAULT_ALWAYS_ENABLED_INFRA;
|
|
129
|
+
if (Array.isArray(alwaysEnabled)) {
|
|
130
|
+
for (const entry of alwaysEnabled)
|
|
131
|
+
known.add(entry);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
const fromTilt = tiltfileAlwaysEnabledInfra(projectRoot);
|
|
135
|
+
if (Array.isArray(fromTilt)) {
|
|
136
|
+
for (const entry of fromTilt)
|
|
137
|
+
known.add(entry);
|
|
138
|
+
}
|
|
139
|
+
return known;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Evaluate whether shared platform Postgres will start for this project.
|
|
143
|
+
*
|
|
144
|
+
* willStart = featureOn OR any inspected project resource depends on either name.
|
|
145
|
+
* Resource set is project scope (discoverResourcesFromRoot), NOT tdk up --only.
|
|
146
|
+
*
|
|
147
|
+
* Also reports focusWouldEnableDatabaseManagement so doctor/verify can warn that
|
|
148
|
+
* default focus expands CORE_INFRA → database-management even when project.json
|
|
149
|
+
* says the feature is off (the 7.1 gap).
|
|
150
|
+
*/
|
|
151
|
+
export function evaluateSharedPlatformPostgres(projectRoot) {
|
|
152
|
+
const featureOnFromProjectJson = projectJsonFeatureOn(projectRoot);
|
|
153
|
+
const fromTilt = tiltfileAlwaysEnabledInfra(projectRoot);
|
|
154
|
+
const featureOnFromTiltfile = Array.isArray(fromTilt) && fromTilt.includes("database-management");
|
|
155
|
+
const featureOnFromSpecMaster = specMasterHasDatabaseManagement(projectRoot) === true;
|
|
156
|
+
const featureOn = featureOnFromProjectJson || featureOnFromTiltfile || featureOnFromSpecMaster;
|
|
157
|
+
const focusWouldEnableDatabaseManagement = !featureOnFromProjectJson && generatedDefaultFocusEnablesDatabaseManagement(projectRoot);
|
|
158
|
+
const known = knownDependsonNames(projectRoot);
|
|
159
|
+
const dependsOnUsers = [];
|
|
160
|
+
const unknownDependsOnNames = [];
|
|
161
|
+
for (const resource of discoverResourcesFromRoot(projectRoot)) {
|
|
162
|
+
const deps = resource.config?.dependsOn ?? [];
|
|
163
|
+
let usesSharedPostgres = false;
|
|
164
|
+
for (const dep of deps) {
|
|
165
|
+
if (isSharedPlatformPostgresDependency(dep)) {
|
|
166
|
+
usesSharedPostgres = true;
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
if (!known.has(dep)) {
|
|
170
|
+
unknownDependsOnNames.push({ resource: resource.name, name: dep });
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
if (usesSharedPostgres) {
|
|
174
|
+
dependsOnUsers.push(resource.name);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
const willStart = featureOn || dependsOnUsers.length > 0;
|
|
178
|
+
const reason = featureOn
|
|
179
|
+
? "feature"
|
|
180
|
+
: dependsOnUsers.length > 0
|
|
181
|
+
? "dependsOn"
|
|
182
|
+
: "none";
|
|
183
|
+
return {
|
|
184
|
+
featureOn,
|
|
185
|
+
featureOnFromProjectJson,
|
|
186
|
+
featureOnFromTiltfile,
|
|
187
|
+
featureOnFromSpecMaster,
|
|
188
|
+
focusWouldEnableDatabaseManagement,
|
|
189
|
+
dependsOnUsers,
|
|
190
|
+
willStart,
|
|
191
|
+
reason,
|
|
192
|
+
unknownDependsOnNames,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
/** Human-readable will-start / will-not-start sentence shared by doctor + verify. */
|
|
196
|
+
export function sharedPlatformPostgresMessage(evaluation) {
|
|
197
|
+
const projectScopeNote = " (project resource set; tdk up may select a subset and not start Postgres)";
|
|
198
|
+
if (!evaluation.willStart) {
|
|
199
|
+
const focusNote = evaluation.focusWouldEnableDatabaseManagement
|
|
200
|
+
? " Note: default focus/CORE_INFRA expansion would enable database-management on a typical tdk up even when project.json lists it off."
|
|
201
|
+
: "";
|
|
202
|
+
return `Shared platform Postgres will not start for this project.json/generated Tiltfile state (no database-management feature, no selected dependsOn).${focusNote}`;
|
|
203
|
+
}
|
|
204
|
+
const why = evaluation.reason === "feature"
|
|
205
|
+
? `the database-management feature is enabled${evaluation.featureOnFromTiltfile || evaluation.featureOnFromSpecMaster
|
|
206
|
+
? " (generated Tiltfile/spec.master)"
|
|
207
|
+
: ""}`
|
|
208
|
+
: `resource(s) ${evaluation.dependsOnUsers.join(", ")} depend on postgres/database-management`;
|
|
209
|
+
return `Shared platform Postgres will start because ${why}${projectScopeNote}`;
|
|
210
|
+
}
|
|
211
|
+
//# sourceMappingURL=shared-platform-postgres.js.map
|
package/cli/package.json
CHANGED
|
@@ -225,6 +225,8 @@ TILT_CONTEXT = {
|
|
|
225
225
|
'project_root': PROJECT_ROOT,
|
|
226
226
|
'all_services': _app_resources_list,
|
|
227
227
|
'golden_image_resource': GOLDEN_IMAGE_RESOURCE,
|
|
228
|
+
'focus_mode': FOCUS_MODE,
|
|
229
|
+
'focus_enabled_resources': FOCUS_ENABLED_RESOURCES,
|
|
228
230
|
}
|
|
229
231
|
|
|
230
232
|
print("🚀 Creating Tilt resources for {} services...".format(len(_app_resources_list)))
|
|
@@ -314,7 +316,9 @@ if FOCUS_MODE and FOCUS_ENABLED_RESOURCES:
|
|
|
314
316
|
# local mail catcher standing in for a managed email provider. Every
|
|
315
317
|
# service it declares is loaded via docker_compose() in infra-loader.star,
|
|
316
318
|
# so it must be enabled here too, or it is silently disabled the same way.
|
|
317
|
-
_ALWAYS_ENABLED_INFRA_RESOURCES = ['init-networks', '
|
|
319
|
+
_ALWAYS_ENABLED_INFRA_RESOURCES = ['init-networks', 'traefik', 'golden-layers-build']
|
|
320
|
+
if should_enable('database-management') or TILT_CONTEXT.get('_shared_platform_postgres_force_started', False):
|
|
321
|
+
_ALWAYS_ENABLED_INFRA_RESOURCES.append('postgres')
|
|
318
322
|
_messaging_compose_path = PROJECT_ROOT + '/services/platform/messaging/docker-compose.yml'
|
|
319
323
|
if str(local("test -f '" + _messaging_compose_path + "' && echo yes || echo no", quiet=True, echo_off=True)).strip() == 'yes':
|
|
320
324
|
# infra-loader.star only creates these Tilt resources when this file
|
|
@@ -273,6 +273,8 @@ def _is_allowed_generated_path(file_path, project_root):
|
|
|
273
273
|
return True
|
|
274
274
|
if path.startswith('.tdk/.tdk-out/'):
|
|
275
275
|
return True
|
|
276
|
+
if path == 'services/platform/database-management/docker-compose.yml':
|
|
277
|
+
return True
|
|
276
278
|
if path.startswith('product_autogenerated_resource_manifests/') or path.startswith('product_autogenerated_services_list/'):
|
|
277
279
|
return True
|
|
278
280
|
if path == 'Tiltfile' or path == '.tiltignore':
|
|
@@ -24,6 +24,7 @@ Validators Architecture:
|
|
|
24
24
|
load("../common/utils.star", "Utils")
|
|
25
25
|
load("../../platform/docker/constants.star", "PlatformDockerConstants")
|
|
26
26
|
load("../manifest/constants.star", "GENERATED_CONFIG_FILENAMES")
|
|
27
|
+
load("../resources/shared-platform-postgres.star", "is_shared_platform_postgres_dependency")
|
|
27
28
|
|
|
28
29
|
load("../common/utils.star", "LIBRARY_ROOTS")
|
|
29
30
|
|
|
@@ -182,6 +183,10 @@ def validate_dependency_graph(resource_path, manifest, all_services):
|
|
|
182
183
|
# Check manifest dependsOn
|
|
183
184
|
internal_deps = manifest.get("dependsOn", [])
|
|
184
185
|
for dep in internal_deps:
|
|
186
|
+
# Shared platform Postgres aliases are not app services; they resolve to
|
|
187
|
+
# the Tilt resource `postgres` and must not be reported as missing.
|
|
188
|
+
if is_shared_platform_postgres_dependency(dep):
|
|
189
|
+
continue
|
|
185
190
|
dep_exists = False
|
|
186
191
|
for svc in all_services:
|
|
187
192
|
# Check service name/stack match (supports legacy 'stack' field)
|
|
@@ -196,7 +201,7 @@ def validate_dependency_graph(resource_path, manifest, all_services):
|
|
|
196
201
|
break
|
|
197
202
|
if dep_exists:
|
|
198
203
|
break
|
|
199
|
-
|
|
204
|
+
|
|
200
205
|
if not dep_exists:
|
|
201
206
|
missing_deps.append({
|
|
202
207
|
"name": dep,
|
|
@@ -13,6 +13,7 @@ load("./__init__.star", "Manifest")
|
|
|
13
13
|
load("./loader.star", "ManifestLoader")
|
|
14
14
|
load("./parser.star", "ManifestParser")
|
|
15
15
|
load("./validator.star", "ManifestValidator")
|
|
16
|
+
load("../resources/shared-platform-postgres.star", "SHARED_POSTGRES_DEPENDENCY_NAMES")
|
|
16
17
|
|
|
17
18
|
# Phase 5: Migration complete - always use new system
|
|
18
19
|
def load_and_validate_manifest(manifest_path, all_manifests=None):
|
|
@@ -290,21 +291,27 @@ def check_port_conflicts(manifests):
|
|
|
290
291
|
def check_dependencies_valid(manifests):
|
|
291
292
|
"""
|
|
292
293
|
Check if all dependencies reference valid services.
|
|
293
|
-
|
|
294
|
+
|
|
295
|
+
Shared platform Postgres names (`postgres`, `database-management`) are not
|
|
296
|
+
app services; they resolve to the Tilt resource `postgres` and are not
|
|
297
|
+
reported as missing.
|
|
298
|
+
|
|
294
299
|
Args:
|
|
295
300
|
manifests: List of manifest dicts
|
|
296
|
-
|
|
301
|
+
|
|
297
302
|
Returns:
|
|
298
303
|
List of dependency error structs
|
|
299
304
|
"""
|
|
300
305
|
errors = []
|
|
301
306
|
app_names = {m.get('appName'): m for m in manifests if m.get('appName')}
|
|
302
|
-
|
|
307
|
+
|
|
303
308
|
for manifest in manifests:
|
|
304
309
|
app_name = manifest.get('appName', 'unknown')
|
|
305
310
|
internal_deps = manifest.get('dependsOn', [])
|
|
306
|
-
|
|
311
|
+
|
|
307
312
|
for dep in internal_deps:
|
|
313
|
+
if dep in SHARED_POSTGRES_DEPENDENCY_NAMES:
|
|
314
|
+
continue
|
|
308
315
|
if dep not in app_names:
|
|
309
316
|
errors.append(ManifestErrors.new(
|
|
310
317
|
message="Service '{}' depends on non-existent service '{}'".format(app_name, dep),
|
|
@@ -25,6 +25,7 @@ load("./constants.star",
|
|
|
25
25
|
load("./schema.star", "ManifestSchema")
|
|
26
26
|
load("./errors.star", "ManifestErrors")
|
|
27
27
|
load("./parser.star", "extract_stack_from_path")
|
|
28
|
+
load("../resources/shared-platform-postgres.star", "is_shared_platform_postgres_dependency")
|
|
28
29
|
|
|
29
30
|
def validate(manifest, context=None, level='all'):
|
|
30
31
|
"""
|
|
@@ -463,6 +464,9 @@ def _validate_cross_resource(manifest, context):
|
|
|
463
464
|
|
|
464
465
|
missing_deps = []
|
|
465
466
|
for dep in internal_deps:
|
|
467
|
+
# Shared platform Postgres aliases are not app services.
|
|
468
|
+
if is_shared_platform_postgres_dependency(dep):
|
|
469
|
+
continue
|
|
466
470
|
if dep not in app_names:
|
|
467
471
|
missing_deps.append(dep)
|
|
468
472
|
|
|
@@ -92,32 +92,83 @@ volumes:
|
|
|
92
92
|
|
|
93
93
|
|
|
94
94
|
def _ensure_database_management_compose(root_prefix, write_fn):
|
|
95
|
-
"""Ensure the database-management stack feature has its compose file.
|
|
95
|
+
"""Ensure the database-management stack feature has its compose file.
|
|
96
|
+
|
|
97
|
+
write_fn is expected to be Utils.write_file_if_changed, which prepends
|
|
98
|
+
TDK_PROJECT_ROOT to relative paths — the same base as root_prefix when
|
|
99
|
+
the Tiltfile sets TDK_PROJECT_ROOT = PROJECT_ROOT. A custom write_fn that
|
|
100
|
+
does not prepend may write a different file; if the path we return is
|
|
101
|
+
absolute and still missing after the relative write, write that exact
|
|
102
|
+
path once so the exists-check and the write cannot diverge.
|
|
103
|
+
"""
|
|
96
104
|
compose_rel = "services/platform/database-management/docker-compose.yml"
|
|
97
105
|
compose_file = root_prefix + compose_rel if root_prefix else compose_rel
|
|
98
106
|
if _file_exists(compose_file):
|
|
99
107
|
return compose_file
|
|
100
108
|
if write_fn:
|
|
101
109
|
write_fn(compose_rel, _generate_database_management_compose())
|
|
110
|
+
if not _file_exists(compose_file) and compose_file.startswith('/'):
|
|
111
|
+
write_fn(compose_file, _generate_database_management_compose())
|
|
102
112
|
return compose_file
|
|
103
113
|
|
|
114
|
+
# No module-level mutable registration flag: Tilt freezes Starlark globals after
|
|
115
|
+
# module load, so writing a key on a global dict raises "cannot insert into
|
|
116
|
+
# frozen hash table". Feature-on (`_load_database_management`) and force-start
|
|
117
|
+
# (`force_start_platform_postgres`) are mutually exclusive via should_enable —
|
|
118
|
+
# the orchestrator only force-starts when database-management is off — so each
|
|
119
|
+
# Tiltfile evaluation registers `postgres` at most once on its own path.
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def _register_platform_postgres(should_enable, root_prefix="", env_file=None, write_fn=None, required=False):
|
|
123
|
+
"""Materialize the platform compose and register the shared `postgres` Tilt resource.
|
|
124
|
+
|
|
125
|
+
Does NOT load messaging, kafka, redis, nats, provision-db, or Prisma.
|
|
126
|
+
|
|
127
|
+
When `required` is true (dependsOn force-start path), a missing compose after
|
|
128
|
+
ensure is a hard failure — not a silent skip — so Tilt never waits on a
|
|
129
|
+
`postgres` resource that has no compose definition.
|
|
130
|
+
"""
|
|
131
|
+
postgres_compose = _ensure_database_management_compose(root_prefix, write_fn)
|
|
132
|
+
if _file_exists(postgres_compose):
|
|
133
|
+
print("DEBUG INFRA: Loading postgres compose from {}".format(postgres_compose))
|
|
134
|
+
_docker_compose(postgres_compose, env_file)
|
|
135
|
+
dc_resource('postgres', labels=['infra.tools'], resource_deps=['init-networks'], auto_init=True)
|
|
136
|
+
return
|
|
137
|
+
message = "Shared platform Postgres compose was not materialized at {}".format(postgres_compose)
|
|
138
|
+
if required:
|
|
139
|
+
fail(message + " (write_fn present: {}). Refusing to register a postgres Tilt resource with no compose.".format(write_fn != None))
|
|
140
|
+
print("DEBUG INFRA: Skipping postgres (compose file not found): {}".format(message))
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def force_start_platform_postgres(should_enable, root_prefix="", env_file=None, write_fn=None):
|
|
144
|
+
"""Force-start the shared platform Postgres for a run whose selected resources
|
|
145
|
+
depend on `postgres` / `database-management` while `database-management` is off.
|
|
146
|
+
|
|
147
|
+
Reuses the existing platform compose path and the existing `postgres` Tilt
|
|
148
|
+
resource — no second image, port, migrator, or Prisma resource.
|
|
149
|
+
|
|
150
|
+
The orchestrator MUST pass the same `write_fn` the feature path uses
|
|
151
|
+
(`ctx['write_file']`) so `services/platform/database-management/docker-compose.yml`
|
|
152
|
+
is materialized when missing. A failed materialize fails the run.
|
|
153
|
+
"""
|
|
154
|
+
if env_file == None:
|
|
155
|
+
candidate_env_file = root_prefix + '.env' if root_prefix else '.env'
|
|
156
|
+
env_file = candidate_env_file if _file_exists(candidate_env_file) else None
|
|
157
|
+
print("🗃️ Force-starting shared platform Postgres (selected dependsOn postgres/database-management)")
|
|
158
|
+
_register_platform_postgres(should_enable, root_prefix, env_file, write_fn, required=True)
|
|
159
|
+
|
|
160
|
+
|
|
104
161
|
def _load_database_management(should_enable, root_prefix="", env_file=None, write_fn=None):
|
|
105
162
|
"""Load database and messaging infrastructure."""
|
|
106
163
|
if not should_enable('database-management'):
|
|
107
164
|
print("DEBUG INFRA: database-management not enabled")
|
|
108
165
|
return
|
|
109
|
-
|
|
166
|
+
|
|
110
167
|
print("🗃️ Loading database management services...")
|
|
111
|
-
|
|
168
|
+
|
|
112
169
|
# Load postgres from the database-management stack feature compose.
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
print("DEBUG INFRA: Loading postgres compose from {}".format(postgres_compose))
|
|
116
|
-
_docker_compose(postgres_compose, env_file)
|
|
117
|
-
dc_resource('postgres', labels=['infra.tools'], resource_deps=['init-networks'], auto_init=True)
|
|
118
|
-
else:
|
|
119
|
-
print("DEBUG INFRA: Skipping postgres (compose file not found)")
|
|
120
|
-
|
|
170
|
+
_register_platform_postgres(should_enable, root_prefix, env_file, write_fn)
|
|
171
|
+
|
|
121
172
|
# Load messaging if compose file exists
|
|
122
173
|
messaging_compose = root_prefix + 'services/platform/messaging/docker-compose.yml'
|
|
123
174
|
if _file_exists(messaging_compose):
|
|
@@ -131,7 +182,7 @@ def _load_database_management(should_enable, root_prefix="", env_file=None, writ
|
|
|
131
182
|
dc_resource('nats', labels=['infra.messaging'], auto_init=True)
|
|
132
183
|
else:
|
|
133
184
|
print("DEBUG INFRA: Skipping messaging (compose file not found)")
|
|
134
|
-
|
|
185
|
+
|
|
135
186
|
# Only create kafka resources if debezium is enabled AND messaging exists
|
|
136
187
|
cdc_enabled = should_enable('debezium')
|
|
137
188
|
if cdc_enabled and _file_exists(messaging_compose):
|
|
@@ -422,7 +473,7 @@ Infra = struct(
|
|
|
422
473
|
# Main loader
|
|
423
474
|
load_all = load_all_infrastructure,
|
|
424
475
|
init_networks = _init_networks,
|
|
425
|
-
|
|
476
|
+
|
|
426
477
|
# Individual loaders (for granular control)
|
|
427
478
|
load_database = _load_database_management,
|
|
428
479
|
load_verdaccio = _load_verdaccio,
|
|
@@ -432,4 +483,8 @@ Infra = struct(
|
|
|
432
483
|
load_debezium = _load_debezium,
|
|
433
484
|
load_elk = _load_elk,
|
|
434
485
|
load_golden_image = _load_golden_image,
|
|
486
|
+
|
|
487
|
+
# Shared platform Postgres start signal (dependsOn postgres/database-management
|
|
488
|
+
# when the database-management feature is off). Idempotent; no messaging/kafka/Prisma.
|
|
489
|
+
force_start_postgres = force_start_platform_postgres,
|
|
435
490
|
)
|
package/engine/topologies/tilt/resources/orchestrator/apply_compose_resource_registration.star
CHANGED
|
@@ -12,10 +12,68 @@ load('../../../platform/docker/constants.star', 'PlatformDockerConstants')
|
|
|
12
12
|
load('../../../tilt/manifest/constants.star', 'BASE_PORT_FRONTEND', 'BASE_PORT_BACKEND')
|
|
13
13
|
load('../../../platform/docker/networking/traefik_static_routes.star', 'generate_static_wake_route', 'normalize_abs_path')
|
|
14
14
|
load('../../../platform/docker/networking/sablier_container_cycle.star', 'sablier_middleware_suffix')
|
|
15
|
+
# Shared platform Postgres force-start path. infra-loader.star must not load
|
|
16
|
+
# apply_compose (no circular load): it only loads platform/docker + registries.
|
|
17
|
+
load('../infra-loader.star', 'Infra')
|
|
18
|
+
load('../shared-platform-postgres.star',
|
|
19
|
+
_shared_postgres_names = 'SHARED_POSTGRES_DEPENDENCY_NAMES',
|
|
20
|
+
_is_shared_platform_postgres_dependency = 'is_shared_platform_postgres_dependency',
|
|
21
|
+
_manifest_needs_shared_platform_postgres = 'manifest_needs_shared_platform_postgres')
|
|
15
22
|
|
|
16
23
|
load('./builders/typescript.star', 'TypescriptBuilders')
|
|
17
24
|
|
|
18
25
|
|
|
26
|
+
# Public wrappers for shared platform Postgres helpers. Starlark `load` does not
|
|
27
|
+
# re-export loaded names. Canonical list + predicates live in
|
|
28
|
+
# shared-platform-postgres.star — do not hardcode a second copy of the names here.
|
|
29
|
+
|
|
30
|
+
def is_shared_platform_postgres_dependency(name):
|
|
31
|
+
"""True when a dependsOn name is one of the shared platform Postgres aliases."""
|
|
32
|
+
return _is_shared_platform_postgres_dependency(name)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def manifest_needs_shared_platform_postgres(manifest):
|
|
36
|
+
"""True when a manifest's dependsOn lists postgres or database-management."""
|
|
37
|
+
return _manifest_needs_shared_platform_postgres(manifest)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def selected_for_shared_platform_postgres(name, ctx):
|
|
41
|
+
"""Use the same focus selection later passed to set_enabled_resources."""
|
|
42
|
+
if not ctx.get('focus_mode', False):
|
|
43
|
+
return True
|
|
44
|
+
return name in (ctx.get('focus_enabled_resources') or [])
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def force_start_shared_platform_postgres_once(ctx, should_enable, resource_name=""):
|
|
48
|
+
"""Force-start shared platform Postgres at most once per Tiltfile evaluation.
|
|
49
|
+
|
|
50
|
+
`register_compose_resources` runs per stack and can see unselected nested
|
|
51
|
+
services; two selected services that both `dependsOn` Postgres would otherwise call
|
|
52
|
+
`Infra.force_start_postgres` twice (docker_compose + dc_resource each time).
|
|
53
|
+
Module-level mutable flags are unusable — Tilt freezes Starlark globals — so
|
|
54
|
+
the guard lives on `ctx`, a dict the Tiltfile already owns for this run.
|
|
55
|
+
|
|
56
|
+
Returns True when this call performed the force-start, False when it skipped
|
|
57
|
+
(already started this run, or database-management is already on).
|
|
58
|
+
"""
|
|
59
|
+
if ctx.get('_shared_platform_postgres_force_started', False):
|
|
60
|
+
print("DEBUG COMPOSE: shared platform Postgres already force-started this run; skip for '{}'".format(resource_name))
|
|
61
|
+
return False
|
|
62
|
+
if should_enable('database-management'):
|
|
63
|
+
return False
|
|
64
|
+
project_root_for_env = ctx.get('project_root', '')
|
|
65
|
+
root_prefix = (project_root_for_env + '/') if project_root_for_env else ''
|
|
66
|
+
env_candidate = (project_root_for_env + '/.env') if project_root_for_env else '.env'
|
|
67
|
+
env_file = _env_file_if_exists(env_candidate)
|
|
68
|
+
write_fn = ctx.get('write_file', None)
|
|
69
|
+
if write_fn == None:
|
|
70
|
+
fail("dependsOn postgres/database-management on selected resource '{}' requires ctx.write_file to materialize services/platform/database-management/docker-compose.yml".format(resource_name))
|
|
71
|
+
print("DEBUG COMPOSE: force-starting shared platform Postgres once (selected '{}' dependsOn)".format(resource_name))
|
|
72
|
+
Infra.force_start_postgres(should_enable, root_prefix=root_prefix, env_file=env_file, write_fn=write_fn)
|
|
73
|
+
ctx['_shared_platform_postgres_force_started'] = True
|
|
74
|
+
return True
|
|
75
|
+
|
|
76
|
+
|
|
19
77
|
def _env_file_if_exists(env_file):
|
|
20
78
|
""".env is gitignored (no .env.example shipped) across every TDK example repo, so a
|
|
21
79
|
fresh clone must not hard-require it: docker_compose()'s env_file kwarg type-checks as
|
|
@@ -51,22 +109,30 @@ def _build_infra_dependencies(resource_name, should_enable, ctx, has_backend=Fal
|
|
|
51
109
|
def _resolve_dependency_to_resource(dep_name, all_services_map):
|
|
52
110
|
"""
|
|
53
111
|
Resolve a short dependency name (e.g., 'identity') to the full YAML resource name.
|
|
54
|
-
|
|
112
|
+
|
|
55
113
|
The discovery registry creates YAML resources with names like:
|
|
56
114
|
- 'identity-management-backend-yaml' for identity backend
|
|
57
115
|
- 'api-gateway-yaml' for api-gateway
|
|
58
|
-
|
|
116
|
+
|
|
59
117
|
But manifests reference them as short names like 'identity' or 'api-gateway'.
|
|
118
|
+
|
|
119
|
+
Shared platform Postgres aliases (`postgres`, `database-management`) resolve
|
|
120
|
+
to the literal Tilt resource `postgres` in BOTH feature states — they must
|
|
121
|
+
NOT fall through to `postgres-yaml` or any other name.
|
|
60
122
|
"""
|
|
123
|
+
# Shared platform Postgres: exact names only (typos stay on the normal path).
|
|
124
|
+
if is_shared_platform_postgres_dependency(dep_name):
|
|
125
|
+
return 'postgres'
|
|
126
|
+
|
|
61
127
|
# Check if already a full YAML resource name
|
|
62
128
|
if dep_name in all_services_map:
|
|
63
129
|
return dep_name
|
|
64
|
-
|
|
130
|
+
|
|
65
131
|
# Try with -yaml suffix (for resources already resolved)
|
|
66
132
|
yaml_name = dep_name + '-yaml'
|
|
67
133
|
if yaml_name in all_services_map:
|
|
68
134
|
return yaml_name
|
|
69
|
-
|
|
135
|
+
|
|
70
136
|
# Try to find matching service by stack/short name
|
|
71
137
|
for resource_name, resource_info in all_services_map.items():
|
|
72
138
|
# Check if this service's stack matches the dependency
|
|
@@ -81,16 +147,51 @@ def _resolve_dependency_to_resource(dep_name, all_services_map):
|
|
|
81
147
|
return res['name'] + '-yaml'
|
|
82
148
|
# Fallback to first resource
|
|
83
149
|
return resources[0]['name'] + '-yaml'
|
|
84
|
-
|
|
150
|
+
|
|
85
151
|
# Try pattern matching: identity -> identity-*-yaml
|
|
86
152
|
for resource_name in all_services_map.keys():
|
|
87
153
|
if resource_name.startswith(dep_name + '-') and resource_name.endswith('-yaml'):
|
|
88
154
|
return resource_name
|
|
89
|
-
|
|
155
|
+
|
|
90
156
|
# Return original with -yaml suffix as last resort
|
|
91
157
|
return dep_name + '-yaml'
|
|
92
158
|
|
|
93
159
|
|
|
160
|
+
def resolve_dependency_to_resource(dep_name, all_services_map):
|
|
161
|
+
"""Public wrapper over `_resolve_dependency_to_resource` (exported for tests)."""
|
|
162
|
+
return _resolve_dependency_to_resource(dep_name, all_services_map)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def build_infra_dependencies(resource_name, should_enable, ctx=None, has_backend=False):
|
|
166
|
+
"""Public wrapper over `_build_infra_dependencies` (exported for tests).
|
|
167
|
+
|
|
168
|
+
feature off → no postgres; feature on → has postgres (plus backend
|
|
169
|
+
provision-db edge when has_backend is true).
|
|
170
|
+
"""
|
|
171
|
+
return _build_infra_dependencies(resource_name, should_enable, ctx if ctx != None else {}, has_backend)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def build_resource_deps(res, res_name, manifest, resource_config, infra_deps, config_gen_resources=None, resource_manifests=None, runtime_flags=None, all_services_map=None):
|
|
175
|
+
"""Public wrapper over `_build_resource_deps` (exported for tests).
|
|
176
|
+
|
|
177
|
+
Ensures the dependsOn→resource_deps edge is exercised by tests: feature off
|
|
178
|
+
+ dependsOn postgres → postgres edge present; feature on + dependsOn →
|
|
179
|
+
exactly one postgres edge (infra_deps already carries it).
|
|
180
|
+
"""
|
|
181
|
+
default_runtime_flags = {
|
|
182
|
+
'enforce_migrator_deps': False,
|
|
183
|
+
'auto_init_apps': True,
|
|
184
|
+
'disable_app_replicas': False,
|
|
185
|
+
}
|
|
186
|
+
return _build_resource_deps(
|
|
187
|
+
res, res_name, manifest, resource_config, infra_deps,
|
|
188
|
+
config_gen_resources if config_gen_resources != None else {},
|
|
189
|
+
resource_manifests if resource_manifests != None else {},
|
|
190
|
+
runtime_flags if runtime_flags != None else default_runtime_flags,
|
|
191
|
+
all_services_map,
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
|
|
94
195
|
def _build_resource_deps(res, res_name, manifest, resource_config, infra_deps, config_gen_resources, resource_manifests, runtime_flags, all_services_map=None):
|
|
95
196
|
# Drop self-deps: shared infra_deps always includes verdaccio when enabled, so
|
|
96
197
|
# registering verdaccio itself would otherwise produce resource_deps=['verdaccio'].
|
|
@@ -572,6 +673,26 @@ def register_compose_resources(resource_config, ctx, runtime_flags, manifest_sta
|
|
|
572
673
|
if res_name:
|
|
573
674
|
all_services_map[res_name] = svc
|
|
574
675
|
|
|
676
|
+
# Selection-bound shared platform Postgres start signal.
|
|
677
|
+
# Only enabled resources in THIS stack are evaluated. Registration sees
|
|
678
|
+
# unselected siblings before Tilt's enabled-resource filter runs.
|
|
679
|
+
# When database-management is off and any selected resource's
|
|
680
|
+
# manifest depends on postgres/database-management, force-start the shared
|
|
681
|
+
# platform Postgres via the existing infra loader (materializes
|
|
682
|
+
# services/platform/database-management/docker-compose.yml + registers the
|
|
683
|
+
# existing `postgres` Tilt resource). Does NOT open migrators/provision-db/Prisma.
|
|
684
|
+
# Idempotent across services: force_start_shared_platform_postgres_once
|
|
685
|
+
# guards on ctx so two selected dependents do not register postgres twice.
|
|
686
|
+
if not should_enable('database-management'):
|
|
687
|
+
for res in resource_config.get('resources', []):
|
|
688
|
+
if not selected_for_shared_platform_postgres(res.get('name', ''), ctx):
|
|
689
|
+
continue
|
|
690
|
+
res_manifest = resource_manifests.get(res['name'], {})
|
|
691
|
+
if manifest_needs_shared_platform_postgres(res_manifest):
|
|
692
|
+
print("DEBUG COMPOSE: selected '{}' dependsOn shared platform Postgres; force-starting postgres".format(res.get('name', '')))
|
|
693
|
+
force_start_shared_platform_postgres_once(ctx, should_enable, res.get('name', ''))
|
|
694
|
+
break
|
|
695
|
+
|
|
575
696
|
resource_entries = []
|
|
576
697
|
resource_configs = []
|
|
577
698
|
|
|
@@ -627,11 +748,17 @@ def register_compose_resources(resource_config, ctx, runtime_flags, manifest_sta
|
|
|
627
748
|
))
|
|
628
749
|
|
|
629
750
|
resource_entries.append(entry)
|
|
630
|
-
|
|
751
|
+
built_config = _build_resource_config(
|
|
631
752
|
res, manifest, resource_config, full_res_path, infra_deps,
|
|
632
753
|
config_gen_resources, resource_manifests, runtime_flags, all_services_map,
|
|
633
754
|
ctx.get('project_root', ''),
|
|
634
|
-
)
|
|
755
|
+
)
|
|
756
|
+
# A focused run still registers disabled siblings before Tilt applies
|
|
757
|
+
# set_enabled_resources. Do not leave their edge pointing at a Postgres
|
|
758
|
+
# resource that this evaluation did not register.
|
|
759
|
+
if not should_enable('database-management') and not ctx.get('_shared_platform_postgres_force_started', False):
|
|
760
|
+
built_config['res_deps'] = [dep for dep in built_config['res_deps'] if dep != 'postgres']
|
|
761
|
+
resource_configs.append(built_config)
|
|
635
762
|
|
|
636
763
|
print("DEBUG COMPOSE: Calling Docker.app_compose for '{}' with write_fn={}".format(resource_name, write_file))
|
|
637
764
|
Docker.app_compose(resource_path, resource_entries, write_file)
|