@tdk-landscape/tdk-cli-core 1.3.45 → 1.3.49
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/release-binaries.yml +54 -0
- package/.pre-commit-config.yaml +57 -0
- package/Makefile +92 -0
- package/bun.lock +538 -0
- package/cli/AGENTS.md +390 -0
- package/cli/DESIGN.md +735 -0
- package/cli/biome.json +44 -0
- package/cli/dependency-graph.svg +1135 -0
- package/cli/knip.json +6 -0
- package/cli/package.json +77 -0
- package/cli/pnpm-lock.yaml +2305 -0
- package/cli/pnpm-workspace.yaml +2 -0
- package/cli/shared-platform-engineering/docker-templates/bun-hoisted-symlink-fix.sh +23 -0
- package/cli/shared-platform-engineering/docker-templates/infisical-entrypoint.sh +464 -0
- package/cli/shared-platform-engineering/docker-templates/install-deps.sh +152 -0
- package/cli/shared-platform-engineering/docker-templates/prisma-bun-client-link-fix.sh +12 -0
- package/cli/shared-platform-engineering/docker-templates/prisma-normalize-client.sh +56 -0
- package/{dist/cli.js → cli/src/cli.ts} +21 -13
- package/cli/src/commands/__tests__/always-enabled-infra.test.ts +115 -0
- package/cli/src/commands/__tests__/config.test.ts +146 -0
- package/cli/src/commands/__tests__/doctor.test.ts +557 -0
- package/cli/src/commands/__tests__/error-handling.test.ts +78 -0
- package/cli/src/commands/__tests__/phases-model.test.ts +163 -0
- package/cli/src/commands/__tests__/project-resource-features-e2e.test.ts +207 -0
- package/cli/src/commands/__tests__/project.test.ts +68 -0
- package/cli/src/commands/__tests__/resource.test.ts +226 -0
- package/cli/src/commands/__tests__/saas-starter-integration.test.ts +196 -0
- package/cli/src/commands/__tests__/stack-features-e2e.test.ts +129 -0
- package/cli/src/commands/__tests__/upgrade.test.ts +101 -0
- package/{dist/commands/completion.js → cli/src/commands/completion.ts} +78 -72
- package/cli/src/commands/config.ts +299 -0
- package/cli/src/commands/doctor.ts +790 -0
- package/cli/src/commands/down.ts +37 -0
- package/cli/src/commands/help.ts +150 -0
- package/cli/src/commands/networks-design.md +80 -0
- package/cli/src/commands/networks.ts +332 -0
- package/cli/src/commands/project.ts +439 -0
- package/cli/src/commands/projects.ts +82 -0
- package/cli/src/commands/resource.ts +652 -0
- package/cli/src/commands/resources.ts +95 -0
- package/cli/src/commands/stack.ts +128 -0
- package/cli/src/commands/stacks.ts +52 -0
- package/cli/src/commands/status.ts +88 -0
- package/cli/src/commands/ui.tsx +843 -0
- package/cli/src/commands/up.ts +150 -0
- package/cli/src/commands/upgrade.ts +570 -0
- package/{dist/commands/version.js → cli/src/commands/version.ts} +5 -5
- package/cli/src/components/Accessible.tsx +21 -0
- package/cli/src/components/BaseTooltip.tsx +66 -0
- package/cli/src/components/DetailPanel.tsx +146 -0
- package/cli/src/components/FileTree.tsx +89 -0
- package/cli/src/components/ResourceSelectInput.tsx +30 -0
- package/cli/src/components/ResourceTable.tsx +78 -0
- package/cli/src/components/TabBar.tsx +53 -0
- package/cli/src/components/Tooltip.tsx +11 -0
- package/{dist/components/index.js → cli/src/components/index.ts} +0 -1
- package/cli/src/config/platform-standards.test.ts +11 -0
- package/cli/src/config/platform-standards.ts +196 -0
- package/cli/src/generator/__tests__/extension-fetch.test.ts +220 -0
- package/cli/src/generator/__tests__/template-engine.test.ts +70 -0
- package/cli/src/generator/extension-fetch.ts +279 -0
- package/cli/src/generator/template-engine.ts +754 -0
- package/cli/src/index.ts +86 -0
- package/cli/src/templates.d.ts +4 -0
- package/cli/src/types/index.ts +351 -0
- package/cli/src/utils/__tests__/cache.test.ts +98 -0
- package/cli/src/utils/__tests__/formatting.test.ts +170 -0
- package/cli/src/utils/__tests__/paths.test.ts +180 -0
- package/cli/src/utils/__tests__/port-assignment.test.ts +121 -0
- package/cli/src/utils/__tests__/service-urls.test.ts +174 -0
- package/cli/src/utils/__tests__/services.test.ts +531 -0
- package/cli/src/utils/__tests__/tilt.test.ts +143 -0
- package/cli/src/utils/__tests__/validation.test.ts +148 -0
- package/cli/src/utils/cache.ts +76 -0
- package/cli/src/utils/command-helpers.ts +54 -0
- package/cli/src/utils/constants.ts +126 -0
- package/cli/src/utils/discovery-context.ts +49 -0
- package/cli/src/utils/docker.ts +20 -0
- package/cli/src/utils/doctor-runtime.ts +604 -0
- package/cli/src/utils/env-validator.ts +139 -0
- package/cli/src/utils/errors.ts +168 -0
- package/cli/src/utils/file-helpers.ts +62 -0
- package/cli/src/utils/formatting.ts +215 -0
- package/cli/src/utils/paths.ts +53 -0
- package/cli/src/utils/port-assignment.ts +106 -0
- package/cli/src/utils/project-features.ts +118 -0
- package/cli/src/utils/project-templates.ts +28 -0
- package/cli/src/utils/prompt.ts +123 -0
- package/cli/src/utils/resource-features.ts +209 -0
- package/cli/src/utils/service-urls.ts +124 -0
- package/cli/src/utils/services.ts +363 -0
- package/cli/src/utils/stack-features.ts +30 -0
- package/cli/src/utils/tar.ts +24 -0
- package/cli/src/utils/tilt.ts +113 -0
- package/cli/src/utils/validation.ts +67 -0
- package/{templates → cli/templates}/Tiltfile.hbs +1 -1
- package/cli/tsconfig.json +25 -0
- package/cli/tsconfig.typecheck.json +8 -0
- package/docs/FEATURES.md +279 -0
- package/engine/topologies/platform/docker/dockerfile/dockerfile.star +3 -3
- package/engine/topologies/platform/docker/generators/golden_docker_generator_v2.star +1 -1
- package/engine/topologies/platform/docker/layers/l2_dependency_layers.star +0 -3
- package/engine/topologies/platform/docker/layers/l3_builder_layers.star +1 -1
- package/engine/topologies/platform/docker/layers/l4_runtime_layers.star +1 -1
- package/engine/topologies/platform/docker/secrets/infisical.star +19 -47
- package/engine/topologies/tilt/resources/orchestrator/apply_migrator_orchestration.star +1 -1
- package/install.sh +242 -0
- package/knip.json +10 -0
- package/openspec/changes/add-vscode-tdk-troubleshooter-plugin/.openspec.yaml +2 -0
- package/openspec/changes/add-vscode-tdk-troubleshooter-plugin/design.md +102 -0
- package/openspec/changes/add-vscode-tdk-troubleshooter-plugin/proposal.md +31 -0
- package/openspec/changes/add-vscode-tdk-troubleshooter-plugin/specs/vscode-tdk-notifications/spec.md +48 -0
- package/openspec/changes/add-vscode-tdk-troubleshooter-plugin/specs/vscode-tdk-orchestration/spec.md +63 -0
- package/openspec/changes/add-vscode-tdk-troubleshooter-plugin/specs/vscode-tdk-troubleshooting/spec.md +56 -0
- package/openspec/changes/add-vscode-tdk-troubleshooter-plugin/tasks.md +61 -0
- package/openspec/changes/archive/2026-04-28-rename-domain-to-stack-service-to-resource/.openspec.yaml +2 -0
- package/openspec/changes/archive/2026-04-28-rename-domain-to-stack-service-to-resource/design.md +114 -0
- package/openspec/changes/archive/2026-04-28-rename-domain-to-stack-service-to-resource/proposal.md +32 -0
- package/openspec/changes/archive/2026-04-28-rename-domain-to-stack-service-to-resource/specs/manifest-field-migration/spec.md +20 -0
- package/openspec/changes/archive/2026-04-28-rename-domain-to-stack-service-to-resource/specs/resource-terminology-standard/spec.md +20 -0
- package/openspec/changes/archive/2026-04-28-rename-domain-to-stack-service-to-resource/specs/stack-terminology-standard/spec.md +20 -0
- package/openspec/changes/archive/2026-04-28-rename-domain-to-stack-service-to-resource/tasks.md +194 -0
- package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/.openspec.yaml +2 -0
- package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/design.md +111 -0
- package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/proposal.md +33 -0
- package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/specs/function-naming-alignment/spec.md +12 -0
- package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/specs/optional-parameter-patterns/spec.md +18 -0
- package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/specs/parameter-documentation/spec.md +16 -0
- package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/specs/parameter-naming-consistency/spec.md +12 -0
- package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/specs/parameter-ordering-standard/spec.md +12 -0
- package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/tasks.md +194 -0
- package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/.openspec.yaml +2 -0
- package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/codebase-audit-report.md +62 -0
- package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/design.md +84 -0
- package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/proposal.md +31 -0
- package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/specs/codebase-audit/spec.md +27 -0
- package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/specs/dead-code-cleanup/spec.md +27 -0
- package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/specs/test-gap-fill/spec.md +23 -0
- package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/specs/todo-resolution/spec.md +27 -0
- package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/tasks.md +39 -0
- package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/.openspec.yaml +5 -0
- package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/design.md +94 -0
- package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/proposal.md +46 -0
- package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/ai-slop-cleanup/CLEANUP_REPORT_8.md +174 -0
- package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/ai-slop-cleanup/CRITICAL_ASSESSMENT_8.md +160 -0
- package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/circular-deps/CRITICAL_ASSESSMENT_4.md +381 -0
- package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/dead-code/CRITICAL_ASSESSMENT_3.md +316 -0
- package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/deduplication/CRITICAL_ASSESSMENT_1.md +290 -0
- package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/deduplication/IMPLEMENTATION_SUMMARY_1.md +233 -0
- package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/defensive-cleanup/CRITICAL_ASSESSMENT_6.md +835 -0
- package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/legacy-cleanup/CRITICAL_ASSESSMENT_7.md +254 -0
- package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/legacy-cleanup/IMPLEMENTATION_SUMMARY_7.md +187 -0
- package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/strong-typing/CRITICAL_ASSESSMENT_5.md +269 -0
- package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/type-consolidation/CRITICAL_ASSESSMENT_2.md +312 -0
- package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/type-consolidation/IMPLEMENTATION_SUMMARY.md +170 -0
- package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/tasks.md +137 -0
- package/openspec/changes/archive/2026-06-25-landscape-consolidation/.openspec.yaml +2 -0
- package/openspec/changes/archive/2026-06-25-landscape-consolidation/design.md +80 -0
- package/openspec/changes/archive/2026-06-25-landscape-consolidation/proposal.md +40 -0
- package/openspec/changes/archive/2026-06-25-landscape-consolidation/spec.md +52 -0
- package/openspec/changes/archive/2026-06-25-landscape-consolidation/tasks.md +51 -0
- package/openspec/changes/archive/2026-09-17-fix-generated-stack-boot/.openspec.yaml +2 -0
- package/openspec/changes/archive/2026-09-17-fix-generated-stack-boot/design.md +43 -0
- package/openspec/changes/archive/2026-09-17-fix-generated-stack-boot/proposal.md +29 -0
- package/openspec/changes/archive/2026-09-17-fix-generated-stack-boot/specs/self-contained-generated-projects/spec.md +67 -0
- package/openspec/changes/archive/2026-09-17-fix-generated-stack-boot/tasks.md +27 -0
- package/openspec/specs/manifest-field-migration/spec.md +24 -0
- package/openspec/specs/resource-terminology-standard/spec.md +24 -0
- package/openspec/specs/self-contained-generated-projects/spec.md +67 -0
- package/openspec/specs/stack-terminology-standard/spec.md +24 -0
- package/package.json +27 -64
- package/platform/services/platform/proxy/service.yaml +14 -0
- package/scripts/audit-codebase.sh +213 -0
- package/scripts/install-git-hooks.sh +14 -0
- package/scripts/release-binaries.sh +94 -0
- package/scripts/setup-pre-commit.sh +33 -0
- package/scripts/stage-npm-engine.sh +36 -0
- package/shared-platform-engineering/docker-templates/bun-hoisted-symlink-fix.sh +23 -0
- package/shared-platform-engineering/docker-templates/infisical-entrypoint.sh +464 -0
- package/shared-platform-engineering/docker-templates/install-deps.sh +152 -0
- package/shared-platform-engineering/docker-templates/prisma-bun-client-link-fix.sh +12 -0
- package/shared-platform-engineering/docker-templates/prisma-normalize-client.sh +56 -0
- package/tests/tilt-engine/__init__.py +5 -0
- package/tests/tilt-engine/conftest.py +182 -0
- package/tests/tilt-engine/fixtures/appointment-frontend-service.json +11 -0
- package/tests/tilt-engine/fixtures/identity-backend-service.json +12 -0
- package/tests/tilt-engine/fixtures/invalid-service.json +4 -0
- package/tests/tilt-engine/test_agents_md_generator.py +341 -0
- package/tests/tilt-engine/test_agents_md_integration.py +378 -0
- package/tests/tilt-engine/test_cache_operations.py +181 -0
- package/tests/tilt-engine/test_cache_persistence.py +142 -0
- package/tests/tilt-engine/test_code_executor.py +152 -0
- package/tests/tilt-engine/test_code_viewer.py +127 -0
- package/tests/tilt-engine/test_compose_port_anchors.py +310 -0
- package/tests/tilt-engine/test_config_inspector.py +162 -0
- package/tests/tilt-engine/test_daemon_focus_mode.py +89 -0
- package/tests/tilt-engine/test_daemon_polling.py +122 -0
- package/tests/tilt-engine/test_daemon_registration.py +115 -0
- package/tests/tilt-engine/test_daemon_validation.py +107 -0
- package/tests/tilt-engine/test_docker_generator_bugs.py +493 -0
- package/tests/tilt-engine/test_docker_prune.py +119 -0
- package/tests/tilt-engine/test_dockerignore_path_bug.py +82 -0
- package/tests/tilt-engine/test_file_browser.py +193 -0
- package/tests/tilt-engine/test_frontend_basepath_manifest.py +27 -0
- package/tests/tilt-engine/test_health_dependencies.py +130 -0
- package/tests/tilt-engine/test_health_endpoints.py +118 -0
- package/tests/tilt-engine/test_health_tilt_ui.py +85 -0
- package/tests/tilt-engine/test_safety_aliases.py +84 -0
- package/tests/tilt-engine/test_snapshot_diff.py +136 -0
- package/tests/tilt-engine/test_snapshot_persistence.py +163 -0
- package/tests/tilt-engine/test_snapshot_scanning.py +123 -0
- package/tests/tilt-engine/test_standalone_project_boot.py +61 -0
- package/tests/tilt-engine/test_standalone_traefik.py +108 -0
- package/dist/cli.d.ts +0 -3
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/commands/completion.d.ts +0 -3
- package/dist/commands/completion.d.ts.map +0 -1
- package/dist/commands/completion.js.map +0 -1
- package/dist/commands/config.d.ts +0 -3
- package/dist/commands/config.d.ts.map +0 -1
- package/dist/commands/config.js +0 -231
- package/dist/commands/config.js.map +0 -1
- package/dist/commands/doctor.d.ts +0 -7
- package/dist/commands/doctor.d.ts.map +0 -1
- package/dist/commands/doctor.js +0 -597
- package/dist/commands/doctor.js.map +0 -1
- package/dist/commands/down.d.ts +0 -3
- package/dist/commands/down.d.ts.map +0 -1
- package/dist/commands/down.js +0 -32
- package/dist/commands/down.js.map +0 -1
- package/dist/commands/help.d.ts +0 -2
- package/dist/commands/help.d.ts.map +0 -1
- package/dist/commands/help.js +0 -130
- package/dist/commands/help.js.map +0 -1
- package/dist/commands/networks.d.ts +0 -3
- package/dist/commands/networks.d.ts.map +0 -1
- package/dist/commands/networks.js +0 -260
- package/dist/commands/networks.js.map +0 -1
- package/dist/commands/project.d.ts +0 -3
- package/dist/commands/project.d.ts.map +0 -1
- package/dist/commands/project.js +0 -373
- package/dist/commands/project.js.map +0 -1
- package/dist/commands/projects.d.ts +0 -3
- package/dist/commands/projects.d.ts.map +0 -1
- package/dist/commands/projects.js +0 -71
- package/dist/commands/projects.js.map +0 -1
- package/dist/commands/resource.d.ts +0 -69
- package/dist/commands/resource.d.ts.map +0 -1
- package/dist/commands/resource.js +0 -579
- package/dist/commands/resource.js.map +0 -1
- package/dist/commands/resources.d.ts +0 -3
- package/dist/commands/resources.d.ts.map +0 -1
- package/dist/commands/resources.js +0 -73
- package/dist/commands/resources.js.map +0 -1
- package/dist/commands/stack.d.ts +0 -3
- package/dist/commands/stack.d.ts.map +0 -1
- package/dist/commands/stack.js +0 -97
- package/dist/commands/stack.js.map +0 -1
- package/dist/commands/stacks.d.ts +0 -3
- package/dist/commands/stacks.d.ts.map +0 -1
- package/dist/commands/stacks.js +0 -43
- package/dist/commands/stacks.js.map +0 -1
- package/dist/commands/status.d.ts +0 -3
- package/dist/commands/status.d.ts.map +0 -1
- package/dist/commands/status.js +0 -71
- package/dist/commands/status.js.map +0 -1
- package/dist/commands/ui.d.ts +0 -3
- package/dist/commands/ui.d.ts.map +0 -1
- package/dist/commands/ui.js +0 -445
- package/dist/commands/ui.js.map +0 -1
- package/dist/commands/up.d.ts +0 -3
- package/dist/commands/up.d.ts.map +0 -1
- package/dist/commands/up.js +0 -110
- package/dist/commands/up.js.map +0 -1
- package/dist/commands/upgrade.d.ts +0 -11
- package/dist/commands/upgrade.d.ts.map +0 -1
- package/dist/commands/upgrade.js +0 -496
- package/dist/commands/upgrade.js.map +0 -1
- package/dist/commands/version.d.ts +0 -3
- package/dist/commands/version.d.ts.map +0 -1
- package/dist/commands/version.js.map +0 -1
- package/dist/components/Accessible.d.ts +0 -8
- package/dist/components/Accessible.d.ts.map +0 -1
- package/dist/components/Accessible.js +0 -10
- package/dist/components/Accessible.js.map +0 -1
- package/dist/components/BaseTooltip.d.ts +0 -4
- package/dist/components/BaseTooltip.d.ts.map +0 -1
- package/dist/components/BaseTooltip.js +0 -28
- package/dist/components/BaseTooltip.js.map +0 -1
- package/dist/components/DetailPanel.d.ts +0 -4
- package/dist/components/DetailPanel.d.ts.map +0 -1
- package/dist/components/DetailPanel.js +0 -18
- package/dist/components/DetailPanel.js.map +0 -1
- package/dist/components/FileTree.d.ts +0 -4
- package/dist/components/FileTree.d.ts.map +0 -1
- package/dist/components/FileTree.js +0 -47
- package/dist/components/FileTree.js.map +0 -1
- package/dist/components/ResourceSelectInput.d.ts +0 -4
- package/dist/components/ResourceSelectInput.d.ts.map +0 -1
- package/dist/components/ResourceSelectInput.js +0 -7
- package/dist/components/ResourceSelectInput.js.map +0 -1
- package/dist/components/ResourceTable.d.ts +0 -4
- package/dist/components/ResourceTable.d.ts.map +0 -1
- package/dist/components/ResourceTable.js +0 -18
- package/dist/components/ResourceTable.js.map +0 -1
- package/dist/components/TabBar.d.ts +0 -4
- package/dist/components/TabBar.d.ts.map +0 -1
- package/dist/components/TabBar.js +0 -17
- package/dist/components/TabBar.js.map +0 -1
- package/dist/components/Tooltip.d.ts +0 -12
- package/dist/components/Tooltip.d.ts.map +0 -1
- package/dist/components/Tooltip.js +0 -12
- package/dist/components/Tooltip.js.map +0 -1
- package/dist/components/index.d.ts +0 -8
- package/dist/components/index.d.ts.map +0 -1
- package/dist/components/index.js.map +0 -1
- package/dist/config/platform-standards.d.ts +0 -159
- package/dist/config/platform-standards.d.ts.map +0 -1
- package/dist/config/platform-standards.js +0 -181
- package/dist/config/platform-standards.js.map +0 -1
- package/dist/generator/extension-fetch.d.ts +0 -25
- package/dist/generator/extension-fetch.d.ts.map +0 -1
- package/dist/generator/extension-fetch.js +0 -252
- package/dist/generator/extension-fetch.js.map +0 -1
- package/dist/generator/template-engine.d.ts +0 -61
- package/dist/generator/template-engine.d.ts.map +0 -1
- package/dist/generator/template-engine.js +0 -557
- package/dist/generator/template-engine.js.map +0 -1
- package/dist/index.d.ts +0 -11
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -11
- package/dist/index.js.map +0 -1
- package/dist/types/index.d.ts +0 -255
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/index.js +0 -17
- package/dist/types/index.js.map +0 -1
- package/dist/utils/cache.d.ts +0 -19
- package/dist/utils/cache.d.ts.map +0 -1
- package/dist/utils/cache.js +0 -64
- package/dist/utils/cache.js.map +0 -1
- package/dist/utils/command-helpers.d.ts +0 -17
- package/dist/utils/command-helpers.d.ts.map +0 -1
- package/dist/utils/command-helpers.js +0 -41
- package/dist/utils/command-helpers.js.map +0 -1
- package/dist/utils/constants.d.ts +0 -52
- package/dist/utils/constants.d.ts.map +0 -1
- package/dist/utils/constants.js +0 -112
- package/dist/utils/constants.js.map +0 -1
- package/dist/utils/discovery-context.d.ts +0 -4
- package/dist/utils/discovery-context.d.ts.map +0 -1
- package/dist/utils/discovery-context.js +0 -39
- package/dist/utils/discovery-context.js.map +0 -1
- package/dist/utils/docker.d.ts +0 -4
- package/dist/utils/docker.d.ts.map +0 -1
- package/dist/utils/docker.js +0 -19
- package/dist/utils/docker.js.map +0 -1
- package/dist/utils/doctor-runtime.d.ts +0 -66
- package/dist/utils/doctor-runtime.d.ts.map +0 -1
- package/dist/utils/doctor-runtime.js +0 -469
- package/dist/utils/doctor-runtime.js.map +0 -1
- package/dist/utils/env-validator.d.ts +0 -15
- package/dist/utils/env-validator.d.ts.map +0 -1
- package/dist/utils/env-validator.js +0 -109
- package/dist/utils/env-validator.js.map +0 -1
- package/dist/utils/errors.d.ts +0 -31
- package/dist/utils/errors.d.ts.map +0 -1
- package/dist/utils/errors.js +0 -140
- package/dist/utils/errors.js.map +0 -1
- package/dist/utils/file-helpers.d.ts +0 -14
- package/dist/utils/file-helpers.d.ts.map +0 -1
- package/dist/utils/file-helpers.js +0 -45
- package/dist/utils/file-helpers.js.map +0 -1
- package/dist/utils/formatting.d.ts +0 -30
- package/dist/utils/formatting.d.ts.map +0 -1
- package/dist/utils/formatting.js +0 -164
- package/dist/utils/formatting.js.map +0 -1
- package/dist/utils/paths.d.ts +0 -3
- package/dist/utils/paths.d.ts.map +0 -1
- package/dist/utils/paths.js +0 -41
- package/dist/utils/paths.js.map +0 -1
- package/dist/utils/port-assignment.d.ts +0 -6
- package/dist/utils/port-assignment.d.ts.map +0 -1
- package/dist/utils/port-assignment.js +0 -81
- package/dist/utils/port-assignment.js.map +0 -1
- package/dist/utils/project-features.d.ts +0 -23
- package/dist/utils/project-features.d.ts.map +0 -1
- package/dist/utils/project-features.js +0 -98
- package/dist/utils/project-features.js.map +0 -1
- package/dist/utils/project-templates.d.ts +0 -7
- package/dist/utils/project-templates.d.ts.map +0 -1
- package/dist/utils/project-templates.js +0 -24
- package/dist/utils/project-templates.js.map +0 -1
- package/dist/utils/prompt.d.ts +0 -28
- package/dist/utils/prompt.d.ts.map +0 -1
- package/dist/utils/prompt.js +0 -79
- package/dist/utils/prompt.js.map +0 -1
- package/dist/utils/resource-features.d.ts +0 -29
- package/dist/utils/resource-features.d.ts.map +0 -1
- package/dist/utils/resource-features.js +0 -171
- package/dist/utils/resource-features.js.map +0 -1
- package/dist/utils/service-urls.d.ts +0 -30
- package/dist/utils/service-urls.d.ts.map +0 -1
- package/dist/utils/service-urls.js +0 -93
- package/dist/utils/service-urls.js.map +0 -1
- package/dist/utils/services.d.ts +0 -18
- package/dist/utils/services.d.ts.map +0 -1
- package/dist/utils/services.js +0 -291
- package/dist/utils/services.js.map +0 -1
- package/dist/utils/stack-features.d.ts +0 -22
- package/dist/utils/stack-features.d.ts.map +0 -1
- package/dist/utils/stack-features.js +0 -13
- package/dist/utils/stack-features.js.map +0 -1
- package/dist/utils/tar.d.ts +0 -7
- package/dist/utils/tar.d.ts.map +0 -1
- package/dist/utils/tar.js +0 -21
- package/dist/utils/tar.js.map +0 -1
- package/dist/utils/tilt.d.ts +0 -19
- package/dist/utils/tilt.d.ts.map +0 -1
- package/dist/utils/tilt.js +0 -78
- package/dist/utils/tilt.js.map +0 -1
- package/dist/utils/validation.d.ts +0 -16
- package/dist/utils/validation.d.ts.map +0 -1
- package/dist/utils/validation.js +0 -58
- package/dist/utils/validation.js.map +0 -1
- /package/{README.md → cli/README.md} +0 -0
- /package/{bin → cli/bin}/tdk.js +0 -0
- /package/{templates → cli/templates}/.tiltignore.hbs +0 -0
- /package/{templates → cli/templates}/TILT_RESOURCE_DEFAULTS.star.hbs +0 -0
- /package/{templates → cli/templates}/TILT_TECH_STACK.star.hbs +0 -0
- /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
|