@tdk-landscape/tdk-cli-core 1.3.14 → 1.3.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/Tiltfile +144 -0
- package/discovery/AGENTS.md +362 -0
- package/discovery/TILT_DISCOVERY.star +424 -0
- package/discovery/Tiltfile +58 -0
- package/discovery/config.star +258 -0
- package/discovery/constants.star +127 -0
- package/discovery/deterministic.star +211 -0
- package/discovery/discovery_daemon.star +191 -0
- package/discovery/discovery_orchestrator.star +460 -0
- package/discovery/filtering.star +5 -0
- package/discovery/identity-management-backend/service.yaml +26 -0
- package/discovery/identity-management-frontend/service.yaml +29 -0
- package/discovery/identity-sdk/service.yaml +9 -0
- package/discovery/incremental_discovery.star +213 -0
- package/discovery/indexing.star +7 -0
- package/discovery/json_manifest_scanner.star +51 -0
- package/discovery/libraries.star +46 -0
- package/discovery/loading.star +23 -0
- package/discovery/manifest/constants.star +104 -0
- package/discovery/manifest/loading.star +386 -0
- package/discovery/manifest/normalize.star +281 -0
- package/discovery/manifest/validation.star +108 -0
- package/discovery/normalization.star +11 -0
- package/discovery/registry.star +544 -0
- package/discovery/resource_snapshot.py +382 -0
- package/discovery/resource_snapshot.star +143 -0
- package/discovery/schemas.star +1 -0
- package/discovery/services/platform/cdc/debezium/nats-connector/service.yaml +9 -0
- package/discovery/services/platform/cdc/nats-http-bridge/service.yaml +9 -0
- package/discovery/services/platform/orchestrator/orchestrator-glue-backend/service.yaml +26 -0
- package/discovery/services/platform/synthetic-monitor/service.yaml +9 -0
- package/discovery/test_resource_snapshot.py +251 -0
- package/discovery/validation.star +78 -0
- package/discovery/versioning.star +1 -0
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/commands/config.js +11 -0
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +1 -2
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/project.d.ts.map +1 -1
- package/dist/commands/project.js +36 -41
- package/dist/commands/project.js.map +1 -1
- package/dist/commands/resource.d.ts.map +1 -1
- package/dist/commands/resource.js +51 -55
- package/dist/commands/resource.js.map +1 -1
- package/dist/commands/stack.d.ts.map +1 -1
- package/dist/commands/stack.js +22 -24
- package/dist/commands/stack.js.map +1 -1
- package/dist/commands/ui.d.ts.map +1 -1
- package/dist/commands/ui.js +1 -2
- package/dist/commands/ui.js.map +1 -1
- package/dist/commands/up.d.ts.map +1 -1
- package/dist/commands/up.js +1 -2
- package/dist/commands/up.js.map +1 -1
- package/dist/commands/upgrade.d.ts +8 -0
- package/dist/commands/upgrade.d.ts.map +1 -1
- package/dist/commands/upgrade.js +62 -21
- package/dist/commands/upgrade.js.map +1 -1
- package/dist/generator/extension-fetch.d.ts +19 -0
- package/dist/generator/extension-fetch.d.ts.map +1 -1
- package/dist/generator/extension-fetch.js +71 -5
- package/dist/generator/extension-fetch.js.map +1 -1
- package/dist/generator/template-engine.d.ts +1 -0
- package/dist/generator/template-engine.d.ts.map +1 -1
- package/dist/generator/template-engine.js +50 -6
- package/dist/generator/template-engine.js.map +1 -1
- package/dist/utils/command-helpers.d.ts.map +1 -1
- package/dist/utils/command-helpers.js +7 -9
- package/dist/utils/command-helpers.js.map +1 -1
- package/dist/utils/constants.d.ts +1 -0
- package/dist/utils/constants.d.ts.map +1 -1
- package/dist/utils/constants.js +1 -0
- package/dist/utils/constants.js.map +1 -1
- package/dist/utils/errors.d.ts +2 -0
- package/dist/utils/errors.d.ts.map +1 -1
- package/dist/utils/errors.js +6 -1
- package/dist/utils/errors.js.map +1 -1
- package/dist/utils/paths.js +1 -1
- package/dist/utils/paths.js.map +1 -1
- package/dist/utils/project-features.d.ts.map +1 -1
- package/dist/utils/project-features.js +1 -1
- package/dist/utils/project-features.js.map +1 -1
- package/dist/utils/resource-features.d.ts.map +1 -1
- package/dist/utils/resource-features.js +2 -2
- package/dist/utils/resource-features.js.map +1 -1
- package/dist/utils/tar.js +4 -4
- package/dist/utils/tar.js.map +1 -1
- package/dist/utils/validation.d.ts +7 -0
- package/dist/utils/validation.d.ts.map +1 -1
- package/dist/utils/validation.js +9 -0
- package/dist/utils/validation.js.map +1 -1
- package/engine/AGENTS.md +705 -0
- package/engine/README.md +29 -0
- package/engine/REFACTORING_MANIFEST_SYSTEM.md +974 -0
- package/engine/Tiltfile +80 -0
- package/engine/assets/docker/golden-layers.Dockerfile +221 -0
- package/engine/assets/scripts/cleanup-completed-migrator-images.sh +37 -0
- package/engine/assets/scripts/cleanup-tilt-images.sh +120 -0
- package/engine/assets/scripts/mac-m1-resource-monitor.sh +121 -0
- package/engine/config.star +3 -0
- package/engine/docs/AUTODISCOVERY_TEST.md +182 -0
- package/engine/docs/C4-ARCHITECTURE-operations.md +396 -0
- package/engine/docs/C4-ARCHITECTURE-overview.md +284 -0
- package/engine/docs/C4-ARCHITECTURE.md +17 -0
- package/engine/docs/GOLDEN_IMAGE_INTEGRATION.md +349 -0
- package/engine/docs/MANIFEST_DRIVEN_ARCHITECTURE.md +357 -0
- package/engine/docs/README.md +284 -0
- package/engine/entrypoint.star +97 -0
- package/engine/lifecycle/orchestrator.star +260 -0
- package/engine/paths.star +717 -0
- package/engine/schemas/manifest-schema.json +301 -0
- package/engine/schemas/service-schema.json +301 -0
- package/engine/scripts/README.md +89 -0
- package/engine/scripts/build-and-publish-lib.sh +205 -0
- package/engine/scripts/check-lib-publish.sh +85 -0
- package/engine/scripts/debug-container.sh +258 -0
- package/engine/scripts/generate-manifest.sh +197 -0
- package/engine/spec.master +702 -0
- package/engine/spec.master.example +341 -0
- package/engine/topologies/README.md +34 -0
- package/engine/topologies/platform/docker/AGENTS.md +59 -0
- package/engine/topologies/platform/docker/auth/auth.star +167 -0
- package/engine/topologies/platform/docker/build/golden_image.star +37 -0
- package/engine/topologies/platform/docker/build/golden_image_build.star +82 -0
- package/engine/topologies/platform/docker/build/golden_image_constants.star +93 -0
- package/engine/topologies/platform/docker/build/golden_image_dockerfile.star +291 -0
- package/engine/topologies/platform/docker/compose/compose.star +636 -0
- package/engine/topologies/platform/docker/compose/compose_helpers.star +52 -0
- package/engine/topologies/platform/docker/compose/traefik_standalone.star +53 -0
- package/engine/topologies/platform/docker/config/bunignore.star +142 -0
- package/engine/topologies/platform/docker/config/dockerignore.star +150 -0
- package/engine/topologies/platform/docker/constants.star +170 -0
- package/engine/topologies/platform/docker/dockerfile/dockerfile.star +54 -0
- package/engine/topologies/platform/docker/generators/golden_docker_generator_v2.star +73 -0
- package/engine/topologies/platform/docker/index.star +27 -0
- package/engine/topologies/platform/docker/layers/bun_helpers.star +23 -0
- package/engine/topologies/platform/docker/layers/infisical/index.star +40 -0
- package/engine/topologies/platform/docker/layers/infisical/infisical_docker.star +262 -0
- package/engine/topologies/platform/docker/layers/l1_base_layers.star +87 -0
- package/engine/topologies/platform/docker/layers/l2_dependency_layers.star +125 -0
- package/engine/topologies/platform/docker/layers/l3_builder_layers.star +209 -0
- package/engine/topologies/platform/docker/layers/l4_runtime_layers.star +214 -0
- package/engine/topologies/platform/docker/layers/prisma/constants.star +135 -0
- package/engine/topologies/platform/docker/layers/prisma/db_readiness.star +96 -0
- package/engine/topologies/platform/docker/layers/prisma/filesystem_setup.star +109 -0
- package/engine/topologies/platform/docker/layers/prisma/migrate_script.star +98 -0
- package/engine/topologies/platform/docker/layers/prisma/prisma_build.star +126 -0
- package/engine/topologies/platform/docker/layers/prisma/prisma_runtime.star +161 -0
- package/engine/topologies/platform/docker/layers/prisma/secret_resolution.star +119 -0
- package/engine/topologies/platform/docker/networking/api_path_constants.star +230 -0
- package/engine/topologies/platform/docker/networking/sablier_container_cycle.star +27 -0
- package/engine/topologies/platform/docker/networking/traefik.star +232 -0
- package/engine/topologies/platform/docker/networking/traefik_constants.star +146 -0
- package/engine/topologies/platform/docker/networking/traefik_helpers.star +160 -0
- package/engine/topologies/platform/docker/secrets/infisical.star +135 -0
- package/engine/topologies/platform/net/proxy.star +4 -0
- package/engine/topologies/platform/registries/local.star +4 -0
- package/engine/topologies/platform/security/secrets.star +239 -0
- package/engine/topologies/platform/state/relational.star +4 -0
- package/engine/topologies/tilt/common/AGENTS.md +59 -0
- package/engine/topologies/tilt/common/utils.star +364 -0
- package/engine/topologies/tilt/common/utils_debug.star +69 -0
- package/engine/topologies/tilt/common/utils_deterministic.star +168 -0
- package/engine/topologies/tilt/common/utils_docker_networks.star +46 -0
- package/engine/topologies/tilt/common/utils_env.star +43 -0
- package/engine/topologies/tilt/common/utils_output.star +97 -0
- package/engine/topologies/tilt/config/AGENTS.md +68 -0
- package/engine/topologies/tilt/config/defaults.star +3 -0
- package/engine/topologies/tilt/config/feature_flags.star +10 -0
- package/engine/topologies/tilt/config/global.star +55 -0
- package/engine/topologies/tilt/config/profiles.star +382 -0
- package/engine/topologies/tilt/generators/agents_md.star +62 -0
- package/engine/topologies/tilt/generators/api_gateway.star +145 -0
- package/engine/topologies/tilt/generators/index.star +124 -0
- package/engine/topologies/tilt/generators/infisical/README.md +424 -0
- package/engine/topologies/tilt/generators/infisical/examples.star +312 -0
- package/engine/topologies/tilt/generators/infisical/index.star +57 -0
- package/engine/topologies/tilt/generators/infisical/machine_identity.star +420 -0
- package/engine/topologies/tilt/generators/infisical/organization.star +534 -0
- package/engine/topologies/tilt/generators/infisical/path_manager.star +476 -0
- package/engine/topologies/tilt/generators/infisical/secrets_generator.star +420 -0
- package/engine/topologies/tilt/generators/nginx_conf.star +143 -0
- package/engine/topologies/tilt/generators/npmrc.star +111 -0
- package/engine/topologies/tilt/generators/tsconfig.star +23 -0
- package/engine/topologies/tilt/generators/typescript/backend_library_tsconfig.star +71 -0
- package/engine/topologies/tilt/generators/typescript/backend_tsconfig.star +124 -0
- package/engine/topologies/tilt/generators/typescript/common.star +7 -0
- package/engine/topologies/tilt/generators/typescript/frontend_library_tsconfig.star +76 -0
- package/engine/topologies/tilt/generators/typescript/frontend_tsconfig.star +110 -0
- package/engine/topologies/tilt/generators/typescript/include.star +23 -0
- package/engine/topologies/tilt/generators/typescript/playwright_config.star +40 -0
- package/engine/topologies/tilt/generators/typescript/prisma_config.star +49 -0
- package/engine/topologies/tilt/generators/validators.star +853 -0
- package/engine/topologies/tilt/generators/vite/backend.star +78 -0
- package/engine/topologies/tilt/generators/vite/frontend.star +98 -0
- package/engine/topologies/tilt/generators/vite/generators.star +43 -0
- package/engine/topologies/tilt/generators/vite/helpers.star +244 -0
- package/engine/topologies/tilt/generators/vite/library.star +65 -0
- package/engine/topologies/tilt/generators/vite/sdk.star +73 -0
- package/engine/topologies/tilt/generators/vite/templates.star +737 -0
- package/engine/topologies/tilt/generators/vite_config.star +22 -0
- package/engine/topologies/tilt/manifest/AGENTS.md +61 -0
- package/engine/topologies/tilt/manifest/README.md +693 -0
- package/engine/topologies/tilt/manifest/__init__.star +218 -0
- package/engine/topologies/tilt/manifest/constants.star +252 -0
- package/engine/topologies/tilt/manifest/errors.star +266 -0
- package/engine/topologies/tilt/manifest/integration.star +339 -0
- package/engine/topologies/tilt/manifest/json_to_yaml.py +54 -0
- package/engine/topologies/tilt/manifest/loader.star +214 -0
- package/engine/topologies/tilt/manifest/mapper.star +227 -0
- package/engine/topologies/tilt/manifest/parser.star +400 -0
- package/engine/topologies/tilt/manifest/schema.star +462 -0
- package/engine/topologies/tilt/manifest/tests/test_integration.star +276 -0
- package/engine/topologies/tilt/manifest/tests/test_loader_parser.star +397 -0
- package/engine/topologies/tilt/manifest/tests/test_manifest.star +316 -0
- package/engine/topologies/tilt/manifest/tests/test_validator.star +470 -0
- package/engine/topologies/tilt/manifest/validator.star +717 -0
- package/engine/topologies/tilt/manifest/yaml_to_json.py +18 -0
- package/engine/topologies/tilt/resources/AGENTS.md +71 -0
- package/engine/topologies/tilt/resources/conditions.star +3 -0
- package/engine/topologies/tilt/resources/databases.star +244 -0
- package/engine/topologies/tilt/resources/declaration.star +3 -0
- package/engine/topologies/tilt/resources/deps.star +3 -0
- package/engine/topologies/tilt/resources/infra-loader.star +437 -0
- package/engine/topologies/tilt/resources/libs.star +30 -0
- package/engine/topologies/tilt/resources/libs_discovery.star +102 -0
- package/engine/topologies/tilt/resources/libs_paths.star +79 -0
- package/engine/topologies/tilt/resources/libs_resource_definition.star +151 -0
- package/engine/topologies/tilt/resources/libs_setup.star +164 -0
- package/engine/topologies/tilt/resources/libs_tsconfig.star +37 -0
- package/engine/topologies/tilt/resources/orchestrator/apply.star +77 -0
- package/engine/topologies/tilt/resources/orchestrator/apply_compose_resource_registration.star +431 -0
- package/engine/topologies/tilt/resources/orchestrator/apply_manifest_orchestration.star +81 -0
- package/engine/topologies/tilt/resources/orchestrator/apply_migrator_orchestration.star +148 -0
- package/engine/topologies/tilt/resources/orchestrator/apply_resource_validation.star +36 -0
- package/engine/topologies/tilt/resources/orchestrator/apply_runtime_flags.star +44 -0
- package/engine/topologies/tilt/resources/orchestrator/builders/typescript.star +164 -0
- package/engine/topologies/tilt/resources/orchestrator/generators/dependencies/AGENTS.md +75 -0
- package/engine/topologies/tilt/resources/orchestrator/generators/dependencies/index.star +21 -0
- package/engine/topologies/tilt/resources/orchestrator/generators/dependencies/resolver.star +134 -0
- package/engine/topologies/tilt/resources/orchestrator/generators/env.star +258 -0
- package/engine/topologies/tilt/resources/orchestrator/generators/frontend.star +169 -0
- package/engine/topologies/tilt/resources/orchestrator/generators/manifest_resource.star +524 -0
- package/engine/topologies/tilt/resources/orchestrator/helpers.star +33 -0
- package/engine/topologies/tilt/resources/orchestrator.star +54 -0
- package/engine/topologies/tilt/resources/ordering.star +3 -0
- package/engine/topologies/tilt/resources/triggers.star +3 -0
- package/engine/topologies/tilt/resources/watchers.star +299 -0
- package/engine/where-things-live.star +717 -0
- package/ext/Tiltfile +66 -0
- package/ext/ide-components/README.md +219 -0
- package/ext/ide-components/code_executor/server.py +268 -0
- package/ext/ide-components/code_viewer/server.py +206 -0
- package/ext/ide-components/config_inspector/server.py +1050 -0
- package/ext/ide-components/file_browser/server.py +285 -0
- package/ext/ide-components/shared/config_service.py +469 -0
- package/ext/ide-components/shared/file_utils.py +298 -0
- package/ext/ide-components/shared/http_utils.py +232 -0
- package/ext/ide-components/shared/static/styles.css +1334 -0
- package/ext/ide-components/shared/syntax_highlighter.py +140 -0
- package/ext/ide-components/shared/template_engine.py +123 -0
- package/ext/ide-components/shared/templates/base.html +121 -0
- package/ext/ide-components/shared/test_config_service.py +326 -0
- package/ext/ide-components/shared/tilt_integration.py +158 -0
- package/ext/ui-enhancements/cron-status-server.sh +49 -0
- package/ext/ui-enhancements/cron-status-tracker.sh +146 -0
- package/ext/ui-enhancements/extension.yaml +36 -0
- package/ext/ui-enhancements/tiltfile.py +99 -0
- package/ext/ui-enhancements/ui/cron-jobs-tab.css +396 -0
- package/ext/ui-enhancements/ui/help-panel.css +279 -0
- package/ext/ui-enhancements/ui/icons.css +125 -0
- package/ext/ui-enhancements/ui/tooltips.css +114 -0
- package/package.json +13 -9
- package/specs/specs/TILT_SERVICE_DEFAULTS.star +205 -0
- package/specs/specs/TILT_TECH_STACK.star +159 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# =============================================================================
|
|
2
|
+
# 👁️ DISCOVERY DAEMON - Continuous Service Monitoring
|
|
3
|
+
# =============================================================================
|
|
4
|
+
# Monitors filesystem for new resources and triggers incremental registration
|
|
5
|
+
# =============================================================================
|
|
6
|
+
|
|
7
|
+
load("./resource_snapshot.star", "ResourceSnapshot")
|
|
8
|
+
load("./resource_registry.star", "CacheOps")
|
|
9
|
+
load("../engine/topologies/tilt/manifest/loader.star", "ManifestLoader")
|
|
10
|
+
# Discovery config - inlined for unified repo
|
|
11
|
+
def get_discovery_config():
|
|
12
|
+
return {
|
|
13
|
+
"scan_interval_seconds": 5,
|
|
14
|
+
"max_scan_timeout_ms": 5000,
|
|
15
|
+
"initial_scan_delay_seconds": 2,
|
|
16
|
+
"scan_debounce_ms": 100,
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
_DISCOVERY_CONFIG = get_discovery_config()
|
|
20
|
+
|
|
21
|
+
def run_discovery_daemon(
|
|
22
|
+
scan_interval_seconds=_DISCOVERY_CONFIG["scan_interval_seconds"],
|
|
23
|
+
focus_mode=False,
|
|
24
|
+
focus_stacks=[],
|
|
25
|
+
auto_init_new=True,
|
|
26
|
+
on_new_resource=None,
|
|
27
|
+
verbose=False
|
|
28
|
+
):
|
|
29
|
+
"""
|
|
30
|
+
Run the discovery daemon with continuous monitoring loop.
|
|
31
|
+
|
|
32
|
+
This function is designed to be called as a serve_cmd in a local_resource,
|
|
33
|
+
running continuously to monitor for new resources.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
scan_interval_seconds: Seconds between scans (default: from get_discovery_config)
|
|
37
|
+
focus_mode: Whether focus mode is active
|
|
38
|
+
focus_stacks: List of domains to focus on (empty = all)
|
|
39
|
+
auto_init_new: Whether to auto-init new resources
|
|
40
|
+
on_new_resource: Callback function for new resource detection
|
|
41
|
+
verbose: Enable verbose logging
|
|
42
|
+
"""
|
|
43
|
+
print("🔍 Discovery daemon starting...")
|
|
44
|
+
print(" └─ Scan interval: {}s".format(scan_interval_seconds))
|
|
45
|
+
print(" └─ Focus mode: {}".format("enabled" if focus_mode else "disabled"))
|
|
46
|
+
print(" └─ Auto-init: {}".format("enabled" if auto_init_new else "disabled"))
|
|
47
|
+
|
|
48
|
+
# Initialize snapshot if it doesn't exist
|
|
49
|
+
if not ResourceSnapshot.exists():
|
|
50
|
+
initial_services = ResourceSnapshot.scan()
|
|
51
|
+
ResourceSnapshot.save(initial_services)
|
|
52
|
+
print("✅ Initial snapshot created: {} services".format(len(initial_services)))
|
|
53
|
+
|
|
54
|
+
# Continuous monitoring loop
|
|
55
|
+
while True:
|
|
56
|
+
start_time = 0
|
|
57
|
+
|
|
58
|
+
# Load previous snapshot
|
|
59
|
+
old_snapshot = ResourceSnapshot.load_from_file()
|
|
60
|
+
|
|
61
|
+
# Scan for current services
|
|
62
|
+
current_services = ResourceSnapshot.scan()
|
|
63
|
+
|
|
64
|
+
# Compare to find changes
|
|
65
|
+
diff = ResourceSnapshot.diff(old_snapshot, current_services)
|
|
66
|
+
|
|
67
|
+
# Handle new resources
|
|
68
|
+
if diff.added_count > 0:
|
|
69
|
+
if verbose:
|
|
70
|
+
print("🔍 Detected {} new resource(s)".format(diff.added_count))
|
|
71
|
+
|
|
72
|
+
for resource_path in diff.added:
|
|
73
|
+
_handle_new_service(
|
|
74
|
+
resource_path,
|
|
75
|
+
focus_mode=focus_mode,
|
|
76
|
+
focus_stacks=focus_stacks,
|
|
77
|
+
auto_init=auto_init_new,
|
|
78
|
+
verbose=verbose,
|
|
79
|
+
on_new_resource=on_new_resource
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
# Handle removed services (optional - just log for now)
|
|
83
|
+
if diff.removed_count > 0 and verbose:
|
|
84
|
+
print("🗑️ Detected {} removed service(s)".format(diff.removed_count))
|
|
85
|
+
for resource_path in diff.removed:
|
|
86
|
+
print(" - {}".format(resource_path))
|
|
87
|
+
|
|
88
|
+
# Update snapshot if there were changes
|
|
89
|
+
if diff.added_count > 0 or diff.removed_count > 0:
|
|
90
|
+
ResourceSnapshot.save(current_services)
|
|
91
|
+
if verbose:
|
|
92
|
+
print("📸 Snapshot updated: {} services".format(len(current_services)))
|
|
93
|
+
|
|
94
|
+
# Calculate sleep time (account for scan duration)
|
|
95
|
+
elapsed = 0 - start_time
|
|
96
|
+
sleep_time = max(0, scan_interval_seconds - elapsed)
|
|
97
|
+
|
|
98
|
+
# Sleep before next scan
|
|
99
|
+
if sleep_time > 0:
|
|
100
|
+
sleep(sleep_time)
|
|
101
|
+
|
|
102
|
+
def _handle_new_service(
|
|
103
|
+
resource_path,
|
|
104
|
+
focus_mode=False,
|
|
105
|
+
focus_stacks=[],
|
|
106
|
+
auto_init=True,
|
|
107
|
+
verbose=False,
|
|
108
|
+
on_new_resource=None
|
|
109
|
+
):
|
|
110
|
+
"""
|
|
111
|
+
Process a newly detected service.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
resource_path: Path to service.json
|
|
115
|
+
focus_mode: Whether to filter by stack
|
|
116
|
+
focus_stacks: Allowed domains
|
|
117
|
+
auto_init: Whether to auto-init the service
|
|
118
|
+
verbose: Verbose logging
|
|
119
|
+
on_new_resource: Optional callback
|
|
120
|
+
"""
|
|
121
|
+
# Extract service directory
|
|
122
|
+
resource_dir = resource_path.rsplit("/", 1)[0] if "/" in resource_path else resource_path
|
|
123
|
+
|
|
124
|
+
# Check for package.json (complete service structure)
|
|
125
|
+
package_json_path = resource_dir + "/package.json"
|
|
126
|
+
if not _file_exists(package_json_path):
|
|
127
|
+
print("⏳ Waiting for package.json in {}".format(resource_dir))
|
|
128
|
+
return
|
|
129
|
+
|
|
130
|
+
# Load and validate manifest
|
|
131
|
+
load_result = ManifestLoader.load_from_file(resource_path)
|
|
132
|
+
if load_result.error:
|
|
133
|
+
print("❌ Invalid manifest: {}".format(resource_path))
|
|
134
|
+
print(" └─ Error: {}".format(load_result.error))
|
|
135
|
+
return
|
|
136
|
+
|
|
137
|
+
manifest = load_result.manifest
|
|
138
|
+
if not manifest:
|
|
139
|
+
print("❌ Empty manifest: {}".format(resource_path))
|
|
140
|
+
return
|
|
141
|
+
|
|
142
|
+
# Get service name
|
|
143
|
+
resource_name = manifest.get("appName", "")
|
|
144
|
+
if not resource_name:
|
|
145
|
+
print("❌ Missing appName in: {}".format(resource_path))
|
|
146
|
+
return
|
|
147
|
+
|
|
148
|
+
# Check for duplicates
|
|
149
|
+
if CacheOps.has(resource_name):
|
|
150
|
+
if verbose:
|
|
151
|
+
print("ℹ️ Service already registered: {}".format(resource_name))
|
|
152
|
+
return
|
|
153
|
+
|
|
154
|
+
# Check focus mode
|
|
155
|
+
stack = manifest.get("stack", "")
|
|
156
|
+
if focus_mode and focus_stacks and stack not in focus_stacks:
|
|
157
|
+
print("📋 Focus mode: Skipping {} (stack: {})".format(resource_name, stack))
|
|
158
|
+
return
|
|
159
|
+
|
|
160
|
+
# Log detection
|
|
161
|
+
print("🔍 New resource detected: {}".format(resource_name))
|
|
162
|
+
print(" └─ Path: {}".format(resource_path))
|
|
163
|
+
print(" └─ Stack: {}".format(stack))
|
|
164
|
+
print(" └─ Type: {}".format(manifest.get("appType", "unknown")))
|
|
165
|
+
|
|
166
|
+
# Call callback if provided
|
|
167
|
+
if on_new_resource:
|
|
168
|
+
on_new_resource(resource_name, resource_path, manifest, auto_init)
|
|
169
|
+
|
|
170
|
+
def _file_exists(path):
|
|
171
|
+
"""Check if a file exists."""
|
|
172
|
+
result = local(
|
|
173
|
+
"test -f {} && echo 'yes' || echo 'no'".format(path),
|
|
174
|
+
quiet=True,
|
|
175
|
+
echo_off=True
|
|
176
|
+
)
|
|
177
|
+
return str(result).strip() == "yes"
|
|
178
|
+
|
|
179
|
+
def time_now():
|
|
180
|
+
"""Get current time in seconds (for timing)."""
|
|
181
|
+
result = local("date +%s", quiet=True, echo_off=True)
|
|
182
|
+
return int(str(result).strip())
|
|
183
|
+
|
|
184
|
+
def sleep(seconds):
|
|
185
|
+
"""Sleep for specified seconds."""
|
|
186
|
+
local("sleep {}".format(seconds), quiet=True, echo_off=True)
|
|
187
|
+
|
|
188
|
+
# Export daemon functions
|
|
189
|
+
DiscoveryDaemon = struct(
|
|
190
|
+
run=run_discovery_daemon,
|
|
191
|
+
)
|
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
load("../engine/topologies/tilt/manifest/parser.star", "extract_stack_from_path")
|
|
2
|
+
load("../engine/topologies/tilt/manifest/loader.star", "ManifestLoader")
|
|
3
|
+
load("./manifest/loading.star", "get_default_syncs_for_type")
|
|
4
|
+
|
|
5
|
+
# Out of scope stacks - defined locally for unified repo
|
|
6
|
+
OUT_OF_SCOPE_STACKS = ["warehouse", "logistics", "accounting"]
|
|
7
|
+
|
|
8
|
+
load("./manifest/constants.star", "MANIFEST_FILENAME", "MANIFEST_FILENAME_YAML")
|
|
9
|
+
|
|
10
|
+
# Service defaults - inlined for unified repo
|
|
11
|
+
def get_default_port(app_type):
|
|
12
|
+
if app_type == "frontend":
|
|
13
|
+
return 3000
|
|
14
|
+
return 4000
|
|
15
|
+
|
|
16
|
+
BASE_PORT_FRONTEND = 3000
|
|
17
|
+
BASE_PORT_BACKEND = 4000
|
|
18
|
+
|
|
19
|
+
# Phase 1: Use json_manifest_scanner for manifest discovery (enables Phase 2 multi-source discovery)
|
|
20
|
+
load("./json_manifest_scanner.star", "discover_json_manifests")
|
|
21
|
+
load("./constants.star",
|
|
22
|
+
"RESOURCES_ROOT",
|
|
23
|
+
"DISCOVERY_SCAN_ROOTS",
|
|
24
|
+
"SCAN_ROOT_PATTERNS",
|
|
25
|
+
"MAX_SCAN_TIMEOUT_MS",
|
|
26
|
+
"MAX_MANIFESTS_PER_ROOT")
|
|
27
|
+
|
|
28
|
+
# Phase 1.5: Validation integration for stability
|
|
29
|
+
load("./validation.star", "validate")
|
|
30
|
+
|
|
31
|
+
# Phase 2: Root overlap validation (prevents duplicate scanning from misconfiguration)
|
|
32
|
+
def _validate_scan_roots(roots):
|
|
33
|
+
"""Ensure no root is a subdirectory of another."""
|
|
34
|
+
sorted_roots = sorted(roots, key=len)
|
|
35
|
+
for i, root in enumerate(sorted_roots):
|
|
36
|
+
for other in sorted_roots[i+1:]:
|
|
37
|
+
root_normalized = root.rstrip("/") + "/"
|
|
38
|
+
other_normalized = other.rstrip("/") + "/"
|
|
39
|
+
if other_normalized.startswith(root_normalized):
|
|
40
|
+
print("⚠️ Warning: {} is subdirectory of {}".format(other, root))
|
|
41
|
+
|
|
42
|
+
# Built-in functions available in Tilt (no load needed)
|
|
43
|
+
# read_json, local are built-in
|
|
44
|
+
|
|
45
|
+
# Define locally since parser.star exports this as private
|
|
46
|
+
def _determine_app_type(manifest, resource_path):
|
|
47
|
+
"""
|
|
48
|
+
Determine app type from manifest or service path.
|
|
49
|
+
"""
|
|
50
|
+
# Check if manifest has explicit appType
|
|
51
|
+
if manifest.get("appType") in ["frontend", "backend", "library", "migrator", "sdk", "worker", "infra"]:
|
|
52
|
+
return manifest["appType"]
|
|
53
|
+
|
|
54
|
+
# Extract from path
|
|
55
|
+
dir_name = resource_path.split("/")[-1] if resource_path else ""
|
|
56
|
+
|
|
57
|
+
if dir_name.endswith("-frontend"):
|
|
58
|
+
return "frontend"
|
|
59
|
+
if dir_name.endswith("-backend"):
|
|
60
|
+
return "backend"
|
|
61
|
+
if dir_name.endswith("-migrator"):
|
|
62
|
+
return "migrator"
|
|
63
|
+
if dir_name.endswith("-sdk"):
|
|
64
|
+
return "sdk"
|
|
65
|
+
|
|
66
|
+
# Default to backend
|
|
67
|
+
return "backend"
|
|
68
|
+
|
|
69
|
+
def _normalize_manifest(manifest, resource_path):
|
|
70
|
+
"""
|
|
71
|
+
Normalize manifest to discovery resource format.
|
|
72
|
+
Phase 5: Works with both legacy and new manifest system normalized manifests.
|
|
73
|
+
"""
|
|
74
|
+
# If manifest is already normalized by new system, use it directly
|
|
75
|
+
if manifest.get("_normalized") or manifest.get("syncs"):
|
|
76
|
+
# Extract fields from normalized manifest
|
|
77
|
+
app_name = manifest.get("appName", resource_path.split("/")[-1])
|
|
78
|
+
app_type = manifest.get("appType", _determine_app_type(manifest, resource_path))
|
|
79
|
+
stack = manifest.get("stack", extract_stack_from_path(resource_path))
|
|
80
|
+
port = manifest.get("port", BASE_PORT_BACKEND if app_type == "backend" else BASE_PORT_FRONTEND)
|
|
81
|
+
else:
|
|
82
|
+
# Legacy path: compute fields
|
|
83
|
+
app_name = manifest.get("appName", resource_path.split("/")[-1])
|
|
84
|
+
app_type = _determine_app_type(manifest, resource_path)
|
|
85
|
+
stack = manifest.get("stack", extract_stack_from_path(resource_path))
|
|
86
|
+
port = manifest.get("port", BASE_PORT_BACKEND if app_type == "backend" else BASE_PORT_FRONTEND)
|
|
87
|
+
|
|
88
|
+
features = manifest.get("features", [])
|
|
89
|
+
syncs = manifest.get("syncs", get_default_syncs_for_type(app_type, features))
|
|
90
|
+
dockerfile = manifest.get("dockerfile", ".autogenerated/Dockerfile.app.autogenerated")
|
|
91
|
+
|
|
92
|
+
resource = {
|
|
93
|
+
"name": app_name,
|
|
94
|
+
"dockerfile": app_name + "/" + dockerfile,
|
|
95
|
+
"syncs": syncs,
|
|
96
|
+
"port": port,
|
|
97
|
+
"stack": stack,
|
|
98
|
+
"_manifest": manifest.get("_manifest", manifest),
|
|
99
|
+
"_resource_path": resource_path,
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if app_type == "frontend":
|
|
103
|
+
resource["frontend"] = True
|
|
104
|
+
resource["backendName"] = manifest.get("backendName", app_name.replace("-frontend", "-backend"))
|
|
105
|
+
|
|
106
|
+
if manifest.get("target_path"):
|
|
107
|
+
resource["target_path"] = manifest["target_path"]
|
|
108
|
+
if manifest.get("prisma_client_path"):
|
|
109
|
+
resource["prisma_client_path"] = manifest["prisma_client_path"]
|
|
110
|
+
if manifest.get("registry_host"):
|
|
111
|
+
resource["registry_host"] = manifest["registry_host"]
|
|
112
|
+
if manifest.get("start_command"):
|
|
113
|
+
resource["start_command"] = manifest["start_command"]
|
|
114
|
+
|
|
115
|
+
return resource
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _get_stack_path(stack):
|
|
119
|
+
return "services/product/" + stack
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def initialize_discovery(cache, second_pass=False):
|
|
123
|
+
"""
|
|
124
|
+
Initialize discovery cache by scanning for manifest files.
|
|
125
|
+
|
|
126
|
+
Args:
|
|
127
|
+
cache: Dictionary to populate with discovery data
|
|
128
|
+
second_pass: If True, this is the second initialization pass (for logging only)
|
|
129
|
+
"""
|
|
130
|
+
if cache.get("initialized"):
|
|
131
|
+
return
|
|
132
|
+
|
|
133
|
+
# Scan for resources from JSON manifests (source of truth)
|
|
134
|
+
# YAML files are only for Tilt resource tracking, not data parsing
|
|
135
|
+
if second_pass:
|
|
136
|
+
print(" 1.2 Preparing Tilt resources...")
|
|
137
|
+
else:
|
|
138
|
+
print(" 1.1 Scanning service manifests...")
|
|
139
|
+
|
|
140
|
+
resources = _scan_resources()
|
|
141
|
+
|
|
142
|
+
# Build dependency graph and aliases
|
|
143
|
+
dependencies = {}
|
|
144
|
+
aliases = {}
|
|
145
|
+
path_map = {}
|
|
146
|
+
|
|
147
|
+
for resource in resources:
|
|
148
|
+
name = resource.get("name", "")
|
|
149
|
+
path = resource.get("path", "")
|
|
150
|
+
|
|
151
|
+
# Map name to path
|
|
152
|
+
if name:
|
|
153
|
+
aliases[name] = path
|
|
154
|
+
path_map[path] = name
|
|
155
|
+
|
|
156
|
+
# Extract dependencies from manifest
|
|
157
|
+
manifest = resource.get("_manifest", {})
|
|
158
|
+
deps = manifest.get("dependsOn", [])
|
|
159
|
+
if deps:
|
|
160
|
+
dependencies[name] = deps
|
|
161
|
+
|
|
162
|
+
# Populate cache
|
|
163
|
+
cache["app_resources"] = resources
|
|
164
|
+
cache["resource_dependencies"] = dependencies
|
|
165
|
+
cache["resource_aliases"] = aliases
|
|
166
|
+
cache["resource_path_map"] = path_map
|
|
167
|
+
cache["stack_configs"] = {}
|
|
168
|
+
cache["initialized"] = True
|
|
169
|
+
if second_pass:
|
|
170
|
+
cache["second_pass"] = True
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def _scan_resources():
|
|
174
|
+
"""
|
|
175
|
+
FIRST PASS: Scan for all resources with JSON manifest files from multiple roots.
|
|
176
|
+
Phase 2: Multi-root discovery with validation, deduplication, and explicit stack extraction.
|
|
177
|
+
Returns list of resource dictionaries.
|
|
178
|
+
"""
|
|
179
|
+
resources = []
|
|
180
|
+
|
|
181
|
+
# Phase 1.5: Track validation stats and skipped stacks for discovery summary
|
|
182
|
+
validation_stats = {
|
|
183
|
+
"total_manifests": 0,
|
|
184
|
+
"valid_manifests": 0,
|
|
185
|
+
"manifests_with_warnings": 0,
|
|
186
|
+
"total_warnings": 0,
|
|
187
|
+
}
|
|
188
|
+
skipped_stacks = {} # Track skipped stacks for summary
|
|
189
|
+
|
|
190
|
+
# Phase 2: Validate scan roots don't overlap (prevents duplicate scanning)
|
|
191
|
+
_validate_scan_roots(DISCOVERY_SCAN_ROOTS)
|
|
192
|
+
|
|
193
|
+
# Phase 2: Scan all root directories and collect manifest paths
|
|
194
|
+
all_manifest_paths = [] # Collect from all roots
|
|
195
|
+
|
|
196
|
+
# Scan all root directories (quiet unless verbose)
|
|
197
|
+
total_roots = len(DISCOVERY_SCAN_ROOTS)
|
|
198
|
+
|
|
199
|
+
for root in DISCOVERY_SCAN_ROOTS:
|
|
200
|
+
# Use json_manifest_scanner.discover_json_manifests() to find all manifest files
|
|
201
|
+
paths = discover_json_manifests(root)
|
|
202
|
+
|
|
203
|
+
if paths:
|
|
204
|
+
# Phase 2: Performance guardrails - warn if root is too large
|
|
205
|
+
if len(paths) > MAX_MANIFESTS_PER_ROOT:
|
|
206
|
+
print(" ⚠️ Large root: {} has {} manifests (max: {})".format(
|
|
207
|
+
root, len(paths), MAX_MANIFESTS_PER_ROOT))
|
|
208
|
+
|
|
209
|
+
# Only show in verbose mode
|
|
210
|
+
if os.environ.get('TILT_LOG_LEVEL') == 'verbose':
|
|
211
|
+
print(" 📁 Scanned {} manifests from {}".format(len(paths), root))
|
|
212
|
+
all_manifest_paths.extend(paths)
|
|
213
|
+
|
|
214
|
+
# Phase 2: Deduplicate by path (in case of overlaps between roots)
|
|
215
|
+
seen_paths = {}
|
|
216
|
+
manifest_paths = []
|
|
217
|
+
for path in all_manifest_paths:
|
|
218
|
+
if path not in seen_paths:
|
|
219
|
+
seen_paths[path] = True
|
|
220
|
+
manifest_paths.append(path)
|
|
221
|
+
|
|
222
|
+
# CRITICAL FIX: Filter out tdk-cli internal paths to prevent self-scanning
|
|
223
|
+
# This prevents tdk-cli from discovering its own internal test stubs
|
|
224
|
+
def _is_tdk_cli_internal_path(path):
|
|
225
|
+
"""Check if path is within tdk-cli's own directory structure."""
|
|
226
|
+
internal_markers = [
|
|
227
|
+
"tdk-cli/discovery/services/",
|
|
228
|
+
"tdk-cli/discovery/identity-",
|
|
229
|
+
]
|
|
230
|
+
for marker in internal_markers:
|
|
231
|
+
if marker in path:
|
|
232
|
+
return True
|
|
233
|
+
return False
|
|
234
|
+
|
|
235
|
+
filtered_manifest_paths = []
|
|
236
|
+
for path in manifest_paths:
|
|
237
|
+
if _is_tdk_cli_internal_path(path):
|
|
238
|
+
print("⚠️ Skipping tdk-cli internal stub: {}".format(path))
|
|
239
|
+
continue
|
|
240
|
+
filtered_manifest_paths.append(path)
|
|
241
|
+
|
|
242
|
+
manifest_paths = filtered_manifest_paths
|
|
243
|
+
|
|
244
|
+
if not manifest_paths:
|
|
245
|
+
return resources
|
|
246
|
+
|
|
247
|
+
for manifest_path in manifest_paths:
|
|
248
|
+
# manifest_path already stripped by json_manifest_scanner
|
|
249
|
+
|
|
250
|
+
# Parse manifest using proper loader (handles JSON and YAML)
|
|
251
|
+
# First, extract service path from manifest path
|
|
252
|
+
full_resource_path = manifest_path.rsplit("/", 1)[0]
|
|
253
|
+
|
|
254
|
+
# Normalize path to be relative (strip leading / if present)
|
|
255
|
+
# Docker build context requires relative paths
|
|
256
|
+
if full_resource_path.startswith('/'):
|
|
257
|
+
full_resource_path = full_resource_path[1:]
|
|
258
|
+
|
|
259
|
+
# Use the loader which handles both JSON and YAML and normalizes format
|
|
260
|
+
load_result = ManifestLoader.load_from_file(manifest_path)
|
|
261
|
+
if load_result.error:
|
|
262
|
+
print("Error loading manifest: " + load_result.error)
|
|
263
|
+
continue
|
|
264
|
+
|
|
265
|
+
manifest = load_result.manifest
|
|
266
|
+
if not manifest:
|
|
267
|
+
continue
|
|
268
|
+
|
|
269
|
+
# Phase 1.5: Validate manifest structure and content
|
|
270
|
+
validation_stats["total_manifests"] += 1
|
|
271
|
+
validation_issues = validate(manifest)
|
|
272
|
+
if validation_issues:
|
|
273
|
+
validation_stats["manifests_with_warnings"] += 1
|
|
274
|
+
validation_stats["total_warnings"] += len(validation_issues)
|
|
275
|
+
# Log all validation issues with file path for context
|
|
276
|
+
for issue in validation_issues:
|
|
277
|
+
print("⚠️ Validation: " + manifest_path + ": " + issue)
|
|
278
|
+
# Lenient mode: log warnings but continue discovery
|
|
279
|
+
# (Strict mode could fail here with: continue)
|
|
280
|
+
else:
|
|
281
|
+
validation_stats["valid_manifests"] += 1
|
|
282
|
+
|
|
283
|
+
# Skip out-of-scope stacks for alpha (defined in spec.master)
|
|
284
|
+
stack = manifest.get("stack", "")
|
|
285
|
+
if stack in OUT_OF_SCOPE_STACKS:
|
|
286
|
+
# Track skipped stack for summary instead of printing immediately
|
|
287
|
+
if stack not in skipped_stacks:
|
|
288
|
+
skipped_stacks[stack] = 0
|
|
289
|
+
skipped_stacks[stack] += 1
|
|
290
|
+
continue
|
|
291
|
+
|
|
292
|
+
# Convert relative path to project-root-relative path
|
|
293
|
+
# manifest_path is like: ../../../../services/product/{domain}/{service}/manifest.json
|
|
294
|
+
# We want: services/product/{domain}/{service}
|
|
295
|
+
if manifest_path.startswith("../../../../"):
|
|
296
|
+
project_relative_path = manifest_path[12:] # Remove "../../../../" prefix
|
|
297
|
+
else:
|
|
298
|
+
project_relative_path = manifest_path
|
|
299
|
+
|
|
300
|
+
# Extract service info from the project-relative path
|
|
301
|
+
# Format: services/product/{domain}/{service}/" + MANIFEST_FILENAME,
|
|
302
|
+
path_parts = project_relative_path.split("/")
|
|
303
|
+
if len(path_parts) < 2:
|
|
304
|
+
continue
|
|
305
|
+
|
|
306
|
+
resource_dir = path_parts[-2] # e.g., "user-management-frontend"
|
|
307
|
+
|
|
308
|
+
# Phase 2: Use explicit stack extraction patterns
|
|
309
|
+
# Determine which root this manifest came from
|
|
310
|
+
matching_root = None
|
|
311
|
+
for root in DISCOVERY_SCAN_ROOTS:
|
|
312
|
+
if manifest_path.startswith(root):
|
|
313
|
+
matching_root = root
|
|
314
|
+
break
|
|
315
|
+
|
|
316
|
+
# Use configured pattern or default to parent folder
|
|
317
|
+
pattern = SCAN_ROOT_PATTERNS.get(matching_root, {"stack_depth": -2})
|
|
318
|
+
depth = pattern["stack_depth"]
|
|
319
|
+
|
|
320
|
+
# Extract stack based on configured depth
|
|
321
|
+
if abs(depth) <= len(path_parts):
|
|
322
|
+
stack_dir = path_parts[depth]
|
|
323
|
+
else:
|
|
324
|
+
stack_dir = path_parts[-2] if len(path_parts) >= 2 else "" # Fallback to parent
|
|
325
|
+
|
|
326
|
+
# Build paths relative to project root (for use by other modules)
|
|
327
|
+
# Handle both nested paths (services/product/stack/service) and flat paths (identity-*)
|
|
328
|
+
if len(path_parts) >= 3:
|
|
329
|
+
base_path = "/".join(path_parts[:-2]) # e.g., "services/product/users"
|
|
330
|
+
elif len(path_parts) == 2:
|
|
331
|
+
base_path = path_parts[0] # e.g., "identity-management-backend"
|
|
332
|
+
else:
|
|
333
|
+
base_path = path_parts[0] if path_parts else ""
|
|
334
|
+
resource_path = base_path # This is the DOMAIN path
|
|
335
|
+
full_resource_path = project_relative_path.rsplit("/", 1)[0] # Full path to service
|
|
336
|
+
|
|
337
|
+
# Normalize path to be relative (strip leading / if present)
|
|
338
|
+
# Docker build context requires relative paths
|
|
339
|
+
if full_resource_path.startswith('/'):
|
|
340
|
+
full_resource_path = full_resource_path[1:]
|
|
341
|
+
|
|
342
|
+
# BUG FIX: Normalize worktree paths to project-relative paths
|
|
343
|
+
# Worktrees create paths like .worktrees/task1-dry/services/platform/...
|
|
344
|
+
# These need to be converted to services/platform/... for correct Dockerfile resolution
|
|
345
|
+
if full_resource_path.startswith('.worktrees/'):
|
|
346
|
+
# Extract the part after the worktree name
|
|
347
|
+
parts = full_resource_path.split('/')
|
|
348
|
+
if len(parts) >= 3:
|
|
349
|
+
# Skip .worktrees/ and the worktree name
|
|
350
|
+
full_resource_path = '/'.join(parts[2:])
|
|
351
|
+
elif '.worktrees/' in full_resource_path:
|
|
352
|
+
# Handle paths that include worktree somewhere in the middle
|
|
353
|
+
worktree_idx = full_resource_path.find('.worktrees/')
|
|
354
|
+
after_worktree = full_resource_path[worktree_idx:]
|
|
355
|
+
parts = after_worktree.split('/')
|
|
356
|
+
if len(parts) >= 3:
|
|
357
|
+
full_resource_path = '/'.join(parts[2:])
|
|
358
|
+
|
|
359
|
+
# Also normalize resource_path (base_path) if it has worktree
|
|
360
|
+
if resource_path.startswith('.worktrees/'):
|
|
361
|
+
parts = resource_path.split('/')
|
|
362
|
+
if len(parts) >= 3:
|
|
363
|
+
resource_path = '/'.join(parts[2:])
|
|
364
|
+
elif '.worktrees/' in resource_path:
|
|
365
|
+
worktree_idx = resource_path.find('.worktrees/')
|
|
366
|
+
after_worktree = resource_path[worktree_idx:]
|
|
367
|
+
parts = after_worktree.split('/')
|
|
368
|
+
if len(parts) >= 3:
|
|
369
|
+
resource_path = '/'.join(parts[2:])
|
|
370
|
+
|
|
371
|
+
app_name = manifest.get("appName", resource_dir)
|
|
372
|
+
app_type = manifest.get("appType", "backend")
|
|
373
|
+
stack = manifest.get("stack", stack_dir)
|
|
374
|
+
port = manifest.get("port", get_default_port(app_type))
|
|
375
|
+
features = manifest.get("features", [])
|
|
376
|
+
|
|
377
|
+
# Build resource entry
|
|
378
|
+
# dockerfile path is relative to the resource directory (prepended with res_path by caller)
|
|
379
|
+
# Use custom dockerfile from manifest if specified, otherwise use autogenerated default
|
|
380
|
+
custom_dockerfile = manifest.get("dockerfile")
|
|
381
|
+
if custom_dockerfile:
|
|
382
|
+
dockerfile_path = custom_dockerfile
|
|
383
|
+
else:
|
|
384
|
+
dockerfile_path = ".autogenerated/Dockerfile.app.autogenerated"
|
|
385
|
+
|
|
386
|
+
resource = {
|
|
387
|
+
"name": app_name,
|
|
388
|
+
"dockerfile": dockerfile_path,
|
|
389
|
+
"syncs": get_default_syncs_for_type(app_type, features),
|
|
390
|
+
"port": port,
|
|
391
|
+
"stack": stack,
|
|
392
|
+
"_manifest": manifest,
|
|
393
|
+
"_resource_path": full_resource_path,
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
# Add frontend-specific fields
|
|
397
|
+
if app_type == "frontend":
|
|
398
|
+
resource["frontend"] = True
|
|
399
|
+
resource["backendName"] = manifest.get("backendName", app_name.replace("-frontend", "-backend"))
|
|
400
|
+
|
|
401
|
+
# Build resource entry
|
|
402
|
+
resource_key = stack if stack else app_name.replace("-" + app_type, "")
|
|
403
|
+
|
|
404
|
+
# Check if app resource already exists (merge resources for same stack)
|
|
405
|
+
existing_app_resource = None
|
|
406
|
+
for app_res in resources:
|
|
407
|
+
if app_res["name"] == resource_key:
|
|
408
|
+
existing_app_resource = app_res
|
|
409
|
+
break
|
|
410
|
+
|
|
411
|
+
if existing_app_resource:
|
|
412
|
+
# Check if resource with same name already exists (prevent duplicates from multiple scan roots)
|
|
413
|
+
resource_exists = False
|
|
414
|
+
for existing_res in existing_app_resource["resources"]:
|
|
415
|
+
if existing_res.get("name") == resource["name"]:
|
|
416
|
+
resource_exists = True
|
|
417
|
+
print(" ⚠️ Skipping duplicate resource: " + app_name + " (from " + resource_path + ")")
|
|
418
|
+
break
|
|
419
|
+
|
|
420
|
+
if not resource_exists:
|
|
421
|
+
# Add resource to existing app resource
|
|
422
|
+
existing_app_resource["resources"].append(resource)
|
|
423
|
+
existing_app_resource["labels"].append("app." + app_name)
|
|
424
|
+
pass # Service merged into existing domain
|
|
425
|
+
else:
|
|
426
|
+
# Create new app resource entry
|
|
427
|
+
# Use resource_path (domain path) for the app_resource - docker-compose goes here
|
|
428
|
+
# Individual resources have _resource_path for their specific paths
|
|
429
|
+
app_resource = {
|
|
430
|
+
"name": resource_key,
|
|
431
|
+
"appName": resource_key,
|
|
432
|
+
"path": resource_path,
|
|
433
|
+
"labels": ["app." + app_name] if app_name else [],
|
|
434
|
+
"resources": [resource],
|
|
435
|
+
}
|
|
436
|
+
pass # New domain group created
|
|
437
|
+
resources.append(app_resource)
|
|
438
|
+
|
|
439
|
+
# Phase 1.5: Print consolidated discovery summary
|
|
440
|
+
if validation_stats["total_manifests"] > 0:
|
|
441
|
+
# Calculate total skipped manually (sum() not available in Starlark)
|
|
442
|
+
total_skipped = 0
|
|
443
|
+
for count in skipped_stacks.values():
|
|
444
|
+
total_skipped += count
|
|
445
|
+
print("✅ {} resources loaded ({} valid, {} skipped)".format(
|
|
446
|
+
validation_stats["total_manifests"],
|
|
447
|
+
validation_stats["valid_manifests"],
|
|
448
|
+
total_skipped))
|
|
449
|
+
|
|
450
|
+
if validation_stats["manifests_with_warnings"] > 0:
|
|
451
|
+
print(" ⚠️ {} manifest(s) with {} validation warning(s)".format(
|
|
452
|
+
validation_stats["manifests_with_warnings"],
|
|
453
|
+
validation_stats["total_warnings"]))
|
|
454
|
+
|
|
455
|
+
if skipped_stacks and os.environ.get('TILT_LOG_LEVEL') == 'verbose':
|
|
456
|
+
skipped_list = ", ".join(["{} ({})".format(d, c) for d, c in skipped_stacks.items()])
|
|
457
|
+
print(" 📋 Focus mode skipped: {}".format(skipped_list))
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
return resources
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
appName: identity-management-backend
|
|
2
|
+
appType: backend
|
|
3
|
+
stack: identity
|
|
4
|
+
port: 4004
|
|
5
|
+
apiPath: /api/identity-management
|
|
6
|
+
features:
|
|
7
|
+
- nats
|
|
8
|
+
- prisma
|
|
9
|
+
healthCheck: True
|
|
10
|
+
healthCheckPath: /api/v1/health
|
|
11
|
+
params:
|
|
12
|
+
BASE_URL: http://identity.backend.beauty.local
|
|
13
|
+
EMAIL_FROM: noreply@beauty-crm.local
|
|
14
|
+
UPSTASH_REDIS_REST_URL: http://redis:6379
|
|
15
|
+
path: identity-management-backend
|
|
16
|
+
playwright: True
|
|
17
|
+
prisma_client_path: ./node_modules/.prisma/client/client
|
|
18
|
+
replicas: 1
|
|
19
|
+
runtime: bun
|
|
20
|
+
secrets:
|
|
21
|
+
optional:
|
|
22
|
+
required:
|
|
23
|
+
- JWT_REFRESH_SECRET
|
|
24
|
+
- JWT_SECRET
|
|
25
|
+
- PASSWORD_PEPPER
|
|
26
|
+
- UPSTASH_REDIS_REST_TOKEN
|