@tdk-landscape/tdk-cli-core 1.3.45 → 1.3.48

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 (431) hide show
  1. package/.github/workflows/release-binaries.yml +54 -0
  2. package/.pre-commit-config.yaml +57 -0
  3. package/Makefile +92 -0
  4. package/bun.lock +538 -0
  5. package/cli/AGENTS.md +390 -0
  6. package/cli/DESIGN.md +735 -0
  7. package/cli/biome.json +44 -0
  8. package/cli/dependency-graph.svg +1135 -0
  9. package/cli/knip.json +6 -0
  10. package/cli/package.json +77 -0
  11. package/cli/pnpm-lock.yaml +2305 -0
  12. package/cli/pnpm-workspace.yaml +2 -0
  13. package/cli/shared-platform-engineering/docker-templates/bun-hoisted-symlink-fix.sh +23 -0
  14. package/cli/shared-platform-engineering/docker-templates/infisical-entrypoint.sh +464 -0
  15. package/cli/shared-platform-engineering/docker-templates/install-deps.sh +152 -0
  16. package/cli/shared-platform-engineering/docker-templates/prisma-bun-client-link-fix.sh +12 -0
  17. package/cli/shared-platform-engineering/docker-templates/prisma-normalize-client.sh +56 -0
  18. package/{dist/cli.js → cli/src/cli.ts} +21 -13
  19. package/cli/src/commands/__tests__/always-enabled-infra.test.ts +115 -0
  20. package/cli/src/commands/__tests__/config.test.ts +146 -0
  21. package/cli/src/commands/__tests__/doctor.test.ts +557 -0
  22. package/cli/src/commands/__tests__/error-handling.test.ts +78 -0
  23. package/cli/src/commands/__tests__/phases-model.test.ts +163 -0
  24. package/cli/src/commands/__tests__/project-resource-features-e2e.test.ts +207 -0
  25. package/cli/src/commands/__tests__/project.test.ts +68 -0
  26. package/cli/src/commands/__tests__/resource.test.ts +226 -0
  27. package/cli/src/commands/__tests__/saas-starter-integration.test.ts +196 -0
  28. package/cli/src/commands/__tests__/stack-features-e2e.test.ts +129 -0
  29. package/cli/src/commands/__tests__/upgrade.test.ts +101 -0
  30. package/{dist/commands/completion.js → cli/src/commands/completion.ts} +78 -72
  31. package/cli/src/commands/config.ts +299 -0
  32. package/cli/src/commands/doctor.ts +790 -0
  33. package/cli/src/commands/down.ts +37 -0
  34. package/cli/src/commands/help.ts +150 -0
  35. package/cli/src/commands/networks-design.md +80 -0
  36. package/cli/src/commands/networks.ts +332 -0
  37. package/cli/src/commands/project.ts +439 -0
  38. package/cli/src/commands/projects.ts +82 -0
  39. package/cli/src/commands/resource.ts +652 -0
  40. package/cli/src/commands/resources.ts +95 -0
  41. package/cli/src/commands/stack.ts +128 -0
  42. package/cli/src/commands/stacks.ts +52 -0
  43. package/cli/src/commands/status.ts +88 -0
  44. package/cli/src/commands/ui.tsx +843 -0
  45. package/cli/src/commands/up.ts +150 -0
  46. package/cli/src/commands/upgrade.ts +570 -0
  47. package/{dist/commands/version.js → cli/src/commands/version.ts} +5 -5
  48. package/cli/src/components/Accessible.tsx +21 -0
  49. package/cli/src/components/BaseTooltip.tsx +66 -0
  50. package/cli/src/components/DetailPanel.tsx +146 -0
  51. package/cli/src/components/FileTree.tsx +89 -0
  52. package/cli/src/components/ResourceSelectInput.tsx +30 -0
  53. package/cli/src/components/ResourceTable.tsx +78 -0
  54. package/cli/src/components/TabBar.tsx +53 -0
  55. package/cli/src/components/Tooltip.tsx +11 -0
  56. package/{dist/components/index.js → cli/src/components/index.ts} +0 -1
  57. package/cli/src/config/platform-standards.test.ts +11 -0
  58. package/cli/src/config/platform-standards.ts +196 -0
  59. package/cli/src/generator/__tests__/extension-fetch.test.ts +220 -0
  60. package/cli/src/generator/__tests__/template-engine.test.ts +70 -0
  61. package/cli/src/generator/extension-fetch.ts +279 -0
  62. package/cli/src/generator/template-engine.ts +754 -0
  63. package/cli/src/index.ts +86 -0
  64. package/cli/src/templates.d.ts +4 -0
  65. package/cli/src/types/index.ts +351 -0
  66. package/cli/src/utils/__tests__/cache.test.ts +98 -0
  67. package/cli/src/utils/__tests__/formatting.test.ts +170 -0
  68. package/cli/src/utils/__tests__/paths.test.ts +180 -0
  69. package/cli/src/utils/__tests__/port-assignment.test.ts +121 -0
  70. package/cli/src/utils/__tests__/service-urls.test.ts +174 -0
  71. package/cli/src/utils/__tests__/services.test.ts +531 -0
  72. package/cli/src/utils/__tests__/tilt.test.ts +143 -0
  73. package/cli/src/utils/__tests__/validation.test.ts +148 -0
  74. package/cli/src/utils/cache.ts +76 -0
  75. package/cli/src/utils/command-helpers.ts +54 -0
  76. package/cli/src/utils/constants.ts +126 -0
  77. package/cli/src/utils/discovery-context.ts +49 -0
  78. package/cli/src/utils/docker.ts +20 -0
  79. package/cli/src/utils/doctor-runtime.ts +604 -0
  80. package/cli/src/utils/env-validator.ts +139 -0
  81. package/cli/src/utils/errors.ts +168 -0
  82. package/cli/src/utils/file-helpers.ts +62 -0
  83. package/cli/src/utils/formatting.ts +215 -0
  84. package/cli/src/utils/paths.ts +53 -0
  85. package/cli/src/utils/port-assignment.ts +106 -0
  86. package/cli/src/utils/project-features.ts +118 -0
  87. package/cli/src/utils/project-templates.ts +28 -0
  88. package/cli/src/utils/prompt.ts +123 -0
  89. package/cli/src/utils/resource-features.ts +209 -0
  90. package/cli/src/utils/service-urls.ts +124 -0
  91. package/cli/src/utils/services.ts +363 -0
  92. package/cli/src/utils/stack-features.ts +30 -0
  93. package/cli/src/utils/tar.ts +24 -0
  94. package/cli/src/utils/tilt.ts +113 -0
  95. package/cli/src/utils/validation.ts +67 -0
  96. package/{templates → cli/templates}/Tiltfile.hbs +1 -1
  97. package/cli/tsconfig.json +25 -0
  98. package/cli/tsconfig.typecheck.json +8 -0
  99. package/docs/FEATURES.md +279 -0
  100. package/engine/topologies/platform/docker/dockerfile/dockerfile.star +3 -3
  101. package/engine/topologies/platform/docker/generators/golden_docker_generator_v2.star +1 -1
  102. package/engine/topologies/platform/docker/layers/l2_dependency_layers.star +0 -3
  103. package/engine/topologies/platform/docker/layers/l3_builder_layers.star +1 -1
  104. package/engine/topologies/platform/docker/layers/l4_runtime_layers.star +1 -1
  105. package/engine/topologies/platform/docker/secrets/infisical.star +19 -47
  106. package/engine/topologies/tilt/resources/orchestrator/apply_migrator_orchestration.star +1 -1
  107. package/install.sh +242 -0
  108. package/knip.json +10 -0
  109. package/openspec/changes/add-vscode-tdk-troubleshooter-plugin/.openspec.yaml +2 -0
  110. package/openspec/changes/add-vscode-tdk-troubleshooter-plugin/design.md +102 -0
  111. package/openspec/changes/add-vscode-tdk-troubleshooter-plugin/proposal.md +31 -0
  112. package/openspec/changes/add-vscode-tdk-troubleshooter-plugin/specs/vscode-tdk-notifications/spec.md +48 -0
  113. package/openspec/changes/add-vscode-tdk-troubleshooter-plugin/specs/vscode-tdk-orchestration/spec.md +63 -0
  114. package/openspec/changes/add-vscode-tdk-troubleshooter-plugin/specs/vscode-tdk-troubleshooting/spec.md +56 -0
  115. package/openspec/changes/add-vscode-tdk-troubleshooter-plugin/tasks.md +61 -0
  116. package/openspec/changes/archive/2026-04-28-rename-domain-to-stack-service-to-resource/.openspec.yaml +2 -0
  117. package/openspec/changes/archive/2026-04-28-rename-domain-to-stack-service-to-resource/design.md +114 -0
  118. package/openspec/changes/archive/2026-04-28-rename-domain-to-stack-service-to-resource/proposal.md +32 -0
  119. package/openspec/changes/archive/2026-04-28-rename-domain-to-stack-service-to-resource/specs/manifest-field-migration/spec.md +20 -0
  120. package/openspec/changes/archive/2026-04-28-rename-domain-to-stack-service-to-resource/specs/resource-terminology-standard/spec.md +20 -0
  121. package/openspec/changes/archive/2026-04-28-rename-domain-to-stack-service-to-resource/specs/stack-terminology-standard/spec.md +20 -0
  122. package/openspec/changes/archive/2026-04-28-rename-domain-to-stack-service-to-resource/tasks.md +194 -0
  123. package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/.openspec.yaml +2 -0
  124. package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/design.md +111 -0
  125. package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/proposal.md +33 -0
  126. package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/specs/function-naming-alignment/spec.md +12 -0
  127. package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/specs/optional-parameter-patterns/spec.md +18 -0
  128. package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/specs/parameter-documentation/spec.md +16 -0
  129. package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/specs/parameter-naming-consistency/spec.md +12 -0
  130. package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/specs/parameter-ordering-standard/spec.md +12 -0
  131. package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/tasks.md +194 -0
  132. package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/.openspec.yaml +2 -0
  133. package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/codebase-audit-report.md +62 -0
  134. package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/design.md +84 -0
  135. package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/proposal.md +31 -0
  136. package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/specs/codebase-audit/spec.md +27 -0
  137. package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/specs/dead-code-cleanup/spec.md +27 -0
  138. package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/specs/test-gap-fill/spec.md +23 -0
  139. package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/specs/todo-resolution/spec.md +27 -0
  140. package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/tasks.md +39 -0
  141. package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/.openspec.yaml +5 -0
  142. package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/design.md +94 -0
  143. package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/proposal.md +46 -0
  144. package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/ai-slop-cleanup/CLEANUP_REPORT_8.md +174 -0
  145. package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/ai-slop-cleanup/CRITICAL_ASSESSMENT_8.md +160 -0
  146. package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/circular-deps/CRITICAL_ASSESSMENT_4.md +381 -0
  147. package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/dead-code/CRITICAL_ASSESSMENT_3.md +316 -0
  148. package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/deduplication/CRITICAL_ASSESSMENT_1.md +290 -0
  149. package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/deduplication/IMPLEMENTATION_SUMMARY_1.md +233 -0
  150. package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/defensive-cleanup/CRITICAL_ASSESSMENT_6.md +835 -0
  151. package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/legacy-cleanup/CRITICAL_ASSESSMENT_7.md +254 -0
  152. package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/legacy-cleanup/IMPLEMENTATION_SUMMARY_7.md +187 -0
  153. package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/strong-typing/CRITICAL_ASSESSMENT_5.md +269 -0
  154. package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/type-consolidation/CRITICAL_ASSESSMENT_2.md +312 -0
  155. package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/type-consolidation/IMPLEMENTATION_SUMMARY.md +170 -0
  156. package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/tasks.md +137 -0
  157. package/openspec/changes/archive/2026-06-25-landscape-consolidation/.openspec.yaml +2 -0
  158. package/openspec/changes/archive/2026-06-25-landscape-consolidation/design.md +80 -0
  159. package/openspec/changes/archive/2026-06-25-landscape-consolidation/proposal.md +40 -0
  160. package/openspec/changes/archive/2026-06-25-landscape-consolidation/spec.md +52 -0
  161. package/openspec/changes/archive/2026-06-25-landscape-consolidation/tasks.md +51 -0
  162. package/openspec/changes/archive/2026-09-17-fix-generated-stack-boot/.openspec.yaml +2 -0
  163. package/openspec/changes/archive/2026-09-17-fix-generated-stack-boot/design.md +43 -0
  164. package/openspec/changes/archive/2026-09-17-fix-generated-stack-boot/proposal.md +29 -0
  165. package/openspec/changes/archive/2026-09-17-fix-generated-stack-boot/specs/self-contained-generated-projects/spec.md +67 -0
  166. package/openspec/changes/archive/2026-09-17-fix-generated-stack-boot/tasks.md +27 -0
  167. package/openspec/specs/manifest-field-migration/spec.md +24 -0
  168. package/openspec/specs/resource-terminology-standard/spec.md +24 -0
  169. package/openspec/specs/self-contained-generated-projects/spec.md +67 -0
  170. package/openspec/specs/stack-terminology-standard/spec.md +24 -0
  171. package/package.json +27 -64
  172. package/platform/services/platform/proxy/service.yaml +14 -0
  173. package/scripts/audit-codebase.sh +213 -0
  174. package/scripts/install-git-hooks.sh +14 -0
  175. package/scripts/release-binaries.sh +94 -0
  176. package/scripts/setup-pre-commit.sh +33 -0
  177. package/scripts/stage-npm-engine.sh +36 -0
  178. package/shared-platform-engineering/docker-templates/bun-hoisted-symlink-fix.sh +23 -0
  179. package/shared-platform-engineering/docker-templates/infisical-entrypoint.sh +464 -0
  180. package/shared-platform-engineering/docker-templates/install-deps.sh +152 -0
  181. package/shared-platform-engineering/docker-templates/prisma-bun-client-link-fix.sh +12 -0
  182. package/shared-platform-engineering/docker-templates/prisma-normalize-client.sh +56 -0
  183. package/tests/tilt-engine/__init__.py +5 -0
  184. package/tests/tilt-engine/conftest.py +182 -0
  185. package/tests/tilt-engine/fixtures/appointment-frontend-service.json +11 -0
  186. package/tests/tilt-engine/fixtures/identity-backend-service.json +12 -0
  187. package/tests/tilt-engine/fixtures/invalid-service.json +4 -0
  188. package/tests/tilt-engine/test_agents_md_generator.py +341 -0
  189. package/tests/tilt-engine/test_agents_md_integration.py +378 -0
  190. package/tests/tilt-engine/test_cache_operations.py +181 -0
  191. package/tests/tilt-engine/test_cache_persistence.py +142 -0
  192. package/tests/tilt-engine/test_code_executor.py +152 -0
  193. package/tests/tilt-engine/test_code_viewer.py +127 -0
  194. package/tests/tilt-engine/test_compose_port_anchors.py +310 -0
  195. package/tests/tilt-engine/test_config_inspector.py +162 -0
  196. package/tests/tilt-engine/test_daemon_focus_mode.py +89 -0
  197. package/tests/tilt-engine/test_daemon_polling.py +122 -0
  198. package/tests/tilt-engine/test_daemon_registration.py +115 -0
  199. package/tests/tilt-engine/test_daemon_validation.py +107 -0
  200. package/tests/tilt-engine/test_docker_generator_bugs.py +493 -0
  201. package/tests/tilt-engine/test_docker_prune.py +119 -0
  202. package/tests/tilt-engine/test_dockerignore_path_bug.py +82 -0
  203. package/tests/tilt-engine/test_file_browser.py +193 -0
  204. package/tests/tilt-engine/test_frontend_basepath_manifest.py +27 -0
  205. package/tests/tilt-engine/test_health_dependencies.py +130 -0
  206. package/tests/tilt-engine/test_health_endpoints.py +118 -0
  207. package/tests/tilt-engine/test_health_tilt_ui.py +85 -0
  208. package/tests/tilt-engine/test_safety_aliases.py +84 -0
  209. package/tests/tilt-engine/test_snapshot_diff.py +136 -0
  210. package/tests/tilt-engine/test_snapshot_persistence.py +163 -0
  211. package/tests/tilt-engine/test_snapshot_scanning.py +123 -0
  212. package/tests/tilt-engine/test_standalone_project_boot.py +61 -0
  213. package/tests/tilt-engine/test_standalone_traefik.py +108 -0
  214. package/dist/cli.d.ts +0 -3
  215. package/dist/cli.d.ts.map +0 -1
  216. package/dist/cli.js.map +0 -1
  217. package/dist/commands/completion.d.ts +0 -3
  218. package/dist/commands/completion.d.ts.map +0 -1
  219. package/dist/commands/completion.js.map +0 -1
  220. package/dist/commands/config.d.ts +0 -3
  221. package/dist/commands/config.d.ts.map +0 -1
  222. package/dist/commands/config.js +0 -231
  223. package/dist/commands/config.js.map +0 -1
  224. package/dist/commands/doctor.d.ts +0 -7
  225. package/dist/commands/doctor.d.ts.map +0 -1
  226. package/dist/commands/doctor.js +0 -597
  227. package/dist/commands/doctor.js.map +0 -1
  228. package/dist/commands/down.d.ts +0 -3
  229. package/dist/commands/down.d.ts.map +0 -1
  230. package/dist/commands/down.js +0 -32
  231. package/dist/commands/down.js.map +0 -1
  232. package/dist/commands/help.d.ts +0 -2
  233. package/dist/commands/help.d.ts.map +0 -1
  234. package/dist/commands/help.js +0 -130
  235. package/dist/commands/help.js.map +0 -1
  236. package/dist/commands/networks.d.ts +0 -3
  237. package/dist/commands/networks.d.ts.map +0 -1
  238. package/dist/commands/networks.js +0 -260
  239. package/dist/commands/networks.js.map +0 -1
  240. package/dist/commands/project.d.ts +0 -3
  241. package/dist/commands/project.d.ts.map +0 -1
  242. package/dist/commands/project.js +0 -373
  243. package/dist/commands/project.js.map +0 -1
  244. package/dist/commands/projects.d.ts +0 -3
  245. package/dist/commands/projects.d.ts.map +0 -1
  246. package/dist/commands/projects.js +0 -71
  247. package/dist/commands/projects.js.map +0 -1
  248. package/dist/commands/resource.d.ts +0 -69
  249. package/dist/commands/resource.d.ts.map +0 -1
  250. package/dist/commands/resource.js +0 -579
  251. package/dist/commands/resource.js.map +0 -1
  252. package/dist/commands/resources.d.ts +0 -3
  253. package/dist/commands/resources.d.ts.map +0 -1
  254. package/dist/commands/resources.js +0 -73
  255. package/dist/commands/resources.js.map +0 -1
  256. package/dist/commands/stack.d.ts +0 -3
  257. package/dist/commands/stack.d.ts.map +0 -1
  258. package/dist/commands/stack.js +0 -97
  259. package/dist/commands/stack.js.map +0 -1
  260. package/dist/commands/stacks.d.ts +0 -3
  261. package/dist/commands/stacks.d.ts.map +0 -1
  262. package/dist/commands/stacks.js +0 -43
  263. package/dist/commands/stacks.js.map +0 -1
  264. package/dist/commands/status.d.ts +0 -3
  265. package/dist/commands/status.d.ts.map +0 -1
  266. package/dist/commands/status.js +0 -71
  267. package/dist/commands/status.js.map +0 -1
  268. package/dist/commands/ui.d.ts +0 -3
  269. package/dist/commands/ui.d.ts.map +0 -1
  270. package/dist/commands/ui.js +0 -445
  271. package/dist/commands/ui.js.map +0 -1
  272. package/dist/commands/up.d.ts +0 -3
  273. package/dist/commands/up.d.ts.map +0 -1
  274. package/dist/commands/up.js +0 -110
  275. package/dist/commands/up.js.map +0 -1
  276. package/dist/commands/upgrade.d.ts +0 -11
  277. package/dist/commands/upgrade.d.ts.map +0 -1
  278. package/dist/commands/upgrade.js +0 -496
  279. package/dist/commands/upgrade.js.map +0 -1
  280. package/dist/commands/version.d.ts +0 -3
  281. package/dist/commands/version.d.ts.map +0 -1
  282. package/dist/commands/version.js.map +0 -1
  283. package/dist/components/Accessible.d.ts +0 -8
  284. package/dist/components/Accessible.d.ts.map +0 -1
  285. package/dist/components/Accessible.js +0 -10
  286. package/dist/components/Accessible.js.map +0 -1
  287. package/dist/components/BaseTooltip.d.ts +0 -4
  288. package/dist/components/BaseTooltip.d.ts.map +0 -1
  289. package/dist/components/BaseTooltip.js +0 -28
  290. package/dist/components/BaseTooltip.js.map +0 -1
  291. package/dist/components/DetailPanel.d.ts +0 -4
  292. package/dist/components/DetailPanel.d.ts.map +0 -1
  293. package/dist/components/DetailPanel.js +0 -18
  294. package/dist/components/DetailPanel.js.map +0 -1
  295. package/dist/components/FileTree.d.ts +0 -4
  296. package/dist/components/FileTree.d.ts.map +0 -1
  297. package/dist/components/FileTree.js +0 -47
  298. package/dist/components/FileTree.js.map +0 -1
  299. package/dist/components/ResourceSelectInput.d.ts +0 -4
  300. package/dist/components/ResourceSelectInput.d.ts.map +0 -1
  301. package/dist/components/ResourceSelectInput.js +0 -7
  302. package/dist/components/ResourceSelectInput.js.map +0 -1
  303. package/dist/components/ResourceTable.d.ts +0 -4
  304. package/dist/components/ResourceTable.d.ts.map +0 -1
  305. package/dist/components/ResourceTable.js +0 -18
  306. package/dist/components/ResourceTable.js.map +0 -1
  307. package/dist/components/TabBar.d.ts +0 -4
  308. package/dist/components/TabBar.d.ts.map +0 -1
  309. package/dist/components/TabBar.js +0 -17
  310. package/dist/components/TabBar.js.map +0 -1
  311. package/dist/components/Tooltip.d.ts +0 -12
  312. package/dist/components/Tooltip.d.ts.map +0 -1
  313. package/dist/components/Tooltip.js +0 -12
  314. package/dist/components/Tooltip.js.map +0 -1
  315. package/dist/components/index.d.ts +0 -8
  316. package/dist/components/index.d.ts.map +0 -1
  317. package/dist/components/index.js.map +0 -1
  318. package/dist/config/platform-standards.d.ts +0 -159
  319. package/dist/config/platform-standards.d.ts.map +0 -1
  320. package/dist/config/platform-standards.js +0 -181
  321. package/dist/config/platform-standards.js.map +0 -1
  322. package/dist/generator/extension-fetch.d.ts +0 -25
  323. package/dist/generator/extension-fetch.d.ts.map +0 -1
  324. package/dist/generator/extension-fetch.js +0 -252
  325. package/dist/generator/extension-fetch.js.map +0 -1
  326. package/dist/generator/template-engine.d.ts +0 -61
  327. package/dist/generator/template-engine.d.ts.map +0 -1
  328. package/dist/generator/template-engine.js +0 -557
  329. package/dist/generator/template-engine.js.map +0 -1
  330. package/dist/index.d.ts +0 -11
  331. package/dist/index.d.ts.map +0 -1
  332. package/dist/index.js +0 -11
  333. package/dist/index.js.map +0 -1
  334. package/dist/types/index.d.ts +0 -255
  335. package/dist/types/index.d.ts.map +0 -1
  336. package/dist/types/index.js +0 -17
  337. package/dist/types/index.js.map +0 -1
  338. package/dist/utils/cache.d.ts +0 -19
  339. package/dist/utils/cache.d.ts.map +0 -1
  340. package/dist/utils/cache.js +0 -64
  341. package/dist/utils/cache.js.map +0 -1
  342. package/dist/utils/command-helpers.d.ts +0 -17
  343. package/dist/utils/command-helpers.d.ts.map +0 -1
  344. package/dist/utils/command-helpers.js +0 -41
  345. package/dist/utils/command-helpers.js.map +0 -1
  346. package/dist/utils/constants.d.ts +0 -52
  347. package/dist/utils/constants.d.ts.map +0 -1
  348. package/dist/utils/constants.js +0 -112
  349. package/dist/utils/constants.js.map +0 -1
  350. package/dist/utils/discovery-context.d.ts +0 -4
  351. package/dist/utils/discovery-context.d.ts.map +0 -1
  352. package/dist/utils/discovery-context.js +0 -39
  353. package/dist/utils/discovery-context.js.map +0 -1
  354. package/dist/utils/docker.d.ts +0 -4
  355. package/dist/utils/docker.d.ts.map +0 -1
  356. package/dist/utils/docker.js +0 -19
  357. package/dist/utils/docker.js.map +0 -1
  358. package/dist/utils/doctor-runtime.d.ts +0 -66
  359. package/dist/utils/doctor-runtime.d.ts.map +0 -1
  360. package/dist/utils/doctor-runtime.js +0 -469
  361. package/dist/utils/doctor-runtime.js.map +0 -1
  362. package/dist/utils/env-validator.d.ts +0 -15
  363. package/dist/utils/env-validator.d.ts.map +0 -1
  364. package/dist/utils/env-validator.js +0 -109
  365. package/dist/utils/env-validator.js.map +0 -1
  366. package/dist/utils/errors.d.ts +0 -31
  367. package/dist/utils/errors.d.ts.map +0 -1
  368. package/dist/utils/errors.js +0 -140
  369. package/dist/utils/errors.js.map +0 -1
  370. package/dist/utils/file-helpers.d.ts +0 -14
  371. package/dist/utils/file-helpers.d.ts.map +0 -1
  372. package/dist/utils/file-helpers.js +0 -45
  373. package/dist/utils/file-helpers.js.map +0 -1
  374. package/dist/utils/formatting.d.ts +0 -30
  375. package/dist/utils/formatting.d.ts.map +0 -1
  376. package/dist/utils/formatting.js +0 -164
  377. package/dist/utils/formatting.js.map +0 -1
  378. package/dist/utils/paths.d.ts +0 -3
  379. package/dist/utils/paths.d.ts.map +0 -1
  380. package/dist/utils/paths.js +0 -41
  381. package/dist/utils/paths.js.map +0 -1
  382. package/dist/utils/port-assignment.d.ts +0 -6
  383. package/dist/utils/port-assignment.d.ts.map +0 -1
  384. package/dist/utils/port-assignment.js +0 -81
  385. package/dist/utils/port-assignment.js.map +0 -1
  386. package/dist/utils/project-features.d.ts +0 -23
  387. package/dist/utils/project-features.d.ts.map +0 -1
  388. package/dist/utils/project-features.js +0 -98
  389. package/dist/utils/project-features.js.map +0 -1
  390. package/dist/utils/project-templates.d.ts +0 -7
  391. package/dist/utils/project-templates.d.ts.map +0 -1
  392. package/dist/utils/project-templates.js +0 -24
  393. package/dist/utils/project-templates.js.map +0 -1
  394. package/dist/utils/prompt.d.ts +0 -28
  395. package/dist/utils/prompt.d.ts.map +0 -1
  396. package/dist/utils/prompt.js +0 -79
  397. package/dist/utils/prompt.js.map +0 -1
  398. package/dist/utils/resource-features.d.ts +0 -29
  399. package/dist/utils/resource-features.d.ts.map +0 -1
  400. package/dist/utils/resource-features.js +0 -171
  401. package/dist/utils/resource-features.js.map +0 -1
  402. package/dist/utils/service-urls.d.ts +0 -30
  403. package/dist/utils/service-urls.d.ts.map +0 -1
  404. package/dist/utils/service-urls.js +0 -93
  405. package/dist/utils/service-urls.js.map +0 -1
  406. package/dist/utils/services.d.ts +0 -18
  407. package/dist/utils/services.d.ts.map +0 -1
  408. package/dist/utils/services.js +0 -291
  409. package/dist/utils/services.js.map +0 -1
  410. package/dist/utils/stack-features.d.ts +0 -22
  411. package/dist/utils/stack-features.d.ts.map +0 -1
  412. package/dist/utils/stack-features.js +0 -13
  413. package/dist/utils/stack-features.js.map +0 -1
  414. package/dist/utils/tar.d.ts +0 -7
  415. package/dist/utils/tar.d.ts.map +0 -1
  416. package/dist/utils/tar.js +0 -21
  417. package/dist/utils/tar.js.map +0 -1
  418. package/dist/utils/tilt.d.ts +0 -19
  419. package/dist/utils/tilt.d.ts.map +0 -1
  420. package/dist/utils/tilt.js +0 -78
  421. package/dist/utils/tilt.js.map +0 -1
  422. package/dist/utils/validation.d.ts +0 -16
  423. package/dist/utils/validation.d.ts.map +0 -1
  424. package/dist/utils/validation.js +0 -58
  425. package/dist/utils/validation.js.map +0 -1
  426. /package/{README.md → cli/README.md} +0 -0
  427. /package/{bin → cli/bin}/tdk.js +0 -0
  428. /package/{templates → cli/templates}/.tiltignore.hbs +0 -0
  429. /package/{templates → cli/templates}/TILT_RESOURCE_DEFAULTS.star.hbs +0 -0
  430. /package/{templates → cli/templates}/TILT_TECH_STACK.star.hbs +0 -0
  431. /package/{templates → cli/templates}/spec.master.hbs +0 -0
@@ -0,0 +1,493 @@
1
+ """
2
+ Tests for Docker and Compose generators to catch parameter mismatch bugs.
3
+
4
+ These tests verify that:
5
+ 1. Docker generator functions are called with correct parameter names (not positional)
6
+ 2. Generated compose files use correct YAML anchors (*backend-port vs *frontend-port)
7
+ 3. Frontend services get correct port anchors (not backend anchors)
8
+ 4. Backend services get correct port anchors (not frontend anchors)
9
+ """
10
+
11
+ import json
12
+ import pytest
13
+ import re
14
+ from pathlib import Path
15
+ from typing import Callable
16
+
17
+ pytestmark = [pytest.mark.fast, pytest.mark.generator]
18
+
19
+
20
+ class TestDockerGeneratorSignatures:
21
+ """Test that Docker generator functions have correct signatures."""
22
+
23
+ def test_generate_frontend_dockerfile_signature(self):
24
+ """Verify frontend generator has expected parameter names."""
25
+ # This test documents the correct parameter names
26
+ # If someone changes the signature, this test will fail
27
+ import inspect
28
+
29
+ # Simulate loading the function (we can't import .star files directly)
30
+ # Instead we verify the source code has the expected signature
31
+ dockerfile_star = Path(__file__).parent.parent.parent / "engine" / "topologies" / "platform" / "docker" / "dockerfile" / "dockerfile.star"
32
+
33
+ if dockerfile_star.exists():
34
+ content = dockerfile_star.read_text()
35
+
36
+ # Check function definition exists with expected params
37
+ assert "def generate_frontend_dockerfile(" in content
38
+ assert "res_path" in content
39
+ assert "resource_name" in content
40
+ assert "port=" in content
41
+ assert "target_path=" in content
42
+ assert "use_nginx=" in content
43
+
44
+ # Verify it uses keyword args when calling L4_generate_orchestrator
45
+ assert "res_path=res_path" in content
46
+ assert "res_type='frontend'" in content
47
+ assert "port=port" in content
48
+
49
+ def test_generate_app_dockerfile_signature(self):
50
+ """Verify backend generator has expected parameter names."""
51
+ dockerfile_star = Path(__file__).parent.parent.parent / "engine" / "topologies" / "platform" / "docker" / "dockerfile" / "dockerfile.star"
52
+
53
+ if dockerfile_star.exists():
54
+ content = dockerfile_star.read_text()
55
+
56
+ # Check function definition exists
57
+ assert "def generate_app_dockerfile(" in content
58
+ assert "res_path" in content
59
+ assert "resource_name" in content
60
+ assert "port=" in content
61
+
62
+ # Verify it uses keyword args when calling L4_generate_orchestrator
63
+ assert "res_path=res_path" in content
64
+ assert "res_type='backend'" in content
65
+ assert "port=port" in content
66
+
67
+ def test_l4_generate_orchestrator_signature(self):
68
+ """Verify L4_generate_orchestrator has expected parameter order."""
69
+ golden_generator = Path(__file__).parent.parent.parent / "engine" / "topologies" / "platform" / "docker" / "generators" / "golden_docker_generator_v2.star"
70
+
71
+ if golden_generator.exists():
72
+ content = golden_generator.read_text()
73
+
74
+ # Check function signature - this is where bugs happen!
75
+ # use_nginx is 4th param, port is 5th param
76
+ # If someone passes use_nginx positionally as 3rd arg after res_path, res_type, resource_name
77
+ # it becomes use_nginx instead of port!
78
+ match = re.search(
79
+ r'def L4_generate_orchestrator\([^)]+\)',
80
+ content
81
+ )
82
+ if match:
83
+ sig = match.group(0)
84
+ # Verify parameter order that caused the bug
85
+ params = ['res_path', 'res_type', 'resource_name', 'use_nginx', 'port']
86
+ for param in params:
87
+ assert param in sig, f"Missing param: {param}"
88
+
89
+
90
+ class TestComposePortAnchors:
91
+ """Test that compose files use correct YAML anchors for ports."""
92
+
93
+ @pytest.fixture
94
+ def sample_backend_entry(self) -> str:
95
+ """Return a sample backend service entry as would be generated."""
96
+ return '''
97
+ identity-management-backend:
98
+ image: identity_identity-management-backend:dev
99
+ restart: unless-stopped
100
+ build:
101
+ context: ../../../../.
102
+ dockerfile: services/product/identity/identity-management-backend/.autogenerated/Dockerfile.app.autogenerated
103
+ env_file:
104
+ - ../../../../services/product/identity/identity-management-backend/.autogenerated/.env.backend.autogenerated
105
+ environment:
106
+ - RESOURCE_NAME=identity
107
+ - PORT=4004
108
+ - INSTANCE_ID=0
109
+ labels:
110
+ - "traefik.enable=true"
111
+ - "traefik.http.routers.identity-management-backend.rule=Host(`identity.backend.{project}.local`)"
112
+ - "traefik.http.routers.identity-management-backend.entrypoints=web"
113
+ - "traefik.http.routers.identity-management-backend.service=identity-management-backend"
114
+ - "traefik.http.services.identity-management-backend.loadbalancer.server.port=4004"
115
+ - "traefik.http.services.identity-management-backend.loadbalancer.healthcheck.path=/api/v1/health"
116
+ networks:
117
+ - {project}_traefik-public
118
+ - {project}_backend
119
+ healthcheck:
120
+ test: ["CMD", "curl", "-f", "--max-time", "5", "http://localhost:4004/api/v1/health"]
121
+ deploy:
122
+ resources:
123
+ limits:
124
+ memory: 512M
125
+ '''
126
+
127
+ @pytest.fixture
128
+ def sample_frontend_entry(self) -> str:
129
+ """Return a sample frontend service entry as would be generated."""
130
+ return '''
131
+ identity-management-frontend:
132
+ image: identity_identity-management-frontend:dev
133
+ build:
134
+ context: ../../../../.
135
+ dockerfile: services/product/identity/identity-management-frontend/.autogenerated/Dockerfile.app.autogenerated
136
+ env_file:
137
+ - ../../../../services/product/identity/identity-management-frontend/.autogenerated/.env.frontend.autogenerated
138
+ environment:
139
+ - VITE_APP_ENV=development
140
+ labels:
141
+ - "traefik.enable=true"
142
+ - "traefik.http.routers.identity-management-frontend.rule=Host(`identity-management-frontend.localhost`)"
143
+ - "traefik.http.routers.identity-management-frontend.entrypoints=web,websecure"
144
+ - "traefik.http.services.identity-management-frontend.loadbalancer.server.port=4000"
145
+ - "traefik.docker.network={project}_traefik-public"
146
+ networks:
147
+ - {project}_traefik-public
148
+ healthcheck:
149
+ test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://127.0.0.1:4000"]
150
+ <<: *frontend-memory-limit
151
+ '''
152
+
153
+ def test_backend_entry_is_detected_as_backend(self, sample_backend_entry: str):
154
+ """Verify backend detection logic identifies backends correctly."""
155
+ # This mirrors the _is_backend_entry function logic
156
+ entry = sample_backend_entry
157
+
158
+ # Backend should NOT be SDK
159
+ assert not ("-sdk:" in entry or entry.strip().startswith("identity-sdk:"))
160
+
161
+ # Backend should NOT have frontend-memory-limit
162
+ assert "<<: *frontend-memory-limit" not in entry
163
+
164
+ # Backend SHOULD have healthcheck with curl
165
+ assert "healthcheck:" in entry
166
+ assert "curl" in entry
167
+
168
+ # Backend port should be 4004
169
+ port_match = re.search(r'loadbalancer\.server\.port=(\d+)', entry)
170
+ if port_match:
171
+ assert port_match.group(1) == "4004", "Backend should use port 4004"
172
+
173
+ def test_frontend_entry_is_not_detected_as_backend(self, sample_frontend_entry: str):
174
+ """Verify backend detection correctly excludes frontends."""
175
+ entry = sample_frontend_entry
176
+
177
+ # Frontend HAS frontend-memory-limit
178
+ assert "<<: *frontend-memory-limit" in entry
179
+
180
+ # Frontend uses wget not curl in healthcheck
181
+ assert "wget" in entry
182
+
183
+ # Frontend port should NOT be backend port
184
+ port_match = re.search(r'loadbalancer\.server\.port=(\d+)', entry)
185
+ if port_match:
186
+ port = port_match.group(1)
187
+ # Frontend port is typically 3000-3999 range
188
+ assert port != "4004", "Frontend should not use backend port 4004"
189
+
190
+ def test_backend_gets_backend_port_anchor(self, sample_backend_entry: str):
191
+ """Verify backend entries get *backend-port anchor."""
192
+ entry = sample_backend_entry
193
+
194
+ # After processing, backend with port 4004 should get *backend-port
195
+ # This tests the _replace_ports_with_anchors logic
196
+ is_backend = "healthcheck:" in entry and "curl" in entry and "<<: *frontend-memory-limit" not in entry
197
+ has_frontend = "<<: *frontend-memory-limit" in entry
198
+
199
+ port = 4004
200
+ if port != 3000 and is_backend:
201
+ # Should use backend-port anchor
202
+ expected = "loadbalancer.server.port=*backend-port"
203
+ # Simulate the replacement
204
+ processed = entry.replace(f"loadbalancer.server.port={port}", expected)
205
+ assert expected in processed
206
+
207
+ def test_frontend_gets_frontend_port_anchor(self, sample_frontend_entry: str):
208
+ """Verify frontend entries get *frontend-port anchor."""
209
+ entry = sample_frontend_entry
210
+
211
+ # Frontend with non-3000 port and has_frontend=True should get *frontend-port
212
+ is_frontend = "<<: *frontend-memory-limit" in entry
213
+
214
+ port_match = re.search(r'loadbalancer\.server\.port=(\d+)', entry)
215
+ if port_match:
216
+ port = int(port_match.group(1))
217
+ if port != 3000 and is_frontend:
218
+ expected = "loadbalancer.server.port=*frontend-port"
219
+ processed = entry.replace(f"loadbalancer.server.port={port}", expected)
220
+ assert expected in processed
221
+
222
+
223
+ class TestDockerfileGeneration:
224
+ """Test Dockerfile generation for correct EXPOSE statements."""
225
+
226
+ def test_frontend_dockerfile_exposes_port_not_path(self):
227
+ """Verify frontend Dockerfile EXPOSEs a port number, not a path."""
228
+ # The bug was: EXPOSE /usr/share/nginx/html (path instead of port)
229
+ # This happens when target_path is passed as port parameter positionally
230
+
231
+ # Simulate the function signature issue
232
+ def generate_frontend_dockerfile_bad(res_path, resource_name, port=3000, target_path='/app/dist', use_nginx=False):
233
+ """Buggy version with positional args."""
234
+ # If called as Docker.frontend(path, name, use_nginx)
235
+ # then use_nginx (bool) becomes port!
236
+ if use_nginx:
237
+ return f"EXPOSE {target_path}\n" # BUG: uses path as port
238
+ return f"EXPOSE {port}\n"
239
+
240
+ def generate_frontend_dockerfile_fixed(res_path, resource_name, port=3000, target_path='/app/dist', use_nginx=False):
241
+ """Fixed version with keyword args."""
242
+ if use_nginx:
243
+ return "EXPOSE 80\n" # Nginx uses port 80
244
+ return f"EXPOSE {port}\n"
245
+
246
+ # Test the buggy call pattern
247
+ bad_result = generate_frontend_dockerfile_bad(
248
+ "services/test", "test-svc", True # True (use_nginx) becomes port!
249
+ )
250
+ # In the bug, port=True (which is 1) and use_nginx uses default False
251
+ # Actually the bug was: port gets /usr/share/nginx/html as string!
252
+
253
+ # Test the fixed call pattern
254
+ fixed_result = generate_frontend_dockerfile_fixed(
255
+ res_path="services/test",
256
+ resource_name="test-svc",
257
+ use_nginx=True
258
+ )
259
+ assert "EXPOSE 80" in fixed_result
260
+
261
+ fixed_result_port = generate_frontend_dockerfile_fixed(
262
+ res_path="services/test",
263
+ resource_name="test-svc",
264
+ port=4000,
265
+ use_nginx=False
266
+ )
267
+ assert "EXPOSE 4000" in fixed_result_port
268
+ assert "usr/share" not in fixed_result_port # Should not have path!
269
+
270
+ def test_expose_line_must_be_numeric_port(self):
271
+ """Verify EXPOSE lines in generated Dockerfiles contain numeric ports only."""
272
+ # Regex to match valid EXPOSE lines
273
+ valid_expose_pattern = re.compile(r'^EXPOSE\s+\d+$', re.MULTILINE)
274
+
275
+ # Invalid patterns that should NOT appear
276
+ invalid_patterns = [
277
+ r'EXPOSE\s+/usr/share', # Path instead of port
278
+ r'EXPOSE\s+\*', # Anchor reference (should be resolved)
279
+ r'EXPOSE\s+\w+', # Word instead of number
280
+ ]
281
+
282
+ sample_dockerfile_good = """
283
+ FROM oven/bun:1.3.11-alpine
284
+ WORKDIR /app
285
+ COPY . .
286
+ EXPOSE 4004
287
+ CMD ["bun", "run", "start"]
288
+ """
289
+
290
+ sample_dockerfile_bad = """
291
+ FROM oven/bun:1.3.11-alpine
292
+ WORKDIR /app
293
+ COPY . .
294
+ EXPOSE /usr/share/nginx/html
295
+ CMD ["bun", "run", "start"]
296
+ """
297
+
298
+ # Good Dockerfile should have valid EXPOSE
299
+ assert valid_expose_pattern.search(sample_dockerfile_good)
300
+
301
+ # Bad Dockerfile should NOT have valid EXPOSE
302
+ assert not valid_expose_pattern.search(sample_dockerfile_bad)
303
+
304
+ # Bad Dockerfile should match invalid patterns
305
+ for pattern in invalid_patterns:
306
+ assert re.search(pattern, sample_dockerfile_bad, re.MULTILINE)
307
+
308
+
309
+ class TestComposeFileValidation:
310
+ """Test generated compose files for structural correctness."""
311
+
312
+ @pytest.fixture
313
+ def compose_file_pattern(self) -> dict:
314
+ """Return patterns that must/must not appear in compose files."""
315
+ return {
316
+ "required": [
317
+ r'x-backend-port: &backend-port\s+\d+',
318
+ r'x-frontend-port: &frontend-port\s+\d+',
319
+ ],
320
+ "forbidden": [
321
+ # Port anchors should be resolved, not literal in services
322
+ r'loadbalancer\.server\.port=\*frontend-port[^\s]*', # In wrong place
323
+ r'loadbalancer\.server\.port=\*backend-port[^\s]*', # In wrong place
324
+ ],
325
+ "resource_valid": [
326
+ r'loadbalancer\.server\.port=\*(?:frontend|backend|sdk)-port',
327
+ ]
328
+ }
329
+
330
+ def test_compose_has_required_anchors(self, compose_file_pattern: dict):
331
+ """Verify compose file defines required YAML anchors."""
332
+ # This would test actual generated compose files
333
+ # For now, just verify the patterns are valid regex
334
+ for pattern in compose_file_pattern["required"]:
335
+ assert re.compile(pattern)
336
+
337
+ def test_backend_resource_uses_backend_anchor(self):
338
+ """Backend service port anchor must be *backend-port not *frontend-port."""
339
+ # The bug: backend with port 4004 got *frontend-port because has_frontend=True
340
+
341
+ resource_entry = '''
342
+ identity-management-backend:
343
+ labels:
344
+ - "traefik.http.services.identity-management-backend.loadbalancer.server.port=*backend-port"
345
+ '''
346
+ # Should have backend-port
347
+ assert "*backend-port" in resource_entry
348
+ assert "*frontend-port" not in resource_entry
349
+
350
+ def test_frontend_resource_uses_frontend_anchor(self):
351
+ """Frontend service port anchor must be *frontend-port not *backend-port."""
352
+ resource_entry = '''
353
+ identity-management-frontend:
354
+ <<: *frontend-memory-limit
355
+ labels:
356
+ - "traefik.http.services.identity-management-frontend.loadbalancer.server.port=*frontend-port"
357
+ '''
358
+ # Should have frontend-port
359
+ assert "*frontend-port" in resource_entry
360
+ assert "*backend-port" not in resource_entry
361
+
362
+
363
+ class TestGeneratorCallerPatterns:
364
+ """Test that generator callers use correct argument patterns."""
365
+
366
+ def test_caller_uses_keyword_args_not_positional(self):
367
+ """Verify all Docker generator callers use keyword arguments."""
368
+ # This test inspects the source code to ensure keyword args are used
369
+
370
+ orchestrator_dir = Path(__file__).parent.parent.parent / "engine" / "topologies" / "tilt" / "resources" / "orchestrator"
371
+
372
+ if orchestrator_dir.exists():
373
+ for star_file in orchestrator_dir.glob("*.star"):
374
+ content = star_file.read_text()
375
+
376
+ # Find all Docker.frontend calls
377
+ frontend_calls = re.findall(r'Docker\.frontend\([^)]+\)', content)
378
+ for call in frontend_calls:
379
+ # Must use keyword args, not positional
380
+ assert "=" in call, f"Docker.frontend call must use keyword args: {call}"
381
+
382
+ # Find all Docker.backend calls
383
+ backend_calls = re.findall(r'Docker\.backend\([^)]+\)', content)
384
+ for call in backend_calls:
385
+ # Must use keyword args, not positional
386
+ assert "=" in call, f"Docker.backend call must use keyword args: {call}"
387
+
388
+ def test_no_bare_boolean_positional_args(self):
389
+ """Test that bare boolean values aren't passed positionally."""
390
+ # The bug: Docker.frontend(path, name, True) - True became port!
391
+
392
+ orchestrator_dir = Path(__file__).parent.parent.parent / "engine" / "topologies" / "tilt" / "resources" / "orchestrator"
393
+
394
+ if orchestrator_dir.exists():
395
+ for star_file in orchestrator_dir.glob("*.star"):
396
+ content = star_file.read_text()
397
+ lines = content.split('\n')
398
+
399
+ for i, line in enumerate(lines, 1):
400
+ # Look for patterns like: Docker.frontend(path, name, True)
401
+ # or Docker.backend(path, name, cmd, True)
402
+ suspicious = re.search(
403
+ r'Docker\.(frontend|backend|migrator)\([^)]+,\s*(True|False|\d+)\s*\)',
404
+ line
405
+ )
406
+ if suspicious:
407
+ # If there's no = before the boolean/number, it's positional
408
+ call = suspicious.group(0)
409
+ # Count commas before the boolean/number
410
+ before_bool = call[:call.rfind(suspicious.group(2))]
411
+ if '=' not in before_bool.split('(')[-1]:
412
+ pytest.fail(
413
+ f"Positional boolean/number in Docker call at {star_file}:{i}: {line.strip()}"
414
+ )
415
+
416
+
417
+ class TestL2BunDirectoryGuarantee:
418
+ """Test that l2_deps_manifest always creates /app/node_modules/.bun.
419
+
420
+ Regression test for: Bun's isolated linker only creates /app/node_modules/.bun
421
+ when hoisted packages are present. Without TDK_LICENSE_KEY the Verdaccio
422
+ private registry is skipped, the public-npm install may not produce that
423
+ directory, and the subsequent L3 `COPY --link --from=l2_deps_manifest
424
+ /app/node_modules/.bun` fails with 'not found'.
425
+ """
426
+
427
+ def test_l2_layer_contains_mkdir_bun(self):
428
+ """l2_dependency_layers.star must guarantee /app/node_modules/.bun exists."""
429
+ l2_star = (
430
+ Path(__file__).parent.parent.parent
431
+ / "engine"
432
+ / "topologies"
433
+ / "platform"
434
+ / "docker"
435
+ / "layers"
436
+ / "l2_dependency_layers.star"
437
+ )
438
+ assert l2_star.exists(), "l2_dependency_layers.star not found"
439
+ content = l2_star.read_text()
440
+ assert "mkdir -p /app/node_modules/.bun" in content, (
441
+ "l2_dependency_layers.star must emit `RUN mkdir -p /app/node_modules/.bun` "
442
+ "so L3's COPY --link never fails when Bun skips creating the directory "
443
+ "(e.g. when TDK_LICENSE_KEY is unset and only public npm packages are installed)."
444
+ )
445
+
446
+ def test_mkdir_comes_after_install_deps(self):
447
+ """The mkdir must appear AFTER install-deps.sh runs, not before."""
448
+ l2_star = (
449
+ Path(__file__).parent.parent.parent
450
+ / "engine"
451
+ / "topologies"
452
+ / "platform"
453
+ / "docker"
454
+ / "layers"
455
+ / "l2_dependency_layers.star"
456
+ )
457
+ if not l2_star.exists():
458
+ pytest.skip("l2_dependency_layers.star not found")
459
+ content = l2_star.read_text()
460
+ install_pos = content.find("install-deps.sh")
461
+ mkdir_pos = content.find("mkdir -p /app/node_modules/.bun")
462
+ assert install_pos != -1, "install-deps.sh reference not found"
463
+ assert mkdir_pos != -1, "mkdir -p /app/node_modules/.bun not found"
464
+ assert mkdir_pos > install_pos, (
465
+ "mkdir -p /app/node_modules/.bun must appear AFTER install-deps.sh"
466
+ )
467
+
468
+ def test_l3_backend_copies_bun_dir_from_l2(self):
469
+ """l3_builder_layers.star must copy /app/node_modules/.bun from l2_deps_manifest."""
470
+ l3_star = (
471
+ Path(__file__).parent.parent.parent
472
+ / "engine"
473
+ / "topologies"
474
+ / "platform"
475
+ / "docker"
476
+ / "layers"
477
+ / "l3_builder_layers.star"
478
+ )
479
+ if not l3_star.exists():
480
+ pytest.skip("l3_builder_layers.star not found")
481
+ content = l3_star.read_text()
482
+ # Both backend and frontend builders copy the .bun dir
483
+ assert content.count("node_modules/.bun") >= 2, (
484
+ "Expected at least 2 COPY references to node_modules/.bun in l3_builder_layers.star "
485
+ "(one for backend builder, one for frontend builder)"
486
+ )
487
+ assert "l2_deps_manifest" in content, (
488
+ "L3 must reference l2_deps_manifest as the source for .bun COPY"
489
+ )
490
+
491
+
492
+ if __name__ == "__main__":
493
+ pytest.main([__file__, "-v"])
@@ -0,0 +1,119 @@
1
+ """
2
+ Tests for safe Docker prune script.
3
+ Tests the volume protection mechanisms.
4
+ """
5
+
6
+ import pytest
7
+ import re
8
+
9
+
10
+ @pytest.mark.safety
11
+ class TestDockerPrune:
12
+ """Tests for Docker prune safety."""
13
+
14
+ def test_standard_prune_excludes_verdaccio(self, protected_volumes):
15
+ """
16
+ Scenario: Standard prune excludes verdaccio
17
+ WHEN the script `./scripts/safe-docker-prune.sh standard` runs
18
+ AND there is a volume named `verdaccio-storage`
19
+ THEN the volume SHALL NOT be deleted
20
+ AND it SHALL appear in the "protected volumes" list
21
+ """
22
+ volume_name = "verdaccio-storage"
23
+
24
+ is_protected = self._is_volume_protected(volume_name, protected_volumes)
25
+
26
+ assert is_protected, "verdaccio-storage should be protected"
27
+
28
+ def test_full_prune_excludes_infisical(self, protected_volumes):
29
+ """
30
+ Scenario: Full prune excludes infisical
31
+ WHEN the script `./scripts/safe-docker-prune.sh full` runs
32
+ AND there is a volume named `infisical-db-data`
33
+ THEN the volume SHALL NOT be deleted
34
+ AND the script SHALL log "Protected: infisical-db-data"
35
+ """
36
+ volume_name = "infisical-db-data"
37
+
38
+ is_protected = self._is_volume_protected(volume_name, protected_volumes)
39
+
40
+ assert is_protected, "infisical-db-data should be protected"
41
+
42
+ def test_pattern_matching_protection(self, protected_volumes):
43
+ """
44
+ Scenario: Pattern matching protection
45
+ WHEN a volume matches patterns `*verdaccio*` or `*infisical*`
46
+ THEN it SHALL be protected regardless of exact name
47
+ AND examples like `verdaccio-conf`, `verdaccio-plugins` SHALL be protected
48
+ """
49
+ # Test actual protected volumes
50
+ for volume in protected_volumes:
51
+ is_protected = self._is_volume_protected(volume, protected_volumes)
52
+ assert is_protected, f"{volume} should be protected"
53
+
54
+ # Test pattern matching with additional volumes
55
+ additional_volumes = [
56
+ "verdaccio-logs",
57
+ "verdaccio-backup",
58
+ "infisical-backup",
59
+ ]
60
+
61
+ for volume in additional_volumes:
62
+ is_protected = self._is_volume_protected(volume, protected_volumes)
63
+ assert is_protected, f"{volume} should be protected by pattern matching"
64
+
65
+ def test_listing_protected_volumes(self, protected_volumes):
66
+ """
67
+ Scenario: Listing protected volumes
68
+ WHEN running `docker-protected-volumes` alias
69
+ THEN it SHALL list all protected volume patterns
70
+ AND it SHALL show currently matching volumes
71
+ """
72
+ # Verify all expected patterns are present
73
+ expected_volumes = [
74
+ "verdaccio-storage",
75
+ "verdaccio-conf",
76
+ "verdaccio-plugins",
77
+ "infisical-db-data",
78
+ "infisical-redis-data"
79
+ ]
80
+
81
+ for volume in expected_volumes:
82
+ assert volume in protected_volumes, f"Volume {volume} should be protected"
83
+
84
+ # Verify pattern-based protection works
85
+ pattern_matches = [v for v in protected_volumes if "verdaccio" in v or "infisical" in v]
86
+ assert len(pattern_matches) >= 5, "Should have multiple protected volumes"
87
+
88
+ def test_dangerous_command_warning(self):
89
+ """
90
+ Scenario: Dangerous command warning
91
+ WHEN a user runs `docker system prune -a -f --volumes` directly
92
+ THEN the system SHALL warn (if alias is configured): "DANGER: Will delete protected volumes!"
93
+ AND it SHALL suggest using the safe script instead
94
+ """
95
+ dangerous_command = "docker system prune -a -f --volumes"
96
+
97
+ # Check if command is dangerous
98
+ is_dangerous = "--volumes" in dangerous_command and "prune" in dangerous_command
99
+
100
+ assert is_dangerous, "Command with --volumes flag should be flagged as dangerous"
101
+
102
+ def _is_volume_protected(self, volume_name: str, protected_patterns: list) -> bool:
103
+ """Check if volume name matches any protected pattern."""
104
+ volume_lower = volume_name.lower()
105
+
106
+ # Direct match
107
+ if volume_name in protected_patterns:
108
+ return True
109
+
110
+ # Pattern matching: check if any protected pattern appears in the volume name
111
+ # This handles cases like "verdaccio-logs" matching "verdaccio-storage" pattern
112
+ for pattern in protected_patterns:
113
+ pattern_lower = pattern.lower()
114
+ # Extract the base name (e.g., "verdaccio" from "verdaccio-storage")
115
+ base_name = pattern_lower.split("-")[0]
116
+ if base_name in volume_lower:
117
+ return True
118
+
119
+ return False