@tdk-landscape/tdk-cli-core 1.3.98 → 1.3.99
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 +2 -0
- package/cli/dist/utils/service-manifest.js +10 -0
- package/cli/package.json +1 -1
- package/engine/topologies/platform/docker/dockerfile/language_dockerfile.star +33 -2
- package/engine/topologies/platform/docker/index.star +2 -0
- package/engine/topologies/tilt/resources/orchestrator/apply_compose_resource_registration.star +17 -2
- package/engine/topologies/tilt/resources/orchestrator/builders/typescript.star +3 -2
- package/engine/topologies/tilt/resources/orchestrator/generators/manifest_resource.star +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# TDK CLI — start services on your laptop
|
|
2
2
|
|
|
3
|
+
English | [简体中文](README-zh_cn.md) | [繁體中文](README-zh_tw.md) | [日本語](README-ja.md) | [한국어](README-ko.md)
|
|
4
|
+
|
|
3
5
|
TDK CLI starts your services on your laptop. It is not a deploy and not a Compose file: define each service in `service.json`, then run `tdk up`. No Kubernetes is needed on the machine.
|
|
4
6
|
|
|
5
7
|
Stability: 1.x local dev. Generated files are a contract; verify with `tdk config verify`. Core CLI is MIT and needs no key. Premium is optional.
|
|
@@ -66,6 +66,13 @@ export function validateServiceManifest(value, displayPath) {
|
|
|
66
66
|
if ("schemaVersion" in manifest && manifest.schemaVersion !== SERVICE_MANIFEST_SCHEMA_VERSION) {
|
|
67
67
|
errors.push(`${displayPath}.schemaVersion: unsupported version ${String(manifest.schemaVersion)} (supported: ${SERVICE_MANIFEST_SCHEMA_VERSION})`);
|
|
68
68
|
}
|
|
69
|
+
const dev = manifest.dev;
|
|
70
|
+
const liveReload = dev && typeof dev === "object" && !Array.isArray(dev)
|
|
71
|
+
? dev.liveReload
|
|
72
|
+
: undefined;
|
|
73
|
+
if (liveReload !== undefined && typeof liveReload !== "boolean") {
|
|
74
|
+
errors.push(`${displayPath}.dev.liveReload: expected true or false`);
|
|
75
|
+
}
|
|
69
76
|
if ("smoke" in manifest) {
|
|
70
77
|
for (const message of validateSmoke(manifest.smoke))
|
|
71
78
|
errors.push(`${displayPath}.${message}`);
|
|
@@ -78,6 +85,9 @@ export function validateServiceManifest(value, displayPath) {
|
|
|
78
85
|
return [`${displayPath}.${field}: unknown field is preserved`];
|
|
79
86
|
return [];
|
|
80
87
|
});
|
|
88
|
+
if (liveReload === true && manifest.language !== "go") {
|
|
89
|
+
warnings.push(`${displayPath}.dev.liveReload: only applies to Go services (language "go"); it is ignored here`);
|
|
90
|
+
}
|
|
81
91
|
return { errors, warnings, manifest };
|
|
82
92
|
}
|
|
83
93
|
export function validateServiceManifestFile(filePath, displayPath) {
|
package/cli/package.json
CHANGED
|
@@ -62,7 +62,7 @@ RUN CGO_ENABLED=0 go build -o /out/app .
|
|
|
62
62
|
|
|
63
63
|
FROM build AS test
|
|
64
64
|
RUN go test ./...
|
|
65
|
-
|
|
65
|
+
{go_development}
|
|
66
66
|
FROM debian:bookworm-slim AS production
|
|
67
67
|
|
|
68
68
|
{curl}
|
|
@@ -102,14 +102,35 @@ EXPOSE {port}
|
|
|
102
102
|
CMD ["app"]
|
|
103
103
|
"""
|
|
104
104
|
|
|
105
|
-
|
|
105
|
+
# Opt-in Go development target (`dev.liveReload`): the toolchain image with Air, which rebuilds and restarts the app when a `.go`
|
|
106
|
+
# file changes. Dependencies are downloaded in their own layer and the module and build caches live in BuildKit cache mounts, so an
|
|
107
|
+
# ordinary source edit recompiles incrementally. `go.mod`, `go.sum` and the Dockerfile still rebuild the image.
|
|
108
|
+
_GO_DEVELOPMENT = """
|
|
109
|
+
FROM golang:1.23-bookworm AS development
|
|
110
|
+
RUN go install github.com/air-verse/air@v1.61.7
|
|
111
|
+
WORKDIR /app/{res_path}
|
|
112
|
+
COPY {res_path}/go.* ./
|
|
113
|
+
RUN --mount=type=cache,target=/go/pkg/mod go mod download
|
|
114
|
+
COPY {res_path}/ ./
|
|
115
|
+
ENV GOFLAGS=-buildvcs=false
|
|
116
|
+
# Prime the build cache inside the image (a cache mount would not be kept), so the first edit recompiles only what changed.
|
|
117
|
+
RUN go build -o /tmp/app .
|
|
118
|
+
EXPOSE {port}
|
|
119
|
+
CMD ["sh", "-c", "exec air --build.cmd 'go build -o /tmp/app .' --build.bin /tmp/app --build.include_ext go"]
|
|
120
|
+
"""
|
|
121
|
+
|
|
122
|
+
def generate_language_dockerfile(res_path, language, port = BASE_PORT_BACKEND, dev = False):
|
|
106
123
|
"""Dockerfile text for a provider-owned backend language, or fail for an unknown one."""
|
|
107
124
|
template = {"python": _PYTHON, "go": _GO, "rust": _RUST}.get(language)
|
|
108
125
|
if template == None:
|
|
109
126
|
fail("No Dockerfile generator for backend language '{}'".format(language))
|
|
127
|
+
go_development = ""
|
|
128
|
+
if dev and language == "go":
|
|
129
|
+
go_development = _GO_DEVELOPMENT.format(res_path = res_path, port = port)
|
|
110
130
|
return template.format(
|
|
111
131
|
res_path = res_path,
|
|
112
132
|
port = port,
|
|
133
|
+
go_development = go_development,
|
|
113
134
|
curl = _CURL,
|
|
114
135
|
healthcheck = _HEALTHCHECK.format(port = port),
|
|
115
136
|
)
|
|
@@ -120,3 +141,13 @@ def is_provider_owned_language(manifest):
|
|
|
120
141
|
if not manifest:
|
|
121
142
|
return False
|
|
122
143
|
return manifest.get("appType", "backend") == "backend" and manifest.get("language", "bun") in PROVIDER_OWNED_LANGUAGES
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def go_live_reload_enabled(manifest):
|
|
147
|
+
"""True for a Go service that opted in with `dev.liveReload: true` (see _GO_DEVELOPMENT); anything else stays off."""
|
|
148
|
+
if not manifest or manifest.get("language", "bun") != "go":
|
|
149
|
+
return False
|
|
150
|
+
dev = manifest.get("dev")
|
|
151
|
+
if type(dev) != "dict":
|
|
152
|
+
return False
|
|
153
|
+
return dev.get("liveReload") == True
|
|
@@ -8,6 +8,7 @@ load(
|
|
|
8
8
|
"./dockerfile/language_dockerfile.star",
|
|
9
9
|
"generate_language_dockerfile",
|
|
10
10
|
"is_provider_owned_language",
|
|
11
|
+
"go_live_reload_enabled",
|
|
11
12
|
"LANGUAGE_REQUIRED_FILES",
|
|
12
13
|
)
|
|
13
14
|
load("./config/dockerignore.star", "generate_dockerignore")
|
|
@@ -26,6 +27,7 @@ Docker = struct(
|
|
|
26
27
|
migrator = generate_migrator_dockerfile,
|
|
27
28
|
backend_language = generate_language_dockerfile,
|
|
28
29
|
is_provider_owned_language = is_provider_owned_language,
|
|
30
|
+
go_live_reload = go_live_reload_enabled,
|
|
29
31
|
language_required_files = LANGUAGE_REQUIRED_FILES,
|
|
30
32
|
generate_dockerignore = generate_dockerignore,
|
|
31
33
|
frontend_compose = generate_frontend_compose,
|
package/engine/topologies/tilt/resources/orchestrator/apply_compose_resource_registration.star
CHANGED
|
@@ -215,7 +215,7 @@ def _live_update_sync_source(project_root, full_res_path, sync_path):
|
|
|
215
215
|
return relative_path
|
|
216
216
|
|
|
217
217
|
|
|
218
|
-
def _build_live_update_rules(res_path, full_res_path, syncs, project_root='', language='bun'):
|
|
218
|
+
def _build_live_update_rules(res_path, full_res_path, syncs, project_root='', language='bun', go_live_reload=False):
|
|
219
219
|
"""Build live update sync and run rules for a resource.
|
|
220
220
|
|
|
221
221
|
Python backends reload their own process after a sync (uvicorn --reload), so they have
|
|
@@ -225,9 +225,22 @@ def _build_live_update_rules(res_path, full_res_path, syncs, project_root='', la
|
|
|
225
225
|
# Go and Rust are compiled into the image and have no reload process, so syncing sources would leave the
|
|
226
226
|
# old binary running. Return before any sync() step is built (Tilt rejects sync steps that are created
|
|
227
227
|
# but never used): no live-update rules means Tilt rebuilds the image when a watched file changes.
|
|
228
|
-
if language
|
|
228
|
+
if language == 'rust' or (language == 'go' and not go_live_reload):
|
|
229
229
|
return []
|
|
230
230
|
|
|
231
|
+
if go_live_reload:
|
|
232
|
+
# Opted-in Go development loop: sync the sources into the development image, where Air rebuilds and restarts.
|
|
233
|
+
# Dependency and Dockerfile changes cannot be picked up by a restart, so they fall back to a full image rebuild.
|
|
234
|
+
source_dir = _live_update_sync_source(project_root, full_res_path, '')
|
|
235
|
+
return [
|
|
236
|
+
fall_back_on([
|
|
237
|
+
source_dir + 'go.mod',
|
|
238
|
+
source_dir + 'go.sum',
|
|
239
|
+
source_dir + OUTPUT.AUTOGENERATED_FOLDER,
|
|
240
|
+
]),
|
|
241
|
+
sync(source_dir.rstrip('/'), '/app/' + full_res_path),
|
|
242
|
+
]
|
|
243
|
+
|
|
231
244
|
live_update_rules = []
|
|
232
245
|
for sync_path in syncs:
|
|
233
246
|
# Python images never read the npm/Bun registry config, and the engine does not
|
|
@@ -285,6 +298,7 @@ def _register_docker_build(config, live_update_rules):
|
|
|
285
298
|
_resolve_dockerfile(config),
|
|
286
299
|
live_update_rules,
|
|
287
300
|
config['res_deps'],
|
|
301
|
+
target = 'development' if Docker.go_live_reload(config.get('manifest', {})) else 'production',
|
|
288
302
|
)
|
|
289
303
|
|
|
290
304
|
|
|
@@ -408,6 +422,7 @@ def _register_single_resource(config, resource_path, compose_project_name, auto_
|
|
|
408
422
|
config['syncs'],
|
|
409
423
|
project_root=config.get('project_root', ''),
|
|
410
424
|
language=config.get('manifest', {}).get('language', 'bun'),
|
|
425
|
+
go_live_reload=Docker.go_live_reload(config.get('manifest', {})),
|
|
411
426
|
)
|
|
412
427
|
_register_docker_build(config, live_update_rules)
|
|
413
428
|
if defers_start:
|
|
@@ -105,7 +105,7 @@ def _get_resource_only_paths(resource_path):
|
|
|
105
105
|
|
|
106
106
|
return only_paths
|
|
107
107
|
|
|
108
|
-
def _build_typescript_service(name, context, dockerfile, live_update_rules, deps, env=None):
|
|
108
|
+
def _build_typescript_service(name, context, dockerfile, live_update_rules, deps, env=None, target='production'):
|
|
109
109
|
"""Build a TypeScript service with Docker.
|
|
110
110
|
|
|
111
111
|
Args:
|
|
@@ -115,6 +115,7 @@ def _build_typescript_service(name, context, dockerfile, live_update_rules, deps
|
|
|
115
115
|
live_update_rules: Live update configuration
|
|
116
116
|
deps: Resource dependencies
|
|
117
117
|
env: Dict of environment variables to pass to container (optional)
|
|
118
|
+
target: Dockerfile target to build (`development` for an opted-in Go service, otherwise `production`)
|
|
118
119
|
"""
|
|
119
120
|
# Extract service path from dockerfile path (e.g., 'services/product/{domain}/{service-name}')
|
|
120
121
|
# The dockerfile parameter looks like: 'services/product/{domain}/{service-name}/autogenerated/Dockerfile.autogenerated'
|
|
@@ -154,7 +155,7 @@ def _build_typescript_service(name, context, dockerfile, live_update_rules, deps
|
|
|
154
155
|
name,
|
|
155
156
|
context,
|
|
156
157
|
dockerfile=dockerfile,
|
|
157
|
-
target=
|
|
158
|
+
target=target,
|
|
158
159
|
live_update=live_update_rules,
|
|
159
160
|
only=_get_resource_only_paths(resource_path),
|
|
160
161
|
ignore=ignore_patterns,
|