@tdk-landscape/tdk-cli-core 1.3.15 → 1.3.26

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.
Files changed (274) hide show
  1. package/Tiltfile +144 -0
  2. package/discovery/AGENTS.md +362 -0
  3. package/discovery/TILT_DISCOVERY.star +424 -0
  4. package/discovery/Tiltfile +58 -0
  5. package/discovery/config.star +258 -0
  6. package/discovery/constants.star +127 -0
  7. package/discovery/deterministic.star +211 -0
  8. package/discovery/discovery_daemon.star +191 -0
  9. package/discovery/discovery_orchestrator.star +460 -0
  10. package/discovery/filtering.star +5 -0
  11. package/discovery/identity-management-backend/service.yaml +26 -0
  12. package/discovery/identity-management-frontend/service.yaml +29 -0
  13. package/discovery/identity-sdk/service.yaml +9 -0
  14. package/discovery/incremental_discovery.star +213 -0
  15. package/discovery/indexing.star +7 -0
  16. package/discovery/json_manifest_scanner.star +51 -0
  17. package/discovery/libraries.star +46 -0
  18. package/discovery/loading.star +23 -0
  19. package/discovery/manifest/constants.star +104 -0
  20. package/discovery/manifest/loading.star +386 -0
  21. package/discovery/manifest/normalize.star +281 -0
  22. package/discovery/manifest/validation.star +108 -0
  23. package/discovery/normalization.star +11 -0
  24. package/discovery/registry.star +544 -0
  25. package/discovery/resource_snapshot.py +382 -0
  26. package/discovery/resource_snapshot.star +143 -0
  27. package/discovery/schemas.star +1 -0
  28. package/discovery/services/platform/cdc/debezium/nats-connector/service.yaml +9 -0
  29. package/discovery/services/platform/cdc/nats-http-bridge/service.yaml +9 -0
  30. package/discovery/services/platform/orchestrator/orchestrator-glue-backend/service.yaml +26 -0
  31. package/discovery/services/platform/synthetic-monitor/service.yaml +9 -0
  32. package/discovery/test_resource_snapshot.py +251 -0
  33. package/discovery/validation.star +78 -0
  34. package/discovery/versioning.star +1 -0
  35. package/dist/commands/config.d.ts.map +1 -1
  36. package/dist/commands/config.js +11 -0
  37. package/dist/commands/config.js.map +1 -1
  38. package/dist/commands/doctor.d.ts.map +1 -1
  39. package/dist/commands/doctor.js +1 -2
  40. package/dist/commands/doctor.js.map +1 -1
  41. package/dist/commands/project.d.ts.map +1 -1
  42. package/dist/commands/project.js +36 -41
  43. package/dist/commands/project.js.map +1 -1
  44. package/dist/commands/resource.d.ts.map +1 -1
  45. package/dist/commands/resource.js +51 -55
  46. package/dist/commands/resource.js.map +1 -1
  47. package/dist/commands/stack.d.ts.map +1 -1
  48. package/dist/commands/stack.js +22 -24
  49. package/dist/commands/stack.js.map +1 -1
  50. package/dist/commands/ui.d.ts.map +1 -1
  51. package/dist/commands/ui.js +1 -2
  52. package/dist/commands/ui.js.map +1 -1
  53. package/dist/commands/up.d.ts.map +1 -1
  54. package/dist/commands/up.js +1 -2
  55. package/dist/commands/up.js.map +1 -1
  56. package/dist/commands/upgrade.d.ts +8 -0
  57. package/dist/commands/upgrade.d.ts.map +1 -1
  58. package/dist/commands/upgrade.js +59 -18
  59. package/dist/commands/upgrade.js.map +1 -1
  60. package/dist/generator/extension-fetch.d.ts +19 -0
  61. package/dist/generator/extension-fetch.d.ts.map +1 -1
  62. package/dist/generator/extension-fetch.js +71 -5
  63. package/dist/generator/extension-fetch.js.map +1 -1
  64. package/dist/generator/template-engine.d.ts +1 -0
  65. package/dist/generator/template-engine.d.ts.map +1 -1
  66. package/dist/generator/template-engine.js +50 -6
  67. package/dist/generator/template-engine.js.map +1 -1
  68. package/dist/utils/command-helpers.d.ts.map +1 -1
  69. package/dist/utils/command-helpers.js +7 -9
  70. package/dist/utils/command-helpers.js.map +1 -1
  71. package/dist/utils/constants.d.ts +1 -0
  72. package/dist/utils/constants.d.ts.map +1 -1
  73. package/dist/utils/constants.js +1 -0
  74. package/dist/utils/constants.js.map +1 -1
  75. package/dist/utils/errors.d.ts +2 -0
  76. package/dist/utils/errors.d.ts.map +1 -1
  77. package/dist/utils/errors.js +6 -1
  78. package/dist/utils/errors.js.map +1 -1
  79. package/dist/utils/project-features.d.ts.map +1 -1
  80. package/dist/utils/project-features.js +1 -1
  81. package/dist/utils/project-features.js.map +1 -1
  82. package/dist/utils/resource-features.d.ts.map +1 -1
  83. package/dist/utils/resource-features.js +2 -2
  84. package/dist/utils/resource-features.js.map +1 -1
  85. package/dist/utils/tar.js +4 -4
  86. package/dist/utils/tar.js.map +1 -1
  87. package/dist/utils/validation.d.ts +7 -0
  88. package/dist/utils/validation.d.ts.map +1 -1
  89. package/dist/utils/validation.js +9 -0
  90. package/dist/utils/validation.js.map +1 -1
  91. package/engine/AGENTS.md +705 -0
  92. package/engine/README.md +29 -0
  93. package/engine/REFACTORING_MANIFEST_SYSTEM.md +974 -0
  94. package/engine/Tiltfile +80 -0
  95. package/engine/assets/docker/golden-layers.Dockerfile +221 -0
  96. package/engine/assets/scripts/cleanup-completed-migrator-images.sh +37 -0
  97. package/engine/assets/scripts/cleanup-tilt-images.sh +120 -0
  98. package/engine/assets/scripts/mac-m1-resource-monitor.sh +121 -0
  99. package/engine/config.star +3 -0
  100. package/engine/docs/AUTODISCOVERY_TEST.md +182 -0
  101. package/engine/docs/C4-ARCHITECTURE-operations.md +396 -0
  102. package/engine/docs/C4-ARCHITECTURE-overview.md +284 -0
  103. package/engine/docs/C4-ARCHITECTURE.md +17 -0
  104. package/engine/docs/GOLDEN_IMAGE_INTEGRATION.md +349 -0
  105. package/engine/docs/MANIFEST_DRIVEN_ARCHITECTURE.md +357 -0
  106. package/engine/docs/README.md +284 -0
  107. package/engine/entrypoint.star +97 -0
  108. package/engine/lifecycle/orchestrator.star +260 -0
  109. package/engine/paths.star +717 -0
  110. package/engine/schemas/manifest-schema.json +301 -0
  111. package/engine/schemas/service-schema.json +301 -0
  112. package/engine/scripts/README.md +89 -0
  113. package/engine/scripts/build-and-publish-lib.sh +205 -0
  114. package/engine/scripts/check-lib-publish.sh +85 -0
  115. package/engine/scripts/debug-container.sh +258 -0
  116. package/engine/scripts/generate-manifest.sh +197 -0
  117. package/engine/spec.master +702 -0
  118. package/engine/spec.master.example +341 -0
  119. package/engine/topologies/README.md +34 -0
  120. package/engine/topologies/platform/docker/AGENTS.md +59 -0
  121. package/engine/topologies/platform/docker/auth/auth.star +167 -0
  122. package/engine/topologies/platform/docker/build/golden_image.star +37 -0
  123. package/engine/topologies/platform/docker/build/golden_image_build.star +82 -0
  124. package/engine/topologies/platform/docker/build/golden_image_constants.star +93 -0
  125. package/engine/topologies/platform/docker/build/golden_image_dockerfile.star +291 -0
  126. package/engine/topologies/platform/docker/compose/compose.star +636 -0
  127. package/engine/topologies/platform/docker/compose/compose_helpers.star +52 -0
  128. package/engine/topologies/platform/docker/compose/traefik_standalone.star +53 -0
  129. package/engine/topologies/platform/docker/config/bunignore.star +142 -0
  130. package/engine/topologies/platform/docker/config/dockerignore.star +150 -0
  131. package/engine/topologies/platform/docker/constants.star +170 -0
  132. package/engine/topologies/platform/docker/dockerfile/dockerfile.star +54 -0
  133. package/engine/topologies/platform/docker/generators/golden_docker_generator_v2.star +73 -0
  134. package/engine/topologies/platform/docker/index.star +27 -0
  135. package/engine/topologies/platform/docker/layers/bun_helpers.star +23 -0
  136. package/engine/topologies/platform/docker/layers/infisical/index.star +40 -0
  137. package/engine/topologies/platform/docker/layers/infisical/infisical_docker.star +262 -0
  138. package/engine/topologies/platform/docker/layers/l1_base_layers.star +87 -0
  139. package/engine/topologies/platform/docker/layers/l2_dependency_layers.star +125 -0
  140. package/engine/topologies/platform/docker/layers/l3_builder_layers.star +209 -0
  141. package/engine/topologies/platform/docker/layers/l4_runtime_layers.star +214 -0
  142. package/engine/topologies/platform/docker/layers/prisma/constants.star +135 -0
  143. package/engine/topologies/platform/docker/layers/prisma/db_readiness.star +96 -0
  144. package/engine/topologies/platform/docker/layers/prisma/filesystem_setup.star +109 -0
  145. package/engine/topologies/platform/docker/layers/prisma/migrate_script.star +98 -0
  146. package/engine/topologies/platform/docker/layers/prisma/prisma_build.star +126 -0
  147. package/engine/topologies/platform/docker/layers/prisma/prisma_runtime.star +161 -0
  148. package/engine/topologies/platform/docker/layers/prisma/secret_resolution.star +119 -0
  149. package/engine/topologies/platform/docker/networking/api_path_constants.star +230 -0
  150. package/engine/topologies/platform/docker/networking/sablier_container_cycle.star +27 -0
  151. package/engine/topologies/platform/docker/networking/traefik.star +232 -0
  152. package/engine/topologies/platform/docker/networking/traefik_constants.star +146 -0
  153. package/engine/topologies/platform/docker/networking/traefik_helpers.star +160 -0
  154. package/engine/topologies/platform/docker/secrets/infisical.star +135 -0
  155. package/engine/topologies/platform/net/proxy.star +4 -0
  156. package/engine/topologies/platform/registries/local.star +4 -0
  157. package/engine/topologies/platform/security/secrets.star +239 -0
  158. package/engine/topologies/platform/state/relational.star +4 -0
  159. package/engine/topologies/tilt/common/AGENTS.md +59 -0
  160. package/engine/topologies/tilt/common/utils.star +364 -0
  161. package/engine/topologies/tilt/common/utils_debug.star +69 -0
  162. package/engine/topologies/tilt/common/utils_deterministic.star +168 -0
  163. package/engine/topologies/tilt/common/utils_docker_networks.star +46 -0
  164. package/engine/topologies/tilt/common/utils_env.star +43 -0
  165. package/engine/topologies/tilt/common/utils_output.star +97 -0
  166. package/engine/topologies/tilt/config/AGENTS.md +68 -0
  167. package/engine/topologies/tilt/config/defaults.star +3 -0
  168. package/engine/topologies/tilt/config/feature_flags.star +10 -0
  169. package/engine/topologies/tilt/config/global.star +55 -0
  170. package/engine/topologies/tilt/config/profiles.star +382 -0
  171. package/engine/topologies/tilt/generators/agents_md.star +62 -0
  172. package/engine/topologies/tilt/generators/api_gateway.star +145 -0
  173. package/engine/topologies/tilt/generators/index.star +124 -0
  174. package/engine/topologies/tilt/generators/infisical/README.md +424 -0
  175. package/engine/topologies/tilt/generators/infisical/examples.star +312 -0
  176. package/engine/topologies/tilt/generators/infisical/index.star +57 -0
  177. package/engine/topologies/tilt/generators/infisical/machine_identity.star +420 -0
  178. package/engine/topologies/tilt/generators/infisical/organization.star +534 -0
  179. package/engine/topologies/tilt/generators/infisical/path_manager.star +476 -0
  180. package/engine/topologies/tilt/generators/infisical/secrets_generator.star +420 -0
  181. package/engine/topologies/tilt/generators/nginx_conf.star +143 -0
  182. package/engine/topologies/tilt/generators/npmrc.star +111 -0
  183. package/engine/topologies/tilt/generators/tsconfig.star +23 -0
  184. package/engine/topologies/tilt/generators/typescript/backend_library_tsconfig.star +71 -0
  185. package/engine/topologies/tilt/generators/typescript/backend_tsconfig.star +124 -0
  186. package/engine/topologies/tilt/generators/typescript/common.star +7 -0
  187. package/engine/topologies/tilt/generators/typescript/frontend_library_tsconfig.star +76 -0
  188. package/engine/topologies/tilt/generators/typescript/frontend_tsconfig.star +110 -0
  189. package/engine/topologies/tilt/generators/typescript/include.star +23 -0
  190. package/engine/topologies/tilt/generators/typescript/playwright_config.star +40 -0
  191. package/engine/topologies/tilt/generators/typescript/prisma_config.star +49 -0
  192. package/engine/topologies/tilt/generators/validators.star +853 -0
  193. package/engine/topologies/tilt/generators/vite/backend.star +78 -0
  194. package/engine/topologies/tilt/generators/vite/frontend.star +98 -0
  195. package/engine/topologies/tilt/generators/vite/generators.star +43 -0
  196. package/engine/topologies/tilt/generators/vite/helpers.star +244 -0
  197. package/engine/topologies/tilt/generators/vite/library.star +65 -0
  198. package/engine/topologies/tilt/generators/vite/sdk.star +73 -0
  199. package/engine/topologies/tilt/generators/vite/templates.star +737 -0
  200. package/engine/topologies/tilt/generators/vite_config.star +22 -0
  201. package/engine/topologies/tilt/manifest/AGENTS.md +61 -0
  202. package/engine/topologies/tilt/manifest/README.md +693 -0
  203. package/engine/topologies/tilt/manifest/__init__.star +218 -0
  204. package/engine/topologies/tilt/manifest/constants.star +252 -0
  205. package/engine/topologies/tilt/manifest/errors.star +266 -0
  206. package/engine/topologies/tilt/manifest/integration.star +339 -0
  207. package/engine/topologies/tilt/manifest/json_to_yaml.py +54 -0
  208. package/engine/topologies/tilt/manifest/loader.star +214 -0
  209. package/engine/topologies/tilt/manifest/mapper.star +227 -0
  210. package/engine/topologies/tilt/manifest/parser.star +400 -0
  211. package/engine/topologies/tilt/manifest/schema.star +462 -0
  212. package/engine/topologies/tilt/manifest/tests/test_integration.star +276 -0
  213. package/engine/topologies/tilt/manifest/tests/test_loader_parser.star +397 -0
  214. package/engine/topologies/tilt/manifest/tests/test_manifest.star +316 -0
  215. package/engine/topologies/tilt/manifest/tests/test_validator.star +470 -0
  216. package/engine/topologies/tilt/manifest/validator.star +717 -0
  217. package/engine/topologies/tilt/manifest/yaml_to_json.py +18 -0
  218. package/engine/topologies/tilt/resources/AGENTS.md +71 -0
  219. package/engine/topologies/tilt/resources/conditions.star +3 -0
  220. package/engine/topologies/tilt/resources/databases.star +244 -0
  221. package/engine/topologies/tilt/resources/declaration.star +3 -0
  222. package/engine/topologies/tilt/resources/deps.star +3 -0
  223. package/engine/topologies/tilt/resources/infra-loader.star +437 -0
  224. package/engine/topologies/tilt/resources/libs.star +30 -0
  225. package/engine/topologies/tilt/resources/libs_discovery.star +102 -0
  226. package/engine/topologies/tilt/resources/libs_paths.star +79 -0
  227. package/engine/topologies/tilt/resources/libs_resource_definition.star +151 -0
  228. package/engine/topologies/tilt/resources/libs_setup.star +164 -0
  229. package/engine/topologies/tilt/resources/libs_tsconfig.star +37 -0
  230. package/engine/topologies/tilt/resources/orchestrator/apply.star +77 -0
  231. package/engine/topologies/tilt/resources/orchestrator/apply_compose_resource_registration.star +431 -0
  232. package/engine/topologies/tilt/resources/orchestrator/apply_manifest_orchestration.star +81 -0
  233. package/engine/topologies/tilt/resources/orchestrator/apply_migrator_orchestration.star +148 -0
  234. package/engine/topologies/tilt/resources/orchestrator/apply_resource_validation.star +36 -0
  235. package/engine/topologies/tilt/resources/orchestrator/apply_runtime_flags.star +44 -0
  236. package/engine/topologies/tilt/resources/orchestrator/builders/typescript.star +164 -0
  237. package/engine/topologies/tilt/resources/orchestrator/generators/dependencies/AGENTS.md +75 -0
  238. package/engine/topologies/tilt/resources/orchestrator/generators/dependencies/index.star +21 -0
  239. package/engine/topologies/tilt/resources/orchestrator/generators/dependencies/resolver.star +134 -0
  240. package/engine/topologies/tilt/resources/orchestrator/generators/env.star +258 -0
  241. package/engine/topologies/tilt/resources/orchestrator/generators/frontend.star +169 -0
  242. package/engine/topologies/tilt/resources/orchestrator/generators/manifest_resource.star +524 -0
  243. package/engine/topologies/tilt/resources/orchestrator/helpers.star +33 -0
  244. package/engine/topologies/tilt/resources/orchestrator.star +54 -0
  245. package/engine/topologies/tilt/resources/ordering.star +3 -0
  246. package/engine/topologies/tilt/resources/triggers.star +3 -0
  247. package/engine/topologies/tilt/resources/watchers.star +299 -0
  248. package/engine/where-things-live.star +717 -0
  249. package/ext/Tiltfile +66 -0
  250. package/ext/ide-components/README.md +219 -0
  251. package/ext/ide-components/code_executor/server.py +268 -0
  252. package/ext/ide-components/code_viewer/server.py +206 -0
  253. package/ext/ide-components/config_inspector/server.py +1050 -0
  254. package/ext/ide-components/file_browser/server.py +285 -0
  255. package/ext/ide-components/shared/config_service.py +469 -0
  256. package/ext/ide-components/shared/file_utils.py +298 -0
  257. package/ext/ide-components/shared/http_utils.py +232 -0
  258. package/ext/ide-components/shared/static/styles.css +1334 -0
  259. package/ext/ide-components/shared/syntax_highlighter.py +140 -0
  260. package/ext/ide-components/shared/template_engine.py +123 -0
  261. package/ext/ide-components/shared/templates/base.html +121 -0
  262. package/ext/ide-components/shared/test_config_service.py +326 -0
  263. package/ext/ide-components/shared/tilt_integration.py +158 -0
  264. package/ext/ui-enhancements/cron-status-server.sh +49 -0
  265. package/ext/ui-enhancements/cron-status-tracker.sh +146 -0
  266. package/ext/ui-enhancements/extension.yaml +36 -0
  267. package/ext/ui-enhancements/tiltfile.py +99 -0
  268. package/ext/ui-enhancements/ui/cron-jobs-tab.css +396 -0
  269. package/ext/ui-enhancements/ui/help-panel.css +279 -0
  270. package/ext/ui-enhancements/ui/icons.css +125 -0
  271. package/ext/ui-enhancements/ui/tooltips.css +114 -0
  272. package/package.json +13 -9
  273. package/specs/specs/TILT_SERVICE_DEFAULTS.star +205 -0
  274. package/specs/specs/TILT_TECH_STACK.star +159 -0
@@ -0,0 +1,108 @@
1
+ # =============================================================================
2
+ # ✅ TILT SDK - MANIFEST VALIDATION
3
+ # =============================================================================
4
+ # Path: .tilt/topologies/tilt/discovery/manifest/validation.star
5
+ # Purpose: Validate manifest structure and values
6
+ # =============================================================================
7
+
8
+ load('./constants.star', 'VALID_APP_TYPES')
9
+ load('../TILT_DISCOVERY.star', 'VALID_STACKS', 'VALID_RESOURCE_FEATURES', 'PORT_RANGES')
10
+
11
+
12
+ def validate_manifest(manifest):
13
+ """
14
+ 🎯 ENTERPRISE VALIDATOR: Strict structure and value validation.
15
+
16
+ Validates:
17
+ - Required fields: appName, appType, stack, port
18
+ - appName pattern: kebab-case, 3-64 chars
19
+ - appType: from VALID_APP_TYPES
20
+ - stack: from VALID_STACKS
21
+ - appType: from VALID_APP_TYPES
22
+ - port: within range for appType (backends: 4000-5999, frontends: 3000-3999)
23
+ - features: from VALID_RESOURCE_FEATURES
24
+ - dependsOn: from VALID_STACKS (resource aliases)
25
+ - replicas: 1-10
26
+ """
27
+ issues = []
28
+
29
+ # 1. Required fields & appName pattern
30
+ app_name = manifest.get('appName')
31
+ if not app_name:
32
+ issues.append("Missing required field: appName")
33
+ elif type(app_name) != 'string':
34
+ issues.append("appName must be a string")
35
+ elif len(app_name) < 3:
36
+ issues.append("appName too short: '" + app_name + "'. Minimum 3 characters")
37
+ elif len(app_name) > 64:
38
+ issues.append("appName too long: '" + app_name + "'. Maximum 64 characters")
39
+
40
+ # Check kebab-case pattern
41
+ for i in range(len(app_name)):
42
+ char = app_name[i]
43
+ if not (char.islower() or char.isdigit() or char == '-'):
44
+ issues.append("Invalid character in appName: '" + char + "'. Must be kebab-case (lowercase, numbers, hyphens)")
45
+ break
46
+ if app_name.startswith('-') or app_name.endswith('-'):
47
+ issues.append("appName cannot start or end with a hyphen")
48
+
49
+ # 2. appType validation
50
+ app_type = manifest.get('appType')
51
+ if not app_type:
52
+ issues.append("Missing required field: appType")
53
+ elif app_type not in VALID_APP_TYPES:
54
+ issues.append("Invalid appType: " + str(app_type) + ". Must be one of: " + ', '.join(VALID_APP_TYPES))
55
+
56
+ # 3. stack validation
57
+ stack = manifest.get('stack')
58
+ if not stack:
59
+ issues.append("Missing required field: stack")
60
+ elif len(VALID_STACKS) > 0 and stack not in VALID_STACKS:
61
+ # Only validate against list if VALID_STACKS is not empty
62
+ issues.append("Invalid stack: " + str(stack) + ". Must be one of: " + ', '.join(VALID_STACKS))
63
+
64
+ # 4. port validation with appType-specific ranges (skip for infra services)
65
+ port = manifest.get('port')
66
+ if port == None:
67
+ if app_type != 'infra':
68
+ issues.append("Missing required field: port")
69
+ elif type(port) != 'int':
70
+ issues.append("Invalid port: " + str(port) + ". Must be an integer")
71
+ elif app_type != 'infra':
72
+ if port < 3000 or port > 9999:
73
+ issues.append("Invalid port: " + str(port) + ". Must be integer 3000-9999")
74
+ elif app_type and app_type in PORT_RANGES:
75
+ port_range = PORT_RANGES[app_type]
76
+ if port < port_range['min'] or port > port_range['max']:
77
+ issues.append("Port " + str(port) + " out of range for " + app_type + ". Expected " + str(port_range['min']) + "-" + str(port_range['max']))
78
+
79
+ # 5. Validate features array
80
+ features = manifest.get('features', [])
81
+ if type(features) != 'list':
82
+ issues.append("Invalid features: must be an array")
83
+ else:
84
+ for f in features:
85
+ if f not in VALID_RESOURCE_FEATURES:
86
+ issues.append("Invalid feature: " + str(f) + ". Must be one of: " + ', '.join(VALID_RESOURCE_FEATURES))
87
+
88
+ # 6. Validate dependsOn array (must be valid resource aliases)
89
+ deps = manifest.get('dependsOn', [])
90
+ if type(deps) != 'list':
91
+ issues.append("Invalid dependsOn: must be an array")
92
+ else:
93
+ # Valid dependency targets are discovered dynamically from manifests
94
+ # No hardcoded list - all dependencies validated against discovered resources
95
+ # The actual validation happens at orchestration time, not here
96
+ pass
97
+
98
+ # 7. Validate replicas (if specified)
99
+ replicas = manifest.get('replicas')
100
+ if replicas != None:
101
+ if type(replicas) != 'int':
102
+ issues.append("Invalid replicas: must be an integer")
103
+ elif replicas < 1:
104
+ issues.append("Invalid replicas: " + str(replicas) + ". Minimum is 1")
105
+ elif replicas > 10:
106
+ issues.append("Invalid replicas: " + str(replicas) + ". Maximum is 10")
107
+
108
+ return issues
@@ -0,0 +1,11 @@
1
+ load(
2
+ "./manifest/normalize.star",
3
+ _load_and_normalize = "load_and_normalize",
4
+ _load_all_manifests = "load_all_manifests",
5
+ )
6
+
7
+ def normalize(manifest_path, warn_only = True):
8
+ return _load_and_normalize(manifest_path, warn_only = warn_only)
9
+
10
+ def normalize_all(root_path, warn_only = True):
11
+ return _load_all_manifests(root_path, warn_only = warn_only)
@@ -0,0 +1,544 @@
1
+ #!/usr/bin/env starlark
2
+ # -*- coding: utf-8 -*-
3
+ # =============================================================================
4
+ # 🔍 DISCOVERY REGISTRY - Service Discovery for TDK CLI
5
+ # =============================================================================
6
+ # This module discovers services from service.json manifest files.
7
+ # It runs discovery at module load time to avoid frozen hash issues.
8
+ #
9
+ # IMPORTANT: Discovery runs when the module loads (if TDK_PROJECT_ROOT is set).
10
+ # This ensures APP_RESOURCES and related variables are populated before export.
11
+ # =============================================================================
12
+
13
+ load(
14
+ "./config.star",
15
+ "GLOBAL_CONFIG",
16
+ "INFRA_RESOURCES",
17
+ "CORE_INFRA",
18
+ "INFRA_STACK_MAP",
19
+ "OPTIONAL_INFRA",
20
+ "Config",
21
+ "DDD_LIBS",
22
+ )
23
+ load("./discovery_orchestrator.star", "initialize_discovery")
24
+ load("../engine/topologies/tilt/manifest/loader.star", "ManifestLoader")
25
+ load("./libraries.star", "autodiscover_libraries", "get_platform_libs", "get_product_libs")
26
+ load(
27
+ "./constants.star",
28
+ "PRODUCT_SNAPSHOT_DIR",
29
+ "PRODUCT_SNAPSHOT_FILES",
30
+ "PRODUCT_TOPOLOGY_DIR",
31
+ "PRODUCT_STACKS_INDEX_FILE",
32
+ "PRODUCT_STACK_RESOURCES_FILE",
33
+ "DISCOVERY_SCAN_ROOTS",
34
+ )
35
+ load("./manifest/constants.star", "MANIFEST_FILENAME", "MANIFEST_FILENAME_YAML")
36
+
37
+
38
+ # =============================================================================
39
+ # 🔧 RUN DISCOVERY AT MODULE LOAD TIME
40
+ # =============================================================================
41
+ # This runs discovery immediately when the module loads (before freeze).
42
+ # The results are stored in module-level variables that get exported.
43
+ # Use a list as a mutable container to track if header was printed
44
+ _DISCOVERY_STATE = [False]
45
+
46
+ # =============================================================================
47
+
48
+ def _run_discovery():
49
+ """Run two-pass discovery and return results."""
50
+ # Only print header on first discovery
51
+ if not _DISCOVERY_STATE[0]:
52
+ print("")
53
+ print("Phase 1: Discovering services...")
54
+ _DISCOVERY_STATE[0] = True
55
+
56
+ # Create a working cache for both passes
57
+ working_cache = {
58
+ "initialized": False,
59
+ "app_resources": [],
60
+ "resource_dependencies": {},
61
+ "resource_aliases": {},
62
+ "resource_path_map": {},
63
+ "stack_configs": {},
64
+ }
65
+
66
+ # First pass - load JSON and generate YAML
67
+ initialize_discovery(working_cache, second_pass=False)
68
+
69
+ # Second pass - reload from JSON
70
+ working_cache["initialized"] = False
71
+ initialize_discovery(working_cache, second_pass=True)
72
+
73
+ print("")
74
+ return working_cache
75
+
76
+ # Run discovery immediately if TDK_PROJECT_ROOT is set
77
+ # This ensures data is available before module is frozen
78
+ _DISCOVERY_RESULTS = {}
79
+ if os.environ.get('TDK_PROJECT_ROOT', ''):
80
+ _DISCOVERY_RESULTS = _run_discovery()
81
+ else:
82
+ # Empty results if no project root set
83
+ _DISCOVERY_RESULTS = {
84
+ "app_resources": [],
85
+ "resource_dependencies": {},
86
+ "resource_aliases": {},
87
+ "resource_path_map": {},
88
+ "stack_configs": {},
89
+ }
90
+
91
+ # Export discovery results as module-level variables
92
+ # These are populated at load time and remain read-only
93
+ APP_RESOURCES = _DISCOVERY_RESULTS["app_resources"]
94
+ RESOURCE_DEPENDENCIES = _DISCOVERY_RESULTS["resource_dependencies"]
95
+ RESOURCE_ALIASES = _DISCOVERY_RESULTS["resource_aliases"]
96
+ RESOURCE_PATH_MAP = _DISCOVERY_RESULTS["resource_path_map"]
97
+
98
+ # Functions for accessing data (re-run discovery if needed)
99
+ def get_app_resources():
100
+ """Get discovered app resources."""
101
+ if len(APP_RESOURCES) > 0:
102
+ return APP_RESOURCES
103
+ if _DISCOVERY_STATE[0]:
104
+ return APP_RESOURCES
105
+ results = _run_discovery()
106
+ return results["app_resources"]
107
+
108
+ def get_resource_dependencies():
109
+ """Get resource dependency graph."""
110
+ if len(RESOURCE_DEPENDENCIES) > 0:
111
+ return RESOURCE_DEPENDENCIES
112
+ if _DISCOVERY_STATE[0]:
113
+ return RESOURCE_DEPENDENCIES
114
+ results = _run_discovery()
115
+ return results["resource_dependencies"]
116
+
117
+ def get_resource_aliases():
118
+ """Get resource name to path aliases."""
119
+ if len(RESOURCE_ALIASES) > 0:
120
+ return RESOURCE_ALIASES
121
+ if _DISCOVERY_STATE[0]:
122
+ return RESOURCE_ALIASES
123
+ results = _run_discovery()
124
+ return results["resource_aliases"]
125
+
126
+ def get_resource_path_map():
127
+ """Get resource path to name mapping."""
128
+ if len(RESOURCE_PATH_MAP) > 0:
129
+ return RESOURCE_PATH_MAP
130
+ if _DISCOVERY_STATE[0]:
131
+ return RESOURCE_PATH_MAP
132
+ results = _run_discovery()
133
+ return results["resource_path_map"]
134
+
135
+ # Ref functions (same as above, for compatibility)
136
+ def get_app_resources_ref():
137
+ return get_app_resources()
138
+
139
+ def get_resource_dependencies_ref():
140
+ return get_resource_dependencies()
141
+
142
+ def get_resource_aliases_ref():
143
+ return get_resource_aliases()
144
+
145
+ def get_resource_path_map_ref():
146
+ return get_resource_path_map()
147
+
148
+
149
+ # =============================================================================
150
+ # 🔄 INCREMENTAL CACHE OPERATIONS (Auto-discovery support)
151
+ # =============================================================================
152
+
153
+ def add_resource_to_cache(resource_dict):
154
+ """Add a new resource to the discovery cache."""
155
+ resource_name = resource_dict.get("appName", "")
156
+ resource_path = resource_dict.get("_path", "")
157
+ if resource_name and resource_path:
158
+ APP_RESOURCES.append(resource_dict)
159
+ RESOURCE_ALIASES[resource_name] = resource_path
160
+ RESOURCE_PATH_MAP[resource_path] = resource_name
161
+
162
+ def remove_resource_from_cache(resource_path):
163
+ """Remove a resource from the discovery cache by path."""
164
+ # Find and remove from app_resources list
165
+ for i, resource in enumerate(APP_RESOURCES):
166
+ if resource.get("_path", "") == resource_path:
167
+ APP_RESOURCES.pop(i)
168
+ break
169
+
170
+ # Remove from aliases
171
+ resource_name = RESOURCE_PATH_MAP.get(resource_path, "")
172
+ if resource_name in RESOURCE_ALIASES:
173
+ RESOURCE_ALIASES.pop(resource_name, None)
174
+ if resource_path in RESOURCE_PATH_MAP:
175
+ RESOURCE_PATH_MAP.pop(resource_path, None)
176
+ if resource_name in RESOURCE_DEPENDENCIES:
177
+ RESOURCE_DEPENDENCIES.pop(resource_name, None)
178
+
179
+ def has_resource_in_cache(resource_path):
180
+ """Check if a resource path exists in cache."""
181
+ return resource_path in RESOURCE_PATH_MAP
182
+
183
+ def get_resource_by_path_from_cache(resource_path):
184
+ """Get resource dict by path."""
185
+ resource_name = RESOURCE_PATH_MAP.get(resource_path, "")
186
+ if resource_name:
187
+ for resource in APP_RESOURCES:
188
+ if resource.get("appName", "") == resource_name:
189
+ return resource
190
+ return None
191
+
192
+ def persist_cache_to_file():
193
+ """Persist cache to snapshot file."""
194
+ pass # No-op for now
195
+
196
+ def get_cache_stats():
197
+ """Get cache statistics."""
198
+ return {
199
+ "app_resources_count": len(APP_RESOURCES),
200
+ "dependencies_count": len(RESOURCE_DEPENDENCIES),
201
+ "aliases_count": len(RESOURCE_ALIASES),
202
+ }
203
+
204
+ def reinitialize():
205
+ """Force re-discovery by clearing and re-running."""
206
+ # Note: Module-level variables are mutable, no global keyword needed in Starlark
207
+ results = _run_discovery()
208
+ return results["app_resources"]
209
+
210
+ CacheOps = struct(
211
+ add=add_resource_to_cache,
212
+ remove=remove_resource_from_cache,
213
+ has=has_resource_in_cache,
214
+ get_by_path=get_resource_by_path_from_cache,
215
+ persist=persist_cache_to_file,
216
+ stats=get_cache_stats,
217
+ )
218
+
219
+ Registry = struct(
220
+ add=add_resource_to_cache,
221
+ remove=remove_resource_from_cache,
222
+ has=has_resource_in_cache,
223
+ get_by_path=get_resource_by_path_from_cache,
224
+ persist=persist_cache_to_file,
225
+ stats=get_cache_stats,
226
+ reinitialize=reinitialize,
227
+ )
228
+
229
+
230
+ # =============================================================================
231
+ # 🔍 SERVICE LOOKUP FUNCTIONS
232
+ # =============================================================================
233
+
234
+ def get_resource_by_name(name):
235
+ """Get a resource by its appName."""
236
+ for resource in APP_RESOURCES:
237
+ if resource.get("name") == name or resource.get("appName") == name:
238
+ return resource
239
+ return None
240
+
241
+ def get_all_backend_resources():
242
+ """Get all backend resource names."""
243
+ backends = []
244
+ for app_resource in APP_RESOURCES:
245
+ resources = app_resource.get("resources", [])
246
+ for resource in resources:
247
+ if not resource.get("frontend", False):
248
+ resource_name = resource.get("name", "")
249
+ if resource_name:
250
+ backends.append(resource_name)
251
+ return backends
252
+
253
+ def get_all_frontend_resources():
254
+ """Get all frontend resource names."""
255
+ frontends = []
256
+ for app_resource in APP_RESOURCES:
257
+ resources = app_resource.get("resources", [])
258
+ for resource in resources:
259
+ if resource.get("frontend", False):
260
+ resource_name = resource.get("name", "")
261
+ if resource_name:
262
+ frontends.append(resource_name)
263
+ return frontends
264
+
265
+
266
+ # =============================================================================
267
+ # 📊 SNAPSHOT & TOPOLOGY GENERATION
268
+ # =============================================================================
269
+
270
+ def _render_product_stack_file(resource):
271
+ """Render a product stack resource file content."""
272
+ resource_name = resource.get("name", "unknown")
273
+ lines = [
274
+ "# ----------------------------------------------------------------------------",
275
+ "# AUTOGENERATED FILE - DO NOT EDIT",
276
+ "# ----------------------------------------------------------------------------",
277
+ "",
278
+ "# Product Stack: " + resource_name,
279
+ "",
280
+ "## Stack Info",
281
+ ]
282
+
283
+ for key in ["name", "appType", "stack", "port"]:
284
+ if key in resource:
285
+ lines.append("- " + key + ": `" + str(resource[key]) + "`")
286
+
287
+ return "\n".join(lines) + "\n"
288
+
289
+ def _render_product_stacks_index(resources):
290
+ """Render the product stacks index file content."""
291
+ lines = [
292
+ "# Product Stacks Index",
293
+ "",
294
+ "## Services",
295
+ "",
296
+ ]
297
+
298
+ for resource in resources:
299
+ resource_name = resource.get("name", "unknown")
300
+ lines.append("- `" + resource_name + "`")
301
+
302
+ return "\n".join(lines) + "\n"
303
+
304
+ def _write_file_if_changed(path, content):
305
+ """Write file only if content changed. Returns True if written."""
306
+ # Read existing file (returns empty string on error via shell)
307
+ existing = str(local("cat " + path + " 2>/dev/null || echo ''", quiet=True, echo_off=True))
308
+
309
+ if existing != content:
310
+ local("mkdir -p $(dirname " + path + ") && echo '" + content + "' > " + path, quiet=True, echo_off=True)
311
+ return True
312
+ return False
313
+
314
+ def _render_star_file(description, var_name, data):
315
+ """Render a Starlark file with data."""
316
+ return "# " + description + "\n" + var_name + " = " + str(data) + "\n"
317
+
318
+ def generate_product_stack_topology():
319
+ """Generate product stack topology folders."""
320
+ local("mkdir -p " + PRODUCT_TOPOLOGY_DIR, quiet=True)
321
+
322
+ changes = 0
323
+ for resource in APP_RESOURCES:
324
+ stack = resource.get("name", "unknown")
325
+ stack_file = PRODUCT_TOPOLOGY_DIR + "/" + stack + "/stack.star"
326
+ if _write_file_if_changed(stack_file, _render_product_stack_file(resource)):
327
+ changes += 1
328
+
329
+ index_path = PRODUCT_TOPOLOGY_DIR + "/INDEX.star"
330
+ if _write_file_if_changed(index_path, _render_product_stacks_index(APP_RESOURCES)):
331
+ changes += 1
332
+
333
+ if changes > 0:
334
+ print("Regenerated {} product stack files".format(changes))
335
+
336
+ def generate_product_snapshot_topology():
337
+ """Generate product snapshot topology files."""
338
+ local("mkdir -p " + PRODUCT_SNAPSHOT_DIR, quiet=True)
339
+
340
+ services_path = PRODUCT_SNAPSHOT_DIR + "/services.star"
341
+ deps_path = PRODUCT_SNAPSHOT_DIR + "/dependencies.star"
342
+ aliases_path = PRODUCT_SNAPSHOT_DIR + "/aliases.star"
343
+ paths_path = PRODUCT_SNAPSHOT_DIR + "/paths.star"
344
+ index_path = PRODUCT_SNAPSHOT_DIR + "/index.star"
345
+
346
+ changes = 0
347
+ if _write_file_if_changed(services_path, _render_star_file("Services", "APP_RESOURCES_AUTOGENERATED", APP_RESOURCES)):
348
+ changes += 1
349
+ if _write_file_if_changed(deps_path, _render_star_file("Dependencies", "RESOURCE_DEPENDENCIES_AUTOGENERATED", RESOURCE_DEPENDENCIES)):
350
+ changes += 1
351
+ if _write_file_if_changed(aliases_path, _render_star_file("Aliases", "RESOURCE_ALIASES_AUTOGENERATED", RESOURCE_ALIASES)):
352
+ changes += 1
353
+ if _write_file_if_changed(paths_path, _render_star_file("Path Map", "RESOURCE_PATH_MAP_AUTOGENERATED", RESOURCE_PATH_MAP)):
354
+ changes += 1
355
+ if _write_file_if_changed(index_path, _render_star_file("Index", "INDEX", {"services": len(APP_RESOURCES)})):
356
+ changes += 1
357
+
358
+ if changes > 0:
359
+ print("Regenerated {} snapshot files".format(changes))
360
+
361
+
362
+ # =============================================================================
363
+ # 📄 YAML MANIFEST GENERATION
364
+ # =============================================================================
365
+
366
+ def _json_to_yaml(json_data, indent=0):
367
+ """Convert JSON to YAML format."""
368
+ spaces = " " * indent
369
+ yaml_lines = []
370
+
371
+ if type(json_data) == "dict":
372
+ priority_fields = ["appName", "appType", "stack", "port"]
373
+ ordered_keys = []
374
+ for key in priority_fields:
375
+ if key in json_data:
376
+ ordered_keys.append(key)
377
+ remaining = [k for k in json_data if k not in priority_fields and not k.startswith("_")]
378
+ for key in sorted(remaining):
379
+ ordered_keys.append(key)
380
+
381
+ for key in ordered_keys:
382
+ value = json_data[key]
383
+ if str(key).startswith("_"):
384
+ continue
385
+ if type(value) == "dict":
386
+ yaml_lines.append(spaces + str(key) + ":")
387
+ nested = _json_to_yaml(value, indent + 1)
388
+ if nested:
389
+ yaml_lines.append(nested)
390
+ elif type(value) == "list":
391
+ yaml_lines.append(spaces + str(key) + ":")
392
+ for item in value:
393
+ if type(item) == "dict":
394
+ yaml_lines.append(spaces + " - " + _json_to_yaml(item, 0).strip())
395
+ else:
396
+ yaml_lines.append(spaces + " - " + str(item))
397
+ else:
398
+ yaml_lines.append(spaces + str(key) + ": " + str(value))
399
+ elif type(json_data) == "list":
400
+ for item in json_data:
401
+ if type(item) == "dict":
402
+ yaml_lines.append(spaces + "- " + _json_to_yaml(item, 0).strip())
403
+ else:
404
+ yaml_lines.append(spaces + "- " + str(item))
405
+ else:
406
+ yaml_lines.append(spaces + str(json_data))
407
+
408
+ return "\n".join(yaml_lines)
409
+
410
+ def _find_manifest_files(root, manifest_filename):
411
+ """Find manifest files, handling glob patterns in root paths."""
412
+ files = []
413
+
414
+ # Check if root contains glob patterns
415
+ project_root = os.environ.get('TDK_PROJECT_ROOT', '.')
416
+ if '*' in root or '?' in root:
417
+ # Use bash to expand glob and find files
418
+ cmd = "cd " + project_root + " && bash -c 'for dir in " + root + "; do if [ -d \"$dir\" ]; then find \"$dir\" -maxdepth 3 -type f -name \"" + manifest_filename + "\" 2>/dev/null; fi; done'"
419
+ result = str(local(cmd, quiet=True, echo_off=True)).strip()
420
+ else:
421
+ # Resolve relative paths against project root (non-glob)
422
+ if root.startswith('/'):
423
+ full_path = root
424
+ else:
425
+ full_path = project_root + "/" + root
426
+ cmd = "find " + full_path + " -type f -name '" + manifest_filename + "' 2>/dev/null"
427
+ result = str(local(cmd, quiet=True, echo_off=True)).strip()
428
+
429
+ if result:
430
+ for f in result.split("\n"):
431
+ f = f.strip()
432
+ if f and f not in files:
433
+ files.append(f)
434
+
435
+ return files
436
+
437
+ def _generate_yaml_from_json_manifests():
438
+ """Generate YAML manifests from JSON manifests."""
439
+ generated_count = 0
440
+ for root in DISCOVERY_SCAN_ROOTS:
441
+ json_files = _find_manifest_files(root, MANIFEST_FILENAME)
442
+
443
+ for json_file in json_files:
444
+ yaml_file = json_file.replace(MANIFEST_FILENAME, MANIFEST_FILENAME_YAML)
445
+ load_result = ManifestLoader.load_from_file(json_file)
446
+ if not load_result.error and load_result.manifest:
447
+ yaml_content = _json_to_yaml(load_result.manifest)
448
+ if _write_file_if_changed(yaml_file, yaml_content):
449
+ generated_count += 1
450
+
451
+ if generated_count > 0:
452
+ print("Generated {} YAML manifests".format(generated_count))
453
+
454
+ def load_yaml_manifests_as_resources():
455
+ """Load YAML manifests as Tilt resources (via local_resource)."""
456
+ _generate_yaml_from_json_manifests()
457
+
458
+ yaml_files = []
459
+ for root in DISCOVERY_SCAN_ROOTS:
460
+ files = _find_manifest_files(root, MANIFEST_FILENAME_YAML)
461
+ for f in files:
462
+ if f not in yaml_files:
463
+ yaml_files.append(f)
464
+
465
+ if not yaml_files:
466
+ return
467
+
468
+ created_resources = {}
469
+ for yaml_file in yaml_files:
470
+ json_file = yaml_file.replace(MANIFEST_FILENAME_YAML, MANIFEST_FILENAME)
471
+ load_result = ManifestLoader.load_from_file(json_file)
472
+ resource_name = ""
473
+
474
+ if load_result.error:
475
+ continue
476
+
477
+ manifest = load_result.manifest
478
+ if manifest:
479
+ resource_name = manifest.get("appName", "")
480
+
481
+ if not resource_name:
482
+ continue
483
+
484
+ resource_name = resource_name + "-yaml"
485
+
486
+ if resource_name in created_resources:
487
+ continue
488
+
489
+ yaml_path_display = yaml_file
490
+ if yaml_file.startswith("../../../../"):
491
+ yaml_path_display = yaml_file[12:]
492
+
493
+ local_resource(
494
+ name=resource_name,
495
+ cmd="echo 'YAML manifest: " + yaml_path_display + "'",
496
+ deps=[yaml_file],
497
+ labels=["yaml-manifest"],
498
+ )
499
+
500
+ created_resources[resource_name] = True
501
+
502
+ # Generate YAML manifests at module load time
503
+ load_yaml_manifests_as_resources()
504
+
505
+ # Load project defaults from spec.master if available
506
+ project_root = os.environ.get('TDK_PROJECT_ROOT', '')
507
+ if project_root:
508
+ project_defaults = Config.load_project_defaults(project_root)
509
+ if project_defaults:
510
+ Config = struct(
511
+ load_project_defaults=Config.load_project_defaults,
512
+ DEFAULTS=project_defaults.DEFAULTS,
513
+ FOCUS_PRE_ALPHA=project_defaults.FOCUS_PRE_ALPHA,
514
+ FOCUS_ALPHA=project_defaults.FOCUS_ALPHA,
515
+ FOCUS_BETA=project_defaults.FOCUS_BETA,
516
+ GLOBAL=Config.GLOBAL,
517
+ INFRA_RESOURCES=Config.INFRA_RESOURCES,
518
+ CORE_INFRA=Config.CORE_INFRA,
519
+ INFRA_STACK_MAP=Config.INFRA_STACK_MAP,
520
+ OPTIONAL_INFRA=Config.OPTIONAL_INFRA,
521
+ DDD_LIBS=Config.DDD_LIBS,
522
+
523
+ )
524
+
525
+ # Export config data
526
+ OPTIONAL_INFRA_EXPORT = OPTIONAL_INFRA
527
+ CORE_INFRA_EXPORT = CORE_INFRA
528
+ INFRA_STACK_MAP_EXPORT = INFRA_STACK_MAP
529
+ DEFAULTS_EXPORT = Config.DEFAULTS
530
+ DDD_LIBS_EXPORT = DDD_LIBS
531
+ GLOBAL_CONFIG_EXPORT = GLOBAL_CONFIG
532
+ INFRA_RESOURCES_EXPORT = INFRA_RESOURCES
533
+
534
+
535
+ # Export focus lists (populated from project spec.master)
536
+ FOCUS_PRE_ALPHA_EXPORT = Config.FOCUS_PRE_ALPHA
537
+ FOCUS_ALPHA_EXPORT = Config.FOCUS_ALPHA
538
+ FOCUS_BETA_EXPORT = Config.FOCUS_BETA
539
+
540
+ def get_platform_libs_export():
541
+ return get_platform_libs()
542
+
543
+ def get_product_libs_export():
544
+ return get_product_libs()