@tdk-landscape/tdk-cli-core 1.3.50 → 1.3.52
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/ci.yml +13 -0
- package/.github/workflows/release-binaries.yml +76 -1
- package/CHANGELOG.md +50 -0
- package/README.md +54 -17
- package/cli/README.md +16 -43
- package/cli/biome.json +0 -4
- package/cli/dist/commands/help.d.ts.map +1 -1
- package/cli/dist/commands/help.js +22 -39
- package/cli/dist/commands/help.js.map +1 -1
- package/cli/dist/commands/project.d.ts +6 -0
- package/cli/dist/commands/project.d.ts.map +1 -1
- package/cli/dist/commands/project.js +16 -0
- package/cli/dist/commands/project.js.map +1 -1
- package/cli/dist/commands/up.d.ts.map +1 -1
- package/cli/dist/commands/up.js +5 -0
- package/cli/dist/commands/up.js.map +1 -1
- package/cli/dist/commands/upgrade.d.ts +3 -0
- package/cli/dist/commands/upgrade.d.ts.map +1 -1
- package/cli/dist/commands/upgrade.js +43 -1
- package/cli/dist/commands/upgrade.js.map +1 -1
- package/cli/dist/utils/constants.d.ts +1 -1
- package/cli/dist/utils/constants.d.ts.map +1 -1
- package/cli/dist/utils/constants.js +1 -1
- package/cli/dist/utils/constants.js.map +1 -1
- package/cli/dist/utils/doctor-runtime.js +5 -5
- package/cli/dist/utils/doctor-runtime.js.map +1 -1
- package/cli/dist/utils/prompt.d.ts.map +1 -1
- package/cli/dist/utils/prompt.js +6 -1
- package/cli/dist/utils/prompt.js.map +1 -1
- package/cli/dist/utils/service-urls.js +1 -1
- package/cli/dist/utils/service-urls.js.map +1 -1
- package/cli/package.json +2 -1
- package/cli/shared-platform-engineering/docker-templates/infisical-entrypoint.sh +2 -2
- package/cli/src/commands/__tests__/doctor.test.ts +8 -8
- package/cli/src/commands/__tests__/upgrade.test.ts +90 -3
- package/cli/src/commands/help.ts +22 -39
- package/cli/src/commands/project.ts +20 -0
- package/cli/src/commands/up.ts +10 -0
- package/cli/src/commands/upgrade.ts +52 -1
- package/cli/src/utils/constants.ts +1 -1
- package/cli/src/utils/doctor-runtime.ts +5 -5
- package/cli/src/utils/prompt.ts +8 -1
- package/cli/src/utils/service-urls.ts +1 -1
- package/discovery/identity-management-backend/service.yaml +2 -2
- package/discovery/identity-management-frontend/service.yaml +1 -1
- package/discovery/registry.star +3 -1
- package/discovery/services/platform/orchestrator/orchestrator-glue-backend/service.yaml +2 -2
- package/docs/demo.svg +217 -0
- package/engine/assets/docker/golden-layers.Dockerfile +18 -18
- package/engine/assets/docker/wake-gateway/Dockerfile +4 -3
- package/engine/assets/docker/wake-gateway/decide.test.ts +72 -1
- package/engine/assets/docker/wake-gateway/decide.ts +51 -2
- package/engine/assets/docker/wake-gateway/server.ts +165 -28
- package/engine/assets/scripts/mac-m1-resource-monitor.sh +1 -1
- package/engine/scripts/debug-container.sh +8 -3
- package/engine/spec.master +2 -2
- package/engine/topologies/platform/docker/compose/traefik_standalone.star +23 -1
- package/engine/topologies/platform/docker/layers/prisma/prisma_build.star +1 -1
- package/engine/topologies/platform/docker/networking/traefik_static_routes.star +62 -8
- package/engine/topologies/tilt/common/utils.star +1 -1
- package/engine/topologies/tilt/resources/orchestrator/apply_compose_resource_registration.star +89 -12
- package/engine/topologies/tilt/resources/orchestrator/builders/typescript.star +20 -0
- package/install.sh +19 -242
- package/openspec/changes/prioritized-cold-start/design.md +27 -3
- package/openspec/changes/prioritized-cold-start/proposal.md +1 -0
- package/openspec/changes/prioritized-cold-start/specs/on-demand-resource-startup/spec.md +9 -3
- package/openspec/changes/prioritized-cold-start/tasks.md +24 -1
- package/package.json +1 -1
- package/platform/services/platform/proxy/service.yaml +1 -1
- package/scripts/record-demo.sh +43 -0
- package/scripts/release-binaries.sh +8 -5
- package/shared-platform-engineering/docker-templates/infisical-entrypoint.sh +2 -2
- package/tests/tilt-engine/test_sablier_defer_start.py +41 -0
- package/tests/tilt-engine/test_standalone_traefik.py +14 -0
- package/tests/tilt-engine/test_traefik_static_wake_route.py +68 -0
- package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/codebase-audit-report.md +0 -62
- package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/.openspec.yaml +0 -5
- package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/design.md +0 -94
- package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/proposal.md +0 -46
- package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/ai-slop-cleanup/CLEANUP_REPORT_8.md +0 -174
- package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/ai-slop-cleanup/CRITICAL_ASSESSMENT_8.md +0 -160
- package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/circular-deps/CRITICAL_ASSESSMENT_4.md +0 -381
- package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/dead-code/CRITICAL_ASSESSMENT_3.md +0 -316
- package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/deduplication/CRITICAL_ASSESSMENT_1.md +0 -290
- package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/deduplication/IMPLEMENTATION_SUMMARY_1.md +0 -233
- package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/defensive-cleanup/CRITICAL_ASSESSMENT_6.md +0 -835
- package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/legacy-cleanup/CRITICAL_ASSESSMENT_7.md +0 -254
- package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/legacy-cleanup/IMPLEMENTATION_SUMMARY_7.md +0 -187
- package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/strong-typing/CRITICAL_ASSESSMENT_5.md +0 -269
- package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/type-consolidation/CRITICAL_ASSESSMENT_2.md +0 -312
- package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/type-consolidation/IMPLEMENTATION_SUMMARY.md +0 -170
- package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/tasks.md +0 -137
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
# 2. Or update service.json
|
|
8
8
|
#
|
|
9
9
|
# Generation Source: GoldenImage.generate_layered_dockerfile()
|
|
10
|
-
# Service:
|
|
10
|
+
# Service: tdk-project-l1-l4
|
|
11
11
|
# Type: GOLDEN LAYERED IMAGES | L1-L4 Pre-built Layers
|
|
12
12
|
###############################################################################
|
|
13
13
|
|
|
@@ -15,14 +15,14 @@
|
|
|
15
15
|
# 🏗️ GOLDEN LAYERED IMAGES - Multi-Stage Base Images
|
|
16
16
|
# =============================================================================
|
|
17
17
|
# This Dockerfile builds 8 golden images:
|
|
18
|
-
# -
|
|
19
|
-
# -
|
|
20
|
-
# -
|
|
21
|
-
# -
|
|
22
|
-
# -
|
|
23
|
-
# -
|
|
24
|
-
# -
|
|
25
|
-
# -
|
|
18
|
+
# - tdk-project-l1:latest - OS base + Bun runtime
|
|
19
|
+
# - tdk-project-l2:latest - Dependencies installed
|
|
20
|
+
# - tdk-project-l3-backend:latest - Backend build tools (Prisma, no Infisical CLI)
|
|
21
|
+
# - tdk-project-l3-frontend:latest - Frontend build tools (no Prisma)
|
|
22
|
+
# - tdk-project-l3-migrator:latest - Migrator build tools (Prisma, no Infisical CLI)
|
|
23
|
+
# - tdk-project-l4-backend:latest - Backend production runtime
|
|
24
|
+
# - tdk-project-l4-frontend:latest - Frontend production runtime (Nginx)
|
|
25
|
+
# - tdk-project-l4-migrator:latest - Migrator production runtime
|
|
26
26
|
#
|
|
27
27
|
# Services use these as base images to skip heavy dependency installation.
|
|
28
28
|
#
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
# =============================================================================
|
|
39
39
|
FROM oven/bun:1.3.11-alpine AS l1_golden
|
|
40
40
|
|
|
41
|
-
LABEL layer="l1" description="OS base with runtime environment" maintainer="
|
|
41
|
+
LABEL layer="l1" description="OS base with runtime environment" maintainer="tdk-project"
|
|
42
42
|
|
|
43
43
|
# System dependencies for all services
|
|
44
44
|
RUN apk add --no-cache ca-certificates tzdata curl file bash openssl
|
|
@@ -57,7 +57,7 @@ WORKDIR /app
|
|
|
57
57
|
# =============================================================================
|
|
58
58
|
FROM l1_golden AS l2_golden
|
|
59
59
|
|
|
60
|
-
LABEL layer="l2" description="Base with common dependencies installed" maintainer="
|
|
60
|
+
LABEL layer="l2" description="Base with common dependencies installed" maintainer="tdk-project"
|
|
61
61
|
|
|
62
62
|
# Install minimal debugging tools (removed: vim, python3, make, g++ = -67MB)
|
|
63
63
|
RUN apk add --no-cache curl netcat-openbsd jq bind-tools
|
|
@@ -74,7 +74,7 @@ RUN echo "Bun version: $(bun --version)"
|
|
|
74
74
|
# =============================================================================
|
|
75
75
|
FROM l2_golden AS l3_backend_golden
|
|
76
76
|
|
|
77
|
-
LABEL layer="l3-backend" description="Backend build tools with Prisma" maintainer="
|
|
77
|
+
LABEL layer="l3-backend" description="Backend build tools with Prisma" maintainer="tdk-project"
|
|
78
78
|
|
|
79
79
|
# Install Prisma CLI globally (pinned for deterministic builds)
|
|
80
80
|
RUN bun add -g prisma@7.5.0 @prisma/client@7.5.0
|
|
@@ -94,7 +94,7 @@ RUN echo "Prisma version: $(bunx prisma --version | head -1)"
|
|
|
94
94
|
# =============================================================================
|
|
95
95
|
FROM l2_golden AS l3_frontend_golden
|
|
96
96
|
|
|
97
|
-
LABEL layer="l3-frontend" description="Frontend build tools without Prisma" maintainer="
|
|
97
|
+
LABEL layer="l3-frontend" description="Frontend build tools without Prisma" maintainer="tdk-project"
|
|
98
98
|
|
|
99
99
|
# Frontend doesn't need Prisma, just verify Bun
|
|
100
100
|
RUN echo "Bun version: $(bun --version)"
|
|
@@ -105,7 +105,7 @@ RUN echo "Bun version: $(bun --version)"
|
|
|
105
105
|
# =============================================================================
|
|
106
106
|
FROM l2_golden AS l3_migrator_golden
|
|
107
107
|
|
|
108
|
-
LABEL layer="l3-migrator" description="Migrator build tools with Prisma" maintainer="
|
|
108
|
+
LABEL layer="l3-migrator" description="Migrator build tools with Prisma" maintainer="tdk-project"
|
|
109
109
|
|
|
110
110
|
# Install Prisma CLI globally (required for migrations, pinned)
|
|
111
111
|
RUN bun add -g prisma@7.5.0 @prisma/client@7.5.0
|
|
@@ -125,7 +125,7 @@ RUN echo "Prisma version: $(bunx prisma --version | head -1)"
|
|
|
125
125
|
# =============================================================================
|
|
126
126
|
FROM l1_golden AS l4_backend_bun
|
|
127
127
|
|
|
128
|
-
LABEL layer="l4-backend-bun" description="Backend production runtime (Bun)" maintainer="
|
|
128
|
+
LABEL layer="l4-backend-bun" description="Backend production runtime (Bun)" maintainer="tdk-project"
|
|
129
129
|
|
|
130
130
|
ENV NODE_ENV=production
|
|
131
131
|
|
|
@@ -150,7 +150,7 @@ WORKDIR /app
|
|
|
150
150
|
# =============================================================================
|
|
151
151
|
FROM node:22-alpine AS l4_backend_node
|
|
152
152
|
|
|
153
|
-
LABEL layer="l4-backend-node" description="Backend production runtime (Node.js - lightweight)" maintainer="
|
|
153
|
+
LABEL layer="l4-backend-node" description="Backend production runtime (Node.js - lightweight)" maintainer="tdk-project"
|
|
154
154
|
|
|
155
155
|
ENV NODE_ENV=production
|
|
156
156
|
|
|
@@ -180,7 +180,7 @@ FROM l4_backend_bun AS l4_backend_golden
|
|
|
180
180
|
# =============================================================================
|
|
181
181
|
FROM nginx:alpine AS l4_frontend_golden
|
|
182
182
|
|
|
183
|
-
LABEL layer="l4-frontend" description="Frontend production runtime with Nginx" maintainer="
|
|
183
|
+
LABEL layer="l4-frontend" description="Frontend production runtime with Nginx" maintainer="tdk-project"
|
|
184
184
|
|
|
185
185
|
ENV NODE_ENV=production
|
|
186
186
|
|
|
@@ -198,7 +198,7 @@ WORKDIR /usr/share/nginx/html
|
|
|
198
198
|
# =============================================================================
|
|
199
199
|
FROM l1_golden AS l4_migrator_golden
|
|
200
200
|
|
|
201
|
-
LABEL layer="l4-migrator" description="Migrator runtime with Prisma" maintainer="
|
|
201
|
+
LABEL layer="l4-migrator" description="Migrator runtime with Prisma" maintainer="tdk-project"
|
|
202
202
|
|
|
203
203
|
ENV NODE_ENV=production
|
|
204
204
|
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
# Wake gateway (see openspec/changes/prioritized-cold-start): fronts every
|
|
2
2
|
# `sablier.deferStart` resource's Traefik static route, triggers cold
|
|
3
3
|
# containers via `tilt trigger` and/or Sablier, then holds the request until
|
|
4
|
-
# healthy. Needs the Docker CLI (client only, talks
|
|
5
|
-
# /var/run/docker.sock
|
|
4
|
+
# healthy. Needs the Docker CLI plus its Compose plugin (client only, talks
|
|
5
|
+
# to the mounted /var/run/docker.sock; compose starts an already-built image
|
|
6
|
+
# directly, ahead of Tilt's build queue) and the real `tilt` CLI (D3: shells out to the
|
|
6
7
|
# documented `tilt trigger` command rather than depending on Tilt's
|
|
7
8
|
# undocumented internal HTTP API).
|
|
8
9
|
|
|
@@ -13,7 +14,7 @@ FROM oven/bun:1.3.11-alpine AS base
|
|
|
13
14
|
# directly (not via tilt-dev's install.sh, which shells out to `sudo` -- not
|
|
14
15
|
# present in this minimal image, and unnecessary since we're already root).
|
|
15
16
|
ARG TILT_VERSION=0.37.7
|
|
16
|
-
RUN apk add --no-cache docker-cli curl gcompat \
|
|
17
|
+
RUN apk add --no-cache docker-cli docker-cli-compose curl gcompat \
|
|
17
18
|
&& arch="$(uname -m)" \
|
|
18
19
|
&& case "$arch" in aarch64) tilt_arch=arm64 ;; x86_64) tilt_arch=x86_64 ;; *) echo "unsupported arch: $arch" >&2; exit 1 ;; esac \
|
|
19
20
|
&& curl -fsSL "https://github.com/tilt-dev/tilt/releases/download/v${TILT_VERSION}/tilt.${TILT_VERSION}.linux.${tilt_arch}.tar.gz" \
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
COMPOSE_START_TIMEOUT_MS,
|
|
4
|
+
EARLY_RESPONSE_MS,
|
|
5
|
+
MAX_EARLY_RESPONSE_MS,
|
|
6
|
+
RETRY_AFTER_SECONDS,
|
|
7
|
+
TILT_TRIGGER_TIMEOUT_MS,
|
|
8
|
+
decideWakeAction,
|
|
9
|
+
earlyResponseMs,
|
|
10
|
+
evaluateReadiness,
|
|
11
|
+
wakeTimeoutMs,
|
|
12
|
+
} from "./decide";
|
|
3
13
|
|
|
4
14
|
describe("decideWakeAction", () => {
|
|
5
15
|
test("never-created resource: tilt trigger only, no Sablier start", () => {
|
|
@@ -56,6 +66,31 @@ describe("decideWakeAction", () => {
|
|
|
56
66
|
]);
|
|
57
67
|
});
|
|
58
68
|
|
|
69
|
+
test("never-created resource with compose details: started directly, not queued in Tilt", () => {
|
|
70
|
+
const action = decideWakeAction({
|
|
71
|
+
resourceContainerName: "data-governance-api",
|
|
72
|
+
resourceState: "absent",
|
|
73
|
+
dependencies: [{ name: "peer-api", state: "absent" }],
|
|
74
|
+
hasComposeInvocation: true,
|
|
75
|
+
});
|
|
76
|
+
expect(action.useComposeStart).toBe(true);
|
|
77
|
+
// Dependencies carry no compose details, so they still go through Tilt.
|
|
78
|
+
expect(action.tiltTriggerNames).toEqual(["peer-api"]);
|
|
79
|
+
expect(action.useSablierStart).toBe(false);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test("existing (exited) container keeps the Sablier path even with compose details", () => {
|
|
83
|
+
const action = decideWakeAction({
|
|
84
|
+
resourceContainerName: "data-governance-api",
|
|
85
|
+
resourceState: "exited",
|
|
86
|
+
dependencies: [],
|
|
87
|
+
hasComposeInvocation: true,
|
|
88
|
+
});
|
|
89
|
+
expect(action.useComposeStart).toBe(false);
|
|
90
|
+
expect(action.tiltTriggerNames).toEqual(["data-governance-api"]);
|
|
91
|
+
expect(action.useSablierStart).toBe(true);
|
|
92
|
+
});
|
|
93
|
+
|
|
59
94
|
test("running dependency is not re-triggered", () => {
|
|
60
95
|
const action = decideWakeAction({
|
|
61
96
|
resourceContainerName: "data-governance-api",
|
|
@@ -66,6 +101,42 @@ describe("decideWakeAction", () => {
|
|
|
66
101
|
});
|
|
67
102
|
});
|
|
68
103
|
|
|
104
|
+
describe("wakeTimeoutMs", () => {
|
|
105
|
+
test("a direct compose start only waits for boot + healthcheck", () => {
|
|
106
|
+
expect(wakeTimeoutMs(true)).toBe(COMPOSE_START_TIMEOUT_MS);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test("the tilt trigger path allows for an image build, under Bun's 255s idle cap", () => {
|
|
110
|
+
expect(wakeTimeoutMs(false)).toBe(TILT_TRIGGER_TIMEOUT_MS);
|
|
111
|
+
expect(TILT_TRIGGER_TIMEOUT_MS).toBeLessThan(255_000);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test("an explicit X-Wake-Timeout-Ms wins", () => {
|
|
115
|
+
expect(wakeTimeoutMs(false, 5_000)).toBe(5_000);
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
describe("earlyResponseMs", () => {
|
|
120
|
+
test("holds a waking request for EARLY_RESPONSE_MS by default", () => {
|
|
121
|
+
expect(earlyResponseMs()).toBe(EARLY_RESPONSE_MS);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test("the default answers well before Bun's 255s idle cap drops the connection", () => {
|
|
125
|
+
expect(EARLY_RESPONSE_MS).toBeLessThan(255_000);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test("an explicit X-Wake-Respond-Within-Ms wins, but is capped under Bun's idle limit", () => {
|
|
129
|
+
expect(earlyResponseMs(5_000)).toBe(5_000);
|
|
130
|
+
expect(earlyResponseMs(600_000)).toBe(MAX_EARLY_RESPONSE_MS);
|
|
131
|
+
expect(MAX_EARLY_RESPONSE_MS).toBeLessThan(255_000);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
test("tells the caller to retry soon", () => {
|
|
135
|
+
expect(RETRY_AFTER_SECONDS).toBeGreaterThan(0);
|
|
136
|
+
expect(RETRY_AFTER_SECONDS * 1000).toBeLessThan(EARLY_RESPONSE_MS);
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
69
140
|
describe("evaluateReadiness", () => {
|
|
70
141
|
test("ready once every required name reports healthy", () => {
|
|
71
142
|
const outcome = evaluateReadiness(["a", "b"], { a: true, b: true });
|
|
@@ -20,11 +20,26 @@ export interface WakeRequestSnapshot {
|
|
|
20
20
|
resourceContainerName: string;
|
|
21
21
|
resourceState: ContainerState;
|
|
22
22
|
dependencies: DependencySnapshot[];
|
|
23
|
+
/**
|
|
24
|
+
* Whether the route carried enough compose detail (project, files, image)
|
|
25
|
+
* for the gateway to create the resource's container itself.
|
|
26
|
+
*/
|
|
27
|
+
hasComposeInvocation?: boolean;
|
|
23
28
|
}
|
|
24
29
|
|
|
25
30
|
export interface WakeAction {
|
|
26
31
|
/** Resources to `tilt trigger` (task 1.5: Tilt never cascades this on its own). */
|
|
27
32
|
tiltTriggerNames: string[];
|
|
33
|
+
/**
|
|
34
|
+
* Whether to create/start the resource's container directly with
|
|
35
|
+
* `docker compose up --no-build` instead of `tilt trigger`. `tilt trigger`
|
|
36
|
+
* only queues the resource: with Tilt's build slots busy (e.g. 100+
|
|
37
|
+
* resources rebuilding after a re-vendor), a triggered resource waited
|
|
38
|
+
* behind them and the request timed out (observed live against
|
|
39
|
+
* tdk-erp-system, see tasks.md 7.2). server.ts falls back to `tilt trigger`
|
|
40
|
+
* when no image exists yet to start from.
|
|
41
|
+
*/
|
|
42
|
+
useComposeStart: boolean;
|
|
28
43
|
/**
|
|
29
44
|
* Whether to also try Sablier's own Docker-level start for the resource.
|
|
30
45
|
* Only meaningful when the resource's container already exists (D2/D3):
|
|
@@ -39,8 +54,9 @@ export interface WakeAction {
|
|
|
39
54
|
*/
|
|
40
55
|
export function decideWakeAction(snapshot: WakeRequestSnapshot): WakeAction {
|
|
41
56
|
const tiltTriggerNames: string[] = [];
|
|
57
|
+
const useComposeStart = snapshot.resourceState === "absent" && snapshot.hasComposeInvocation === true;
|
|
42
58
|
|
|
43
|
-
if (snapshot.resourceState !== "running") {
|
|
59
|
+
if (snapshot.resourceState !== "running" && !useComposeStart) {
|
|
44
60
|
tiltTriggerNames.push(snapshot.resourceContainerName);
|
|
45
61
|
}
|
|
46
62
|
for (const dep of snapshot.dependencies) {
|
|
@@ -51,7 +67,40 @@ export function decideWakeAction(snapshot: WakeRequestSnapshot): WakeAction {
|
|
|
51
67
|
|
|
52
68
|
const useSablierStart = snapshot.resourceState === "exited" || snapshot.resourceState === "created";
|
|
53
69
|
|
|
54
|
-
return { tiltTriggerNames, useSablierStart };
|
|
70
|
+
return { tiltTriggerNames, useComposeStart, useSablierStart };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Wait budget when the gateway starts an existing image itself (boot + healthcheck only). */
|
|
74
|
+
export const COMPOSE_START_TIMEOUT_MS = 180_000;
|
|
75
|
+
/**
|
|
76
|
+
* Wait budget when the resource has to go through `tilt trigger` (image build
|
|
77
|
+
* included). Kept under Bun.serve's 255s idleTimeout cap in server.ts.
|
|
78
|
+
*/
|
|
79
|
+
export const TILT_TRIGGER_TIMEOUT_MS = 240_000;
|
|
80
|
+
|
|
81
|
+
/** The wait budget for a wake: an explicit X-Wake-Timeout-Ms wins, else by start path. */
|
|
82
|
+
export function wakeTimeoutMs(startedViaCompose: boolean, overrideMs?: number): number {
|
|
83
|
+
if (overrideMs && overrideMs > 0) return overrideMs;
|
|
84
|
+
return startedViaCompose ? COMPOSE_START_TIMEOUT_MS : TILT_TRIGGER_TIMEOUT_MS;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* How long a waking request is held before the gateway answers
|
|
89
|
+
* `503` + `Retry-After` while the start keeps going in the background.
|
|
90
|
+
* Found live (task 7.2): during a full `tdk up` bring-up the Docker daemon
|
|
91
|
+
* took ~5 min to start one container, and Bun.serve's 255s idle cap dropped
|
|
92
|
+
* the held connection with no response at all.
|
|
93
|
+
*/
|
|
94
|
+
export const EARLY_RESPONSE_MS = 60_000;
|
|
95
|
+
/** Largest hold allowed: stays under Bun.serve's 255s idle cap. */
|
|
96
|
+
export const MAX_EARLY_RESPONSE_MS = 240_000;
|
|
97
|
+
/** The `Retry-After` value sent with the early 503. */
|
|
98
|
+
export const RETRY_AFTER_SECONDS = 10;
|
|
99
|
+
|
|
100
|
+
/** How long to hold a request: an explicit X-Wake-Respond-Within-Ms wins, capped under Bun's idle limit. */
|
|
101
|
+
export function earlyResponseMs(overrideMs?: number): number {
|
|
102
|
+
if (overrideMs && overrideMs > 0) return Math.min(overrideMs, MAX_EARLY_RESPONSE_MS);
|
|
103
|
+
return EARLY_RESPONSE_MS;
|
|
55
104
|
}
|
|
56
105
|
|
|
57
106
|
export interface WaitOutcome {
|
|
@@ -10,11 +10,10 @@
|
|
|
10
10
|
* injects (set by the file-provider entry generated in
|
|
11
11
|
* traefik_static_routes.star) rather than the path, so the original request
|
|
12
12
|
* path/query reach the real backend unchanged once it's ready:
|
|
13
|
-
* X-Wake-Resource Tilt/Compose resource name (for `tilt trigger
|
|
14
|
-
*
|
|
15
|
-
* resolves a service by this name too).
|
|
13
|
+
* X-Wake-Resource Tilt/Compose resource name (for `tilt trigger` and
|
|
14
|
+
* `docker compose up <service>`).
|
|
16
15
|
* X-Wake-Container The actual Docker container name (for `docker
|
|
17
|
-
* inspect` state/health -- NOT the same string as
|
|
16
|
+
* inspect` state/health and the proxy target -- NOT the same string as
|
|
18
17
|
* the resource name; Compose derives it as
|
|
19
18
|
* <project>-<service>-<replica>, confirmed empirically
|
|
20
19
|
* against a live TDK container, e.g.
|
|
@@ -23,15 +22,33 @@
|
|
|
23
22
|
* X-Wake-Deps Comma-separated "resourceName=containerName" pairs
|
|
24
23
|
* for required dependencies (task 1.5: Tilt never
|
|
25
24
|
* cascades a trigger to these on its own).
|
|
25
|
+
* X-Wake-Compose-Project The resource's Compose project (e.g. "analytics-autogenerated").
|
|
26
|
+
* X-Wake-Compose-Files Comma-separated absolute compose file paths, the
|
|
27
|
+
* same ones Tilt's `<resource>-run-only` resource
|
|
28
|
+
* passes. The project root is mounted into this
|
|
29
|
+
* container at the same absolute path, so relative
|
|
30
|
+
* paths inside those files resolve exactly as for Tilt.
|
|
31
|
+
* X-Wake-Env-File Absolute path of the project's .env (optional).
|
|
32
|
+
* X-Wake-Image The image the compose service declares
|
|
33
|
+
* (<service>_<resource>:dev).
|
|
26
34
|
* X-Wake-Timeout-Ms, X-Wake-Proxy-Port, X-Wake-Session-Duration
|
|
35
|
+
* X-Wake-Respond-Within-Ms How long to hold the request before answering
|
|
36
|
+
* 503 + Retry-After while the start continues in the
|
|
37
|
+
* background (default decide.ts EARLY_RESPONSE_MS).
|
|
27
38
|
*/
|
|
28
|
-
import {
|
|
39
|
+
import {
|
|
40
|
+
RETRY_AFTER_SECONDS,
|
|
41
|
+
decideWakeAction,
|
|
42
|
+
earlyResponseMs,
|
|
43
|
+
evaluateReadiness,
|
|
44
|
+
wakeTimeoutMs,
|
|
45
|
+
type ContainerState,
|
|
46
|
+
} from "./decide";
|
|
29
47
|
|
|
30
48
|
const TILT_HOST = process.env.TILT_HOST || "host.docker.internal";
|
|
31
49
|
const TILT_PORT = process.env.TILT_PORT || "10350";
|
|
32
50
|
const SABLIER_URL = process.env.SABLIER_URL || "http://sablier:10000";
|
|
33
51
|
const POLL_INTERVAL_MS = 1000;
|
|
34
|
-
const DEFAULT_TIMEOUT_MS = 60_000;
|
|
35
52
|
|
|
36
53
|
async function run(cmd: string[]): Promise<{ code: number; stdout: string; stderr: string }> {
|
|
37
54
|
const proc = Bun.spawn(cmd, { stdout: "pipe", stderr: "pipe" });
|
|
@@ -66,7 +83,52 @@ async function isHealthy(name: string): Promise<boolean> {
|
|
|
66
83
|
}
|
|
67
84
|
|
|
68
85
|
async function tiltTrigger(name: string): Promise<void> {
|
|
69
|
-
await run(["tilt", "trigger", name, "--host", TILT_HOST, "--port", TILT_PORT]);
|
|
86
|
+
const { code, stderr } = await run(["tilt", "trigger", name, "--host", TILT_HOST, "--port", TILT_PORT]);
|
|
87
|
+
console.log(`tilt trigger ${name}: ${code === 0 ? "queued" : `failed (${stderr.trim()})`}`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
interface ComposeInvocation {
|
|
91
|
+
project: string;
|
|
92
|
+
files: string[];
|
|
93
|
+
envFile: string;
|
|
94
|
+
image: string;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Make sure the compose service's declared image exists locally. Tilt tags its
|
|
99
|
+
* own builds `<repo>:tilt-<hash>` and injects that tag when it runs compose, so
|
|
100
|
+
* the declared `<repo>:dev` tag may be missing even though the resource was
|
|
101
|
+
* built; retag Tilt's newest build in that case. False when nothing was ever
|
|
102
|
+
* built -- only `tilt trigger` (which builds) can wake the resource then.
|
|
103
|
+
*/
|
|
104
|
+
async function ensureImage(image: string): Promise<boolean> {
|
|
105
|
+
if ((await run(["docker", "image", "inspect", "--format", "{{.Id}}", image])).code === 0) return true;
|
|
106
|
+
const repo = image.split(":")[0];
|
|
107
|
+
// `docker images` lists newest first.
|
|
108
|
+
const { code, stdout } = await run(["docker", "images", repo, "--format", "{{.Tag}}"]);
|
|
109
|
+
if (code !== 0) return false;
|
|
110
|
+
const tiltTag = stdout.split("\n").map((t) => t.trim()).find((t) => t.startsWith("tilt-"));
|
|
111
|
+
if (!tiltTag) return false;
|
|
112
|
+
return (await run(["docker", "tag", `${repo}:${tiltTag}`, image])).code === 0;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Create and start the resource's container directly, with the same
|
|
117
|
+
* `docker compose ... up -d --no-build` its Tilt `-run-only` resource runs,
|
|
118
|
+
* bypassing Tilt's build queue (see decide.ts useComposeStart).
|
|
119
|
+
*/
|
|
120
|
+
async function composeStart(compose: ComposeInvocation, service: string): Promise<boolean> {
|
|
121
|
+
if (!(await ensureImage(compose.image))) {
|
|
122
|
+
console.log(`compose start ${service}: no local image for ${compose.image}, falling back to tilt trigger`);
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
const args = ["docker", "compose", "-p", compose.project];
|
|
126
|
+
for (const file of compose.files) args.push("-f", file);
|
|
127
|
+
if (compose.envFile) args.push("--env-file", compose.envFile);
|
|
128
|
+
args.push("up", "-d", "--no-build", "--no-deps", service);
|
|
129
|
+
const { code, stderr } = await run(args);
|
|
130
|
+
console.log(`compose start ${service}: ${code === 0 ? "started" : `failed (${stderr.trim()})`}`);
|
|
131
|
+
return code === 0;
|
|
70
132
|
}
|
|
71
133
|
|
|
72
134
|
/**
|
|
@@ -110,39 +172,111 @@ function parseRequest(req: Request) {
|
|
|
110
172
|
const resource = req.headers.get("X-Wake-Resource") || "";
|
|
111
173
|
const container = req.headers.get("X-Wake-Container") || resource;
|
|
112
174
|
const deps = parseDeps(req.headers.get("X-Wake-Deps") || "");
|
|
113
|
-
const
|
|
175
|
+
const timeoutOverrideMs = Number(req.headers.get("X-Wake-Timeout-Ms")) || undefined;
|
|
176
|
+
const respondWithinOverrideMs = Number(req.headers.get("X-Wake-Respond-Within-Ms")) || undefined;
|
|
114
177
|
const proxyPort = req.headers.get("X-Wake-Proxy-Port") || "80";
|
|
115
178
|
const sessionDuration = req.headers.get("X-Wake-Session-Duration") || "10m";
|
|
116
|
-
|
|
179
|
+
const composeFiles = (req.headers.get("X-Wake-Compose-Files") || "").split(",").filter(Boolean);
|
|
180
|
+
const compose: ComposeInvocation = {
|
|
181
|
+
project: req.headers.get("X-Wake-Compose-Project") || "",
|
|
182
|
+
files: composeFiles,
|
|
183
|
+
envFile: req.headers.get("X-Wake-Env-File") || "",
|
|
184
|
+
image: req.headers.get("X-Wake-Image") || "",
|
|
185
|
+
};
|
|
186
|
+
const hasCompose = Boolean(compose.project && compose.files.length && compose.image);
|
|
187
|
+
return {
|
|
188
|
+
resource,
|
|
189
|
+
container,
|
|
190
|
+
deps,
|
|
191
|
+
timeoutOverrideMs,
|
|
192
|
+
respondWithinOverrideMs,
|
|
193
|
+
proxyPort,
|
|
194
|
+
sessionDuration,
|
|
195
|
+
compose,
|
|
196
|
+
hasCompose,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
type WakeParams = ReturnType<typeof parseRequest>;
|
|
201
|
+
type WakeOutcome = { ready: true } | { ready: false; timeoutMs: number };
|
|
202
|
+
|
|
203
|
+
// One wake per resource at a time: a caller that got an early 503 and retries,
|
|
204
|
+
// or a second caller arriving meanwhile, joins the wake already in progress
|
|
205
|
+
// instead of starting the container again.
|
|
206
|
+
const inflight = new Map<string, Promise<WakeOutcome>>();
|
|
207
|
+
|
|
208
|
+
function wakeOnce(params: WakeParams): Promise<WakeOutcome> {
|
|
209
|
+
let wake = inflight.get(params.resource);
|
|
210
|
+
if (!wake) {
|
|
211
|
+
wake = runWake(params).finally(() => inflight.delete(params.resource));
|
|
212
|
+
inflight.set(params.resource, wake);
|
|
213
|
+
}
|
|
214
|
+
return wake;
|
|
117
215
|
}
|
|
118
216
|
|
|
119
217
|
async function handleWake(req: Request): Promise<Response> {
|
|
120
|
-
const
|
|
121
|
-
if (!resource) {
|
|
218
|
+
const params = parseRequest(req);
|
|
219
|
+
if (!params.resource) {
|
|
122
220
|
return new Response("wake gateway: missing X-Wake-Resource header (set by the generated Traefik static route)", { status: 400 });
|
|
123
221
|
}
|
|
124
222
|
|
|
223
|
+
const holdMs = earlyResponseMs(params.respondWithinOverrideMs);
|
|
224
|
+
const outcome = await Promise.race([wakeOnce(params), Bun.sleep(holdMs).then(() => null)]);
|
|
225
|
+
if (outcome === null) {
|
|
226
|
+
console.log(`wake ${params.resource}: still starting after ${holdMs}ms, answering 503`);
|
|
227
|
+
return new Response(
|
|
228
|
+
`wake gateway: ${params.resource} is starting; retry in ${RETRY_AFTER_SECONDS}s`,
|
|
229
|
+
{ status: 503, headers: { "Retry-After": String(RETRY_AFTER_SECONDS) } },
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
if (outcome.ready) {
|
|
233
|
+
return proxyThrough(req, params.container, params.proxyPort);
|
|
234
|
+
}
|
|
235
|
+
return new Response(
|
|
236
|
+
`wake gateway: timed out after ${outcome.timeoutMs}ms waiting for ${params.resource} and its dependencies to become healthy`,
|
|
237
|
+
{ status: 504 },
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
async function runWake(params: WakeParams): Promise<WakeOutcome> {
|
|
242
|
+
const { resource, container, deps, timeoutOverrideMs, sessionDuration, compose, hasCompose } = params;
|
|
125
243
|
const resourceState = await containerState(container);
|
|
126
244
|
const dependencies = await Promise.all(
|
|
127
245
|
deps.map(async (dep) => ({ name: dep.resourceName, state: await containerState(dep.containerName) })),
|
|
128
246
|
);
|
|
129
247
|
|
|
130
|
-
const action = decideWakeAction({
|
|
248
|
+
const action = decideWakeAction({
|
|
249
|
+
resourceContainerName: resource,
|
|
250
|
+
resourceState,
|
|
251
|
+
dependencies,
|
|
252
|
+
hasComposeInvocation: hasCompose,
|
|
253
|
+
});
|
|
254
|
+
console.log(
|
|
255
|
+
`wake ${resource}: state=${resourceState} compose=${action.useComposeStart} ` +
|
|
256
|
+
`trigger=[${action.tiltTriggerNames.join(",")}] sablier=${action.useSablierStart}`,
|
|
257
|
+
);
|
|
258
|
+
|
|
259
|
+
// Dependencies (and the resource itself when it can't be started directly)
|
|
260
|
+
// go through tilt trigger (task 1.5: Tilt never cascades this on its own).
|
|
261
|
+
const tiltNames = [...action.tiltTriggerNames];
|
|
262
|
+
let startedViaCompose = false;
|
|
263
|
+
if (action.useComposeStart) {
|
|
264
|
+
startedViaCompose = await composeStart(compose, resource);
|
|
265
|
+
if (!startedViaCompose) tiltNames.unshift(resource);
|
|
266
|
+
}
|
|
267
|
+
const timeoutMs = wakeTimeoutMs(startedViaCompose, timeoutOverrideMs);
|
|
131
268
|
const deadline = Date.now() + timeoutMs;
|
|
132
269
|
|
|
133
|
-
//
|
|
134
|
-
//
|
|
135
|
-
// waits for the resource itself in one round trip.
|
|
270
|
+
// Sablier's blocking call, when applicable, both starts AND waits for the
|
|
271
|
+
// resource itself in one round trip.
|
|
136
272
|
const [, resourceReadyViaSablier] = await Promise.all([
|
|
137
|
-
Promise.all(
|
|
273
|
+
Promise.all(tiltNames.map(tiltTrigger)),
|
|
138
274
|
action.useSablierStart
|
|
139
275
|
? sablierBlockingWait(resource, sessionDuration, timeoutMs)
|
|
140
276
|
: Promise.resolve(false),
|
|
141
277
|
]);
|
|
142
278
|
|
|
143
279
|
const requiredNames = [resource, ...deps.map((d) => d.resourceName)];
|
|
144
|
-
const containerByName: Record<string, string> = { [resource]: container };
|
|
145
|
-
for (const dep of deps) containerByName[dep.resourceName] = dep.containerName;
|
|
146
280
|
|
|
147
281
|
while (Date.now() < deadline) {
|
|
148
282
|
const healthy: Record<string, boolean> = {};
|
|
@@ -152,17 +286,12 @@ async function handleWake(req: Request): Promise<Response> {
|
|
|
152
286
|
for (const dep of deps) {
|
|
153
287
|
healthy[dep.resourceName] = await isHealthy(dep.containerName);
|
|
154
288
|
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
return proxyThrough(req, resource, proxyPort);
|
|
289
|
+
if (evaluateReadiness(requiredNames, healthy).ready) {
|
|
290
|
+
return { ready: true };
|
|
158
291
|
}
|
|
159
292
|
await Bun.sleep(POLL_INTERVAL_MS);
|
|
160
293
|
}
|
|
161
|
-
|
|
162
|
-
return new Response(
|
|
163
|
-
`wake gateway: timed out after ${timeoutMs}ms waiting for ${resource} and its dependencies to become healthy`,
|
|
164
|
-
{ status: 504 },
|
|
165
|
-
);
|
|
294
|
+
return { ready: false, timeoutMs };
|
|
166
295
|
}
|
|
167
296
|
|
|
168
297
|
// Headers that must not be forwarded verbatim to the upstream fetch: Host
|
|
@@ -174,11 +303,19 @@ const STRIPPED_REQUEST_HEADERS = new Set([
|
|
|
174
303
|
"x-wake-container",
|
|
175
304
|
"x-wake-deps",
|
|
176
305
|
"x-wake-timeout-ms",
|
|
306
|
+
"x-wake-respond-within-ms",
|
|
177
307
|
"x-wake-proxy-port",
|
|
178
308
|
"x-wake-session-duration",
|
|
309
|
+
"x-wake-compose-project",
|
|
310
|
+
"x-wake-compose-files",
|
|
311
|
+
"x-wake-env-file",
|
|
312
|
+
"x-wake-image",
|
|
179
313
|
]);
|
|
180
314
|
|
|
181
|
-
|
|
315
|
+
// Proxies to the container name rather than the Compose service name: every
|
|
316
|
+
// TDK project's resources share the traefik-public network, where a bare
|
|
317
|
+
// service name is only an alias and could collide across projects.
|
|
318
|
+
async function proxyThrough(req: Request, container: string, port: string): Promise<Response> {
|
|
182
319
|
// The original path/query reach the gateway untouched (config travels via
|
|
183
320
|
// X-Wake-* headers, not the path), so they're forwarded as-is.
|
|
184
321
|
const target = new URL(req.url);
|
|
@@ -186,7 +323,7 @@ async function proxyThrough(req: Request, resource: string, port: string): Promi
|
|
|
186
323
|
// Set hostname/port separately, not via `.host` -- confirmed empirically
|
|
187
324
|
// that `target.host = "name:port"` silently keeps the URL's original port
|
|
188
325
|
// when one is already present instead of replacing it.
|
|
189
|
-
target.hostname =
|
|
326
|
+
target.hostname = container;
|
|
190
327
|
target.port = port;
|
|
191
328
|
|
|
192
329
|
const headers = new Headers();
|
|
@@ -84,7 +84,7 @@ while IFS= read -r container; do
|
|
|
84
84
|
|
|
85
85
|
printf "%-20s\t%s%%\t%s\t%s\n" "$CONTAINER_DISPLAY" "$CPU_PERCENT" "$MEMORY_USAGE" "$STATUS"
|
|
86
86
|
fi
|
|
87
|
-
done <<< "$
|
|
87
|
+
done <<< "$PROJECT_CONTAINERS"
|
|
88
88
|
|
|
89
89
|
echo ""
|
|
90
90
|
echo -e "${BLUE}📈 Resource Summary:${NC}"
|
|
@@ -13,6 +13,11 @@ YELLOW='\033[1;33m'
|
|
|
13
13
|
BLUE='\033[0;34m'
|
|
14
14
|
NC='\033[0m' # No Color
|
|
15
15
|
|
|
16
|
+
# Container name filter: project name from .tdk/project.json (override with TDK_PROJECT_NAME)
|
|
17
|
+
PROJECT_FILTER="${TDK_PROJECT_NAME:-$(sed -n 's/.*"name"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' .tdk/project.json 2>/dev/null | head -1)}"
|
|
18
|
+
PROJECT_FILTER="${PROJECT_FILTER:-tdk-project}"
|
|
19
|
+
PROJECT_FILTER="${PROJECT_FILTER//-/[-_]}"
|
|
20
|
+
|
|
16
21
|
# Help text
|
|
17
22
|
show_help() {
|
|
18
23
|
cat << EOF
|
|
@@ -68,11 +73,11 @@ EOF
|
|
|
68
73
|
# List containers
|
|
69
74
|
list_containers() {
|
|
70
75
|
echo -e "${GREEN}=== Running Containers ===${NC}"
|
|
71
|
-
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Image}}" | grep -
|
|
76
|
+
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Image}}" | grep -iE "$PROJECT_FILTER"
|
|
72
77
|
|
|
73
78
|
echo ""
|
|
74
79
|
echo -e "${YELLOW}=== Recently Exited Containers ===${NC}"
|
|
75
|
-
docker ps -a --format "table {{.Names}}\t{{.Status}}\t{{.Image}}" | grep -
|
|
80
|
+
docker ps -a --format "table {{.Names}}\t{{.Status}}\t{{.Image}}" | grep -iE "$PROJECT_FILTER" | grep -i "exited"
|
|
76
81
|
}
|
|
77
82
|
|
|
78
83
|
# Get container ID from service name
|
|
@@ -122,7 +127,7 @@ run_command() {
|
|
|
122
127
|
# Show failed containers
|
|
123
128
|
show_failed() {
|
|
124
129
|
echo -e "${RED}=== Failed/Exited Containers ===${NC}"
|
|
125
|
-
docker ps -a --format "table {{.Names}}\t{{.Status}}\t{{.Image}}" | grep -
|
|
130
|
+
docker ps -a --format "table {{.Names}}\t{{.Status}}\t{{.Image}}" | grep -iE "$PROJECT_FILTER" | grep -iE "(exited|error)"
|
|
126
131
|
|
|
127
132
|
echo ""
|
|
128
133
|
echo -e "${YELLOW}To inspect logs of a failed container:${NC}"
|
package/engine/spec.master
CHANGED
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
|
|
63
63
|
# PROJECT_ROOT is automatically set by Tiltfile based on where you run `tilt up`.
|
|
64
64
|
# You don't need to set it. Example:
|
|
65
|
-
# - If you run in /home/dev/
|
|
65
|
+
# - If you run in /home/dev/tdk-project, PROJECT_ROOT = "/home/dev/tdk-project"
|
|
66
66
|
# - All other paths are built from this root.
|
|
67
67
|
#
|
|
68
68
|
# This file is manually maintained to match the directory structure.
|
|
@@ -563,7 +563,7 @@ FEATURES = struct(
|
|
|
563
563
|
|
|
564
564
|
CONSTANTS = struct(
|
|
565
565
|
# System identity - EXPLICIT
|
|
566
|
-
SYSTEM_NAME = "
|
|
566
|
+
SYSTEM_NAME = "tdk-project-tilt-platform",
|
|
567
567
|
SYSTEM_VERSION = "2.0.0",
|
|
568
568
|
|
|
569
569
|
# Runtimes - EXPLICIT supported options
|