@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,152 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tests for code executor component.
|
|
3
|
+
Tests the terminal interface and command safety features.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import pytest
|
|
7
|
+
import asyncio
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import List, Tuple
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@pytest.mark.ide
|
|
13
|
+
class TestCodeExecutor:
|
|
14
|
+
"""Tests for code executor functionality."""
|
|
15
|
+
|
|
16
|
+
def test_allowed_command_execution(self, safe_commands):
|
|
17
|
+
"""
|
|
18
|
+
Scenario: Allowed command execution
|
|
19
|
+
WHEN a user submits `bun test` via the terminal interface
|
|
20
|
+
THEN it SHALL execute the command
|
|
21
|
+
AND it SHALL stream output to the browser in real-time
|
|
22
|
+
"""
|
|
23
|
+
for cmd in safe_commands:
|
|
24
|
+
is_allowed = self._is_command_allowed(cmd)
|
|
25
|
+
assert is_allowed, f"Command should be allowed: {cmd}"
|
|
26
|
+
|
|
27
|
+
def test_dangerous_command_blocked(self, dangerous_commands):
|
|
28
|
+
"""
|
|
29
|
+
Scenario: Dangerous command blocked
|
|
30
|
+
WHEN a user submits `rm -rf /` via the terminal interface
|
|
31
|
+
THEN it SHALL return `403 Forbidden`
|
|
32
|
+
AND it SHALL NOT execute the command
|
|
33
|
+
AND it SHALL log the blocked attempt
|
|
34
|
+
"""
|
|
35
|
+
for cmd in dangerous_commands:
|
|
36
|
+
is_allowed = self._is_command_allowed(cmd)
|
|
37
|
+
assert not is_allowed, f"Dangerous command should be blocked: {cmd}"
|
|
38
|
+
|
|
39
|
+
@pytest.mark.asyncio
|
|
40
|
+
async def test_timeout_enforcement(self):
|
|
41
|
+
"""
|
|
42
|
+
Scenario: Timeout enforcement
|
|
43
|
+
WHEN a user submits a command that runs longer than 60 seconds
|
|
44
|
+
THEN it SHALL terminate the process after 60 seconds
|
|
45
|
+
AND it SHALL return partial output with a timeout message
|
|
46
|
+
"""
|
|
47
|
+
# Simulate long-running command
|
|
48
|
+
timeout_seconds = 60
|
|
49
|
+
|
|
50
|
+
async def long_command():
|
|
51
|
+
await asyncio.sleep(100) # Would run for 100s
|
|
52
|
+
return "complete"
|
|
53
|
+
|
|
54
|
+
# Wrap with timeout
|
|
55
|
+
try:
|
|
56
|
+
result = await asyncio.wait_for(long_command(), timeout=timeout_seconds)
|
|
57
|
+
assert False, "Should have timed out"
|
|
58
|
+
except asyncio.TimeoutError:
|
|
59
|
+
pass # Expected
|
|
60
|
+
|
|
61
|
+
# Verify timeout occurred
|
|
62
|
+
assert True, "Timeout was correctly enforced"
|
|
63
|
+
|
|
64
|
+
def test_command_history_navigation(self):
|
|
65
|
+
"""
|
|
66
|
+
Scenario: Command history
|
|
67
|
+
WHEN a user presses the Up arrow in the terminal
|
|
68
|
+
THEN it SHALL display the previously executed command
|
|
69
|
+
AND Down arrow SHALL navigate forward in history
|
|
70
|
+
"""
|
|
71
|
+
# Simulate command history
|
|
72
|
+
history: List[str] = [
|
|
73
|
+
"bun test",
|
|
74
|
+
"ls -la",
|
|
75
|
+
"git status"
|
|
76
|
+
]
|
|
77
|
+
|
|
78
|
+
current_index = len(history) # Start at end (empty line)
|
|
79
|
+
|
|
80
|
+
# Press Up - should go to last command
|
|
81
|
+
current_index -= 1
|
|
82
|
+
assert current_index == 2
|
|
83
|
+
assert history[current_index] == "git status"
|
|
84
|
+
|
|
85
|
+
# Press Up again
|
|
86
|
+
current_index -= 1
|
|
87
|
+
assert history[current_index] == "ls -la"
|
|
88
|
+
|
|
89
|
+
# Press Down - should go forward
|
|
90
|
+
current_index += 1
|
|
91
|
+
assert history[current_index] == "git status"
|
|
92
|
+
|
|
93
|
+
def test_command_output_streaming(self):
|
|
94
|
+
"""Test that command output streams in real-time."""
|
|
95
|
+
output_lines = [
|
|
96
|
+
"Running tests...",
|
|
97
|
+
"Test 1 passed",
|
|
98
|
+
"Test 2 passed",
|
|
99
|
+
"All tests passed!"
|
|
100
|
+
]
|
|
101
|
+
|
|
102
|
+
# Simulate streaming
|
|
103
|
+
received_lines = []
|
|
104
|
+
for line in output_lines:
|
|
105
|
+
received_lines.append(line)
|
|
106
|
+
|
|
107
|
+
assert len(received_lines) == len(output_lines)
|
|
108
|
+
assert received_lines == output_lines
|
|
109
|
+
|
|
110
|
+
def test_blocked_command_logging(self, dangerous_commands):
|
|
111
|
+
"""Test that blocked commands are logged."""
|
|
112
|
+
blocked_commands: List[str] = []
|
|
113
|
+
|
|
114
|
+
for cmd in dangerous_commands:
|
|
115
|
+
if not self._is_command_allowed(cmd):
|
|
116
|
+
blocked_commands.append(cmd)
|
|
117
|
+
|
|
118
|
+
assert len(blocked_commands) == len(dangerous_commands)
|
|
119
|
+
|
|
120
|
+
def _is_command_allowed(self, command: str) -> bool:
|
|
121
|
+
"""Check if command is allowed."""
|
|
122
|
+
dangerous_patterns = [
|
|
123
|
+
"rm -rf /",
|
|
124
|
+
"sudo",
|
|
125
|
+
"su -",
|
|
126
|
+
"passwd",
|
|
127
|
+
"mkfs",
|
|
128
|
+
"dd if=/dev/zero",
|
|
129
|
+
"> /dev/sda",
|
|
130
|
+
"chmod 777 /",
|
|
131
|
+
":(){ :|:& };:", # Fork bomb
|
|
132
|
+
]
|
|
133
|
+
|
|
134
|
+
cmd_lower = command.lower()
|
|
135
|
+
|
|
136
|
+
for pattern in dangerous_patterns:
|
|
137
|
+
if pattern in cmd_lower:
|
|
138
|
+
return False
|
|
139
|
+
|
|
140
|
+
# Allow common safe commands
|
|
141
|
+
safe_prefixes = [
|
|
142
|
+
"bun ",
|
|
143
|
+
"npm ",
|
|
144
|
+
"git ",
|
|
145
|
+
"ls",
|
|
146
|
+
"cat ",
|
|
147
|
+
"echo ",
|
|
148
|
+
"pwd",
|
|
149
|
+
"cd ",
|
|
150
|
+
]
|
|
151
|
+
|
|
152
|
+
return any(command.startswith(prefix) for prefix in safe_prefixes)
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tests for code viewer component.
|
|
3
|
+
Tests the syntax highlighting and file rendering features.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import pytest
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@pytest.mark.ide
|
|
11
|
+
class TestCodeViewer:
|
|
12
|
+
"""Tests for code viewer functionality."""
|
|
13
|
+
|
|
14
|
+
def test_typescript_file_syntax_highlighting(self, temp_dir):
|
|
15
|
+
"""
|
|
16
|
+
Scenario: TypeScript file rendering
|
|
17
|
+
WHEN a user requests to view a `.ts` file in the code viewer
|
|
18
|
+
THEN it SHALL return HTML with syntax-highlighted TypeScript
|
|
19
|
+
AND it SHALL include line numbers
|
|
20
|
+
"""
|
|
21
|
+
# Create TypeScript file
|
|
22
|
+
ts_file = temp_dir / "example.ts"
|
|
23
|
+
ts_file.write_text("""interface User {
|
|
24
|
+
id: number;
|
|
25
|
+
name: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const user: User = { id: 1, name: "Test" };""")
|
|
29
|
+
|
|
30
|
+
# Simulate code viewer response
|
|
31
|
+
content = ts_file.read_text()
|
|
32
|
+
html = self._generate_code_html(content, "typescript")
|
|
33
|
+
|
|
34
|
+
# Verify HTML structure
|
|
35
|
+
assert "<pre>" in html or "<code>" in html, "Should contain code block"
|
|
36
|
+
assert "line-number" in html or "data-line" in html, "Should include line numbers"
|
|
37
|
+
assert "interface" in html, "Should preserve TypeScript keywords"
|
|
38
|
+
|
|
39
|
+
def test_read_only_indicator_display(self, temp_dir):
|
|
40
|
+
"""
|
|
41
|
+
Scenario: File read-only indicator
|
|
42
|
+
WHEN viewing any file in the code viewer
|
|
43
|
+
THEN the page SHALL display a "👁️ READ-ONLY" indicator
|
|
44
|
+
AND the content SHALL NOT be editable
|
|
45
|
+
"""
|
|
46
|
+
html = self._generate_viewer_html("test.ts", "content", editable=False)
|
|
47
|
+
|
|
48
|
+
# Verify read-only indicator
|
|
49
|
+
assert "👁️" in html or "READ-ONLY" in html, "Should show read-only indicator"
|
|
50
|
+
assert 'contenteditable="false"' in html or "readonly" in html.lower(), "Content should not be editable"
|
|
51
|
+
|
|
52
|
+
def test_unsupported_file_type_handling(self, temp_dir):
|
|
53
|
+
"""
|
|
54
|
+
Scenario: Unsupported file type
|
|
55
|
+
WHEN a user requests a binary file (e.g., `.png`, `.exe`)
|
|
56
|
+
THEN it SHALL return `415 Unsupported Media Type`
|
|
57
|
+
AND it SHALL suggest downloading the file instead
|
|
58
|
+
"""
|
|
59
|
+
# Create binary file
|
|
60
|
+
binary_file = temp_dir / "test.png"
|
|
61
|
+
binary_file.write_bytes(b"\x89PNG\r\n\x1a\n") # PNG magic bytes
|
|
62
|
+
|
|
63
|
+
# Check if file is binary
|
|
64
|
+
is_binary = self._is_binary_file(binary_file)
|
|
65
|
+
assert is_binary, "Should detect binary file"
|
|
66
|
+
|
|
67
|
+
# Binary files should not be displayed as text
|
|
68
|
+
should_display = not is_binary
|
|
69
|
+
assert not should_display, "Binary files should not be displayed in code viewer"
|
|
70
|
+
|
|
71
|
+
def _generate_code_html(self, content: str, language: str) -> str:
|
|
72
|
+
"""Helper to generate syntax-highlighted HTML."""
|
|
73
|
+
lines = content.split("\n")
|
|
74
|
+
|
|
75
|
+
html_lines = ['<div class="code-viewer read-only">']
|
|
76
|
+
html_lines.append('<div class="read-only-indicator">👁️ READ-ONLY</div>')
|
|
77
|
+
html_lines.append(f'<pre class="language-{language}"><code>')
|
|
78
|
+
|
|
79
|
+
for i, line in enumerate(lines, 1):
|
|
80
|
+
html_lines.append(f'<span class="line" data-line="{i}"><span class="line-number">{i}</span>{self._escape_html(line)}</span>')
|
|
81
|
+
|
|
82
|
+
html_lines.append('</code></pre>')
|
|
83
|
+
html_lines.append('</div>')
|
|
84
|
+
|
|
85
|
+
return "\n".join(html_lines)
|
|
86
|
+
|
|
87
|
+
def _generate_viewer_html(self, filename: str, content: str, editable: bool = False) -> str:
|
|
88
|
+
"""Helper to generate viewer HTML."""
|
|
89
|
+
return f"""<!DOCTYPE html>
|
|
90
|
+
<html>
|
|
91
|
+
<head><title>{filename}</title></head>
|
|
92
|
+
<body>
|
|
93
|
+
<div class="code-viewer {'' if editable else 'read-only'}">
|
|
94
|
+
<div class="header">
|
|
95
|
+
<span>👁️ READ-ONLY</span>
|
|
96
|
+
<span>{filename}</span>
|
|
97
|
+
</div>
|
|
98
|
+
<pre><code contenteditable="{'true' if editable else 'false'}">{self._escape_html(content)}</code></pre>
|
|
99
|
+
</div>
|
|
100
|
+
</body>
|
|
101
|
+
</html>"""
|
|
102
|
+
|
|
103
|
+
def _escape_html(self, text: str) -> str:
|
|
104
|
+
"""Escape HTML special characters."""
|
|
105
|
+
return (text
|
|
106
|
+
.replace("&", "&")
|
|
107
|
+
.replace("<", "<")
|
|
108
|
+
.replace(">", ">")
|
|
109
|
+
.replace('"', """))
|
|
110
|
+
|
|
111
|
+
def _is_binary_file(self, path: Path) -> bool:
|
|
112
|
+
"""Check if file is binary."""
|
|
113
|
+
try:
|
|
114
|
+
with open(path, "rb") as f:
|
|
115
|
+
chunk = f.read(1024)
|
|
116
|
+
return b"\x00" in chunk or not self._is_text_content(chunk)
|
|
117
|
+
except Exception:
|
|
118
|
+
return True
|
|
119
|
+
|
|
120
|
+
def _is_text_content(self, content: bytes) -> bool:
|
|
121
|
+
"""Check if content is likely text."""
|
|
122
|
+
# Try to decode as UTF-8
|
|
123
|
+
try:
|
|
124
|
+
content.decode("utf-8")
|
|
125
|
+
return True
|
|
126
|
+
except UnicodeDecodeError:
|
|
127
|
+
return False
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Test the compose port anchor replacement logic.
|
|
3
|
+
|
|
4
|
+
This directly tests the _replace_ports_with_anchors function behavior
|
|
5
|
+
to ensure backend services get *backend-port and frontends get *frontend-port.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import pytest
|
|
9
|
+
import re
|
|
10
|
+
|
|
11
|
+
pytestmark = [pytest.mark.fast, pytest.mark.generator]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class TestReplacePortsWithAnchors:
|
|
15
|
+
"""Test the port anchor replacement logic that caused the Traefik bug."""
|
|
16
|
+
|
|
17
|
+
@pytest.fixture
|
|
18
|
+
def backend_resource_entry(self) -> str:
|
|
19
|
+
"""Backend service entry - should get *backend-port."""
|
|
20
|
+
return ''' identity-backend:
|
|
21
|
+
image: identity_identity-backend:dev
|
|
22
|
+
labels:
|
|
23
|
+
- "traefik.enable=true"
|
|
24
|
+
- "traefik.http.services.identity-backend.loadbalancer.server.port=4004"
|
|
25
|
+
- "traefik.http.services.identity-backend.loadbalancer.healthcheck.path=/api/v1/health"
|
|
26
|
+
healthcheck:
|
|
27
|
+
test: ["CMD", "curl", "-f", "http://localhost:4004/api/v1/health"]
|
|
28
|
+
deploy:
|
|
29
|
+
resources:
|
|
30
|
+
limits:
|
|
31
|
+
memory: 512M
|
|
32
|
+
'''
|
|
33
|
+
|
|
34
|
+
@pytest.fixture
|
|
35
|
+
def frontend_resource_entry(self) -> str:
|
|
36
|
+
"""Frontend service entry - should get *frontend-port."""
|
|
37
|
+
return ''' identity-frontend:
|
|
38
|
+
image: identity_identity-frontend:dev
|
|
39
|
+
<<: *frontend-memory-limit
|
|
40
|
+
labels:
|
|
41
|
+
- "traefik.enable=true"
|
|
42
|
+
- "traefik.http.services.identity-frontend.loadbalancer.server.port=3000"
|
|
43
|
+
healthcheck:
|
|
44
|
+
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://127.0.0.1:3000"]
|
|
45
|
+
'''
|
|
46
|
+
|
|
47
|
+
@pytest.fixture
|
|
48
|
+
def sdk_resource_entry(self) -> str:
|
|
49
|
+
"""SDK service entry - should get *sdk-port."""
|
|
50
|
+
return ''' identity-sdk:
|
|
51
|
+
image: identity_identity-sdk:dev
|
|
52
|
+
labels:
|
|
53
|
+
- "traefik.enable=true"
|
|
54
|
+
- "traefik.http.services.identity-sdk.loadbalancer.server.port=5175"
|
|
55
|
+
'''
|
|
56
|
+
|
|
57
|
+
def simulate_replace_ports_with_anchors(
|
|
58
|
+
self,
|
|
59
|
+
entry: str,
|
|
60
|
+
has_sdk: bool = False,
|
|
61
|
+
has_frontend: bool = False,
|
|
62
|
+
has_backend: bool = False
|
|
63
|
+
) -> str:
|
|
64
|
+
"""
|
|
65
|
+
Simulate the fixed _replace_ports_with_anchors function.
|
|
66
|
+
|
|
67
|
+
This mirrors the logic in compose.star after the fix:
|
|
68
|
+
- Detects entry type by checking entry content
|
|
69
|
+
- Uses entry type, not just global flags
|
|
70
|
+
"""
|
|
71
|
+
# Detect entry type from content (same logic as _is_backend_entry, _is_sdk_entry)
|
|
72
|
+
is_sdk = "-sdk:" in entry or entry.strip().startswith("identity-sdk:")
|
|
73
|
+
is_frontend = "<<: *frontend-memory-limit" in entry
|
|
74
|
+
is_backend = "healthcheck:" in entry and "curl" in entry and not is_frontend and not is_sdk
|
|
75
|
+
|
|
76
|
+
result = []
|
|
77
|
+
for line in entry.split('\n'):
|
|
78
|
+
new_line = line
|
|
79
|
+
|
|
80
|
+
# Replace Traefik loadbalancer port
|
|
81
|
+
if "loadbalancer.server.port=" in line and "traefik.http.services." in line:
|
|
82
|
+
# Extract port value
|
|
83
|
+
port_match = re.search(r'loadbalancer\.server\.port=(\d+)', line)
|
|
84
|
+
if port_match:
|
|
85
|
+
port_val = int(port_match.group(1))
|
|
86
|
+
|
|
87
|
+
if port_val == 3000:
|
|
88
|
+
# Port 3000 always uses backend-port
|
|
89
|
+
new_line = line.replace(
|
|
90
|
+
f"loadbalancer.server.port={port_val}",
|
|
91
|
+
"loadbalancer.server.port=*backend-port"
|
|
92
|
+
)
|
|
93
|
+
else:
|
|
94
|
+
# Non-3000 ports: use correct anchor based on entry type
|
|
95
|
+
if is_sdk and has_sdk:
|
|
96
|
+
new_line = line.replace(
|
|
97
|
+
f"loadbalancer.server.port={port_val}",
|
|
98
|
+
"loadbalancer.server.port=*sdk-port"
|
|
99
|
+
)
|
|
100
|
+
elif is_backend and has_backend:
|
|
101
|
+
# CRITICAL FIX: Use backend-port for backends
|
|
102
|
+
new_line = line.replace(
|
|
103
|
+
f"loadbalancer.server.port={port_val}",
|
|
104
|
+
"loadbalancer.server.port=*backend-port"
|
|
105
|
+
)
|
|
106
|
+
elif is_frontend and has_frontend:
|
|
107
|
+
# Frontend uses frontend-port
|
|
108
|
+
new_line = line.replace(
|
|
109
|
+
f"loadbalancer.server.port={port_val}",
|
|
110
|
+
"loadbalancer.server.port=*frontend-port"
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
result.append(new_line)
|
|
114
|
+
|
|
115
|
+
return '\n'.join(result)
|
|
116
|
+
|
|
117
|
+
def test_backend_gets_backend_port_anchor(self, backend_resource_entry: str):
|
|
118
|
+
"""Backend with port 4004 must get *backend-port anchor."""
|
|
119
|
+
result = self.simulate_replace_ports_with_anchors(
|
|
120
|
+
backend_resource_entry,
|
|
121
|
+
has_sdk=False,
|
|
122
|
+
has_frontend=True, # Simulate identity stack which has frontend
|
|
123
|
+
has_backend=True
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
# Should have backend-port, NOT frontend-port
|
|
127
|
+
assert "*backend-port" in result, "Backend must use *backend-port anchor"
|
|
128
|
+
assert "*frontend-port" not in result, "Backend must NOT use *frontend-port anchor"
|
|
129
|
+
assert "*sdk-port" not in result, "Backend must NOT use *sdk-port anchor"
|
|
130
|
+
|
|
131
|
+
def test_frontend_gets_frontend_port_anchor(self, frontend_resource_entry: str):
|
|
132
|
+
"""Frontend with port 3000 (or other) must get *frontend-port anchor."""
|
|
133
|
+
result = self.simulate_replace_ports_with_anchors(
|
|
134
|
+
frontend_resource_entry,
|
|
135
|
+
has_sdk=False,
|
|
136
|
+
has_frontend=True,
|
|
137
|
+
has_backend=False
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
# Port 3000 gets converted to backend-port (by design for 3000)
|
|
141
|
+
# But if it were 4000, it would get frontend-port
|
|
142
|
+
assert "*frontend-port" in result or "*backend-port" in result
|
|
143
|
+
|
|
144
|
+
def test_frontend_non_3000_port_gets_frontend_anchor(self):
|
|
145
|
+
"""Frontend with port 4000 must get *frontend-port (not *backend-port)."""
|
|
146
|
+
frontend_with_4000 = ''' custom-frontend:
|
|
147
|
+
image: test:test
|
|
148
|
+
<<: *frontend-memory-limit
|
|
149
|
+
labels:
|
|
150
|
+
- "traefik.http.services.custom-frontend.loadbalancer.server.port=4000"
|
|
151
|
+
'''
|
|
152
|
+
result = self.simulate_replace_ports_with_anchors(
|
|
153
|
+
frontend_with_4000,
|
|
154
|
+
has_sdk=False,
|
|
155
|
+
has_frontend=True,
|
|
156
|
+
has_backend=True # Even with has_backend=True
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
# Frontend should get frontend-port even with port 4000
|
|
160
|
+
assert "*frontend-port" in result, "Frontend must use *frontend-port"
|
|
161
|
+
assert "*backend-port" not in result, "Frontend must NOT use *backend-port"
|
|
162
|
+
|
|
163
|
+
def test_sdk_gets_sdk_port_anchor(self, sdk_resource_entry: str):
|
|
164
|
+
"""SDK with port 5175 must get *sdk-port anchor."""
|
|
165
|
+
result = self.simulate_replace_ports_with_anchors(
|
|
166
|
+
sdk_resource_entry,
|
|
167
|
+
has_sdk=True,
|
|
168
|
+
has_frontend=True,
|
|
169
|
+
has_backend=True
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
assert "*sdk-port" in result, "SDK must use *sdk-port anchor"
|
|
173
|
+
assert "*backend-port" not in result, "SDK must NOT use *backend-port"
|
|
174
|
+
assert "*frontend-port" not in result, "SDK must NOT use *frontend-port"
|
|
175
|
+
|
|
176
|
+
def test_port_3000_always_backend_port(self):
|
|
177
|
+
"""Port 3000 always maps to *backend-port regardless of service type."""
|
|
178
|
+
resource_with_3000 = ''' some-service:
|
|
179
|
+
labels:
|
|
180
|
+
- "traefik.http.services.some-service.loadbalancer.server.port=3000"
|
|
181
|
+
'''
|
|
182
|
+
result = self.simulate_replace_ports_with_anchors(
|
|
183
|
+
resource_with_3000,
|
|
184
|
+
has_sdk=False,
|
|
185
|
+
has_frontend=True,
|
|
186
|
+
has_backend=True
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
# Port 3000 always becomes *backend-port
|
|
190
|
+
assert "*backend-port" in result
|
|
191
|
+
assert "*frontend-port" not in result
|
|
192
|
+
|
|
193
|
+
def test_original_bug_scenario(self):
|
|
194
|
+
"""
|
|
195
|
+
Reproduce the original bug: identity-backend got *frontend-port.
|
|
196
|
+
|
|
197
|
+
The bug occurred because:
|
|
198
|
+
1. has_frontend=True (because identity stack has a frontend)
|
|
199
|
+
2. Port 4004 != 3000
|
|
200
|
+
3. Old code checked has_frontend first, ignoring entry type
|
|
201
|
+
4. Result: backend got *frontend-port instead of *backend-port
|
|
202
|
+
"""
|
|
203
|
+
identity_backend = ''' identity-management-backend:
|
|
204
|
+
image: identity_identity-management-backend:dev
|
|
205
|
+
labels:
|
|
206
|
+
- "traefik.enable=true"
|
|
207
|
+
- "traefik.http.services.identity-management-backend.loadbalancer.server.port=4004"
|
|
208
|
+
- "traefik.http.services.identity-management-backend.loadbalancer.healthcheck.path=/api/v1/health"
|
|
209
|
+
healthcheck:
|
|
210
|
+
test: ["CMD", "curl", "-f", "http://localhost:4004/api/v1/health"]
|
|
211
|
+
'''
|
|
212
|
+
|
|
213
|
+
# Simulate old buggy logic (BEFORE fix)
|
|
214
|
+
def old_buggy_replace(entry: str, has_frontend: bool = False) -> str:
|
|
215
|
+
"""The buggy version - only checks has_frontend flag."""
|
|
216
|
+
result = []
|
|
217
|
+
for line in entry.split('\n'):
|
|
218
|
+
if "loadbalancer.server.port=" in line:
|
|
219
|
+
port_match = re.search(r'loadbalancer\.server\.port=(\d+)', line)
|
|
220
|
+
if port_match:
|
|
221
|
+
port_val = int(port_match.group(1))
|
|
222
|
+
if port_val != 3000:
|
|
223
|
+
# BUG: Only checks has_frontend, ignores entry type!
|
|
224
|
+
if has_frontend:
|
|
225
|
+
line = line.replace(
|
|
226
|
+
f"loadbalancer.server.port={port_val}",
|
|
227
|
+
"loadbalancer.server.port=*frontend-port" # WRONG!
|
|
228
|
+
)
|
|
229
|
+
result.append(line)
|
|
230
|
+
return '\n'.join(result)
|
|
231
|
+
|
|
232
|
+
# Buggy result (what was happening before)
|
|
233
|
+
buggy_result = old_buggy_replace(identity_backend, has_frontend=True)
|
|
234
|
+
assert "*frontend-port" in buggy_result, "Bug: backend got frontend-port"
|
|
235
|
+
|
|
236
|
+
# Fixed result (after our fix)
|
|
237
|
+
fixed_result = self.simulate_replace_ports_with_anchors(
|
|
238
|
+
identity_backend,
|
|
239
|
+
has_sdk=False,
|
|
240
|
+
has_frontend=True,
|
|
241
|
+
has_backend=True
|
|
242
|
+
)
|
|
243
|
+
assert "*backend-port" in fixed_result, "Fix: backend now gets backend-port"
|
|
244
|
+
assert "*frontend-port" not in fixed_result
|
|
245
|
+
|
|
246
|
+
def test_mixed_stack_all_services_correct_anchors(self):
|
|
247
|
+
"""Test full identity stack with backend + frontend + SDK."""
|
|
248
|
+
entries = [
|
|
249
|
+
(''' identity-management-backend:
|
|
250
|
+
image: identity_identity-management-backend:dev
|
|
251
|
+
labels:
|
|
252
|
+
- "traefik.http.services.identity-management-backend.loadbalancer.server.port=4004"
|
|
253
|
+
healthcheck:
|
|
254
|
+
test: ["CMD", "curl", "-f", "http://localhost:4004/api/v1/health"]
|
|
255
|
+
''', "*backend-port"),
|
|
256
|
+
(''' identity-management-frontend:
|
|
257
|
+
<<: *frontend-memory-limit
|
|
258
|
+
labels:
|
|
259
|
+
- "traefik.http.services.identity-management-frontend.loadbalancer.server.port=4000"
|
|
260
|
+
''', "*frontend-port"),
|
|
261
|
+
(''' identity-sdk:
|
|
262
|
+
labels:
|
|
263
|
+
- "traefik.http.services.identity-sdk.loadbalancer.server.port=5175"
|
|
264
|
+
''', "*sdk-port"),
|
|
265
|
+
]
|
|
266
|
+
|
|
267
|
+
for entry, expected_anchor in entries:
|
|
268
|
+
result = self.simulate_replace_ports_with_anchors(
|
|
269
|
+
entry,
|
|
270
|
+
has_sdk=True,
|
|
271
|
+
has_frontend=True,
|
|
272
|
+
has_backend=True
|
|
273
|
+
)
|
|
274
|
+
assert expected_anchor in result, f"Entry should use {expected_anchor}"
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
class TestPortAnchorExtraction:
|
|
278
|
+
"""Test that port anchors are correctly extracted from service entries."""
|
|
279
|
+
|
|
280
|
+
def test_extract_backend_port_from_entry(self):
|
|
281
|
+
"""Extract backend port (4004) from service entry."""
|
|
282
|
+
entry = ''' backend:
|
|
283
|
+
labels:
|
|
284
|
+
- "traefik.http.services.backend.loadbalancer.server.port=4004"
|
|
285
|
+
'''
|
|
286
|
+
match = re.search(r'loadbalancer\.server\.port=(\d+)', entry)
|
|
287
|
+
assert match
|
|
288
|
+
assert match.group(1) == "4004"
|
|
289
|
+
|
|
290
|
+
def test_extract_frontend_port_from_entry(self):
|
|
291
|
+
"""Extract frontend port (3000) from service entry."""
|
|
292
|
+
entry = ''' frontend:
|
|
293
|
+
labels:
|
|
294
|
+
- "traefik.http.services.frontend.loadbalancer.server.port=3000"
|
|
295
|
+
'''
|
|
296
|
+
match = re.search(r'loadbalancer\.server\.port=(\d+)', entry)
|
|
297
|
+
assert match
|
|
298
|
+
assert match.group(1) == "3000"
|
|
299
|
+
|
|
300
|
+
def test_no_port_returns_none(self):
|
|
301
|
+
"""Entry without loadbalancer port should return None."""
|
|
302
|
+
entry = ''' some-service:
|
|
303
|
+
image: test:test
|
|
304
|
+
'''
|
|
305
|
+
match = re.search(r'loadbalancer\.server\.port=(\d+)', entry)
|
|
306
|
+
assert match is None
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
if __name__ == "__main__":
|
|
310
|
+
pytest.main([__file__, "-v"])
|