@tdk-landscape/tdk-cli-core 1.3.157 → 1.3.159
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/cli/dist/commands/doctor.js +13 -5
- package/cli/dist/commands/down.js +1 -1
- package/cli/dist/commands/up.js +2 -15
- package/cli/dist/utils/doctor-fix.js +168 -9
- package/cli/dist/utils/down-cleanup.js +26 -0
- package/cli/dist/utils/tilt-port-owner.js +14 -0
- package/cli/dist/utils/tilt.js +3 -3
- package/cli/dist-bundle/chunk-NYJ4DFC2.js +377 -0
- package/cli/dist-bundle/cli.js +206 -206
- package/cli/dist-bundle/{cold-preflight-OQUN7OFG.js → cold-preflight-V77OAM2K.js} +1 -1
- package/cli/package.json +1 -1
- package/engine/topologies/tilt/resources/orchestrator/apply.star +3 -4
- package/engine/topologies/tilt/resources/orchestrator/apply_compose_resource_registration.star +12 -2
- package/package.json +1 -1
- package/cli/dist-bundle/chunk-O2SVXNG5.js +0 -377
|
@@ -1017,6 +1017,7 @@ export const doctorCommand = new Command("doctor")
|
|
|
1017
1017
|
.option("--strict", "Fail warnings such as a WSL project under /mnt/c")
|
|
1018
1018
|
.option("--fix", "Repair this project's unused Docker networks and leftover doctor probes", false)
|
|
1019
1019
|
.option("--dry-run", "With --fix: list the repairs without changing anything", false)
|
|
1020
|
+
.option("--scope <scope>", "With --fix: project (default) or machine, which also removes unused networks of other TDK projects", "project")
|
|
1020
1021
|
.option("-y, --yes", "With --fix: apply every repair without asking", false)
|
|
1021
1022
|
.option("--ping-timeout <ms>", "Per-service ping timeout in milliseconds", String(DEFAULT_PING_TIMEOUT_MS))
|
|
1022
1023
|
.action(async (options) => {
|
|
@@ -1032,8 +1033,14 @@ export const doctorCommand = new Command("doctor")
|
|
|
1032
1033
|
process.exit(2);
|
|
1033
1034
|
return;
|
|
1034
1035
|
}
|
|
1035
|
-
|
|
1036
|
-
|
|
1036
|
+
const scopeInvalid = !["project", "machine"].includes(options.scope);
|
|
1037
|
+
const scopeWithoutFix = options.scope !== "project" && !options.fix;
|
|
1038
|
+
if (scopeInvalid || scopeWithoutFix || ((options.dryRun || options.yes) && !options.fix)) {
|
|
1039
|
+
const message = scopeInvalid
|
|
1040
|
+
? `--scope must be project or machine, got "${options.scope}"`
|
|
1041
|
+
: scopeWithoutFix
|
|
1042
|
+
? "--scope only applies with --fix"
|
|
1043
|
+
: `${options.dryRun ? "--dry-run" : "--yes"} only applies with --fix`;
|
|
1037
1044
|
if (options.json)
|
|
1038
1045
|
console.log(JSON.stringify(createDoctorReport([], false, [{ code: "USAGE", message }])));
|
|
1039
1046
|
console.error(message);
|
|
@@ -1100,9 +1107,10 @@ export const doctorCommand = new Command("doctor")
|
|
|
1100
1107
|
}
|
|
1101
1108
|
}
|
|
1102
1109
|
const tiltPids = projectRoot ? projectTiltPids(projectRoot) : [];
|
|
1103
|
-
const planned = await planDoctorFixes(projectName, undefined,
|
|
1104
|
-
|
|
1105
|
-
:
|
|
1110
|
+
const planned = await planDoctorFixes(projectName, undefined, {
|
|
1111
|
+
tiltPids,
|
|
1112
|
+
machine: options.scope === "machine",
|
|
1113
|
+
});
|
|
1106
1114
|
fixActions = await applyDoctorFixes(planned, {
|
|
1107
1115
|
dryRun: options.dryRun,
|
|
1108
1116
|
confirm: options.yes || (!options.json && process.stdin.isTTY)
|
|
@@ -46,7 +46,7 @@ function pruneNetworksOrWarn(json) {
|
|
|
46
46
|
export const downCommand = new Command("down")
|
|
47
47
|
.description("Stop all tilt resources")
|
|
48
48
|
.option("-v, --verbose", "Enable verbose output", false)
|
|
49
|
-
.option("-f, --force", "Skip confirmation", false)
|
|
49
|
+
.option("-f, --force", "Skip confirmation (no-op; accepted for script compatibility)", false)
|
|
50
50
|
.option("--prune-networks", "Also remove this project's Docker networks (only those no container is attached to)", false)
|
|
51
51
|
.option("--dry-run", "Show what would be stopped without stopping", false)
|
|
52
52
|
.option("--json", "Print one JSON object on stdout; implies quiet human output", false)
|
package/cli/dist/commands/up.js
CHANGED
|
@@ -29,7 +29,7 @@ import { buildSmokePlans, formatSmokeFailure, runSmokePlans } from "../utils/smo
|
|
|
29
29
|
import { buildStartupReport, formatStartupReport, isStartupStalled, } from "../utils/startup-report.js";
|
|
30
30
|
import { evaluateTdkVersionFloor } from "../utils/tdk-version.js";
|
|
31
31
|
import { buildTiltUpArgs, runTilt } from "../utils/tilt.js";
|
|
32
|
-
import {
|
|
32
|
+
import { classifyTiltPorts } from "../utils/tilt-port-owner.js";
|
|
33
33
|
import { stopTiltOnPort } from "../utils/tilt-process.js";
|
|
34
34
|
import { parseTiltPort, resolveTiltPort, secondUpAction, stopTiltForUp, } from "../utils/tilt-startup.js";
|
|
35
35
|
import { findUnknownServices, resolveOnlySelection } from "../utils/up-only.js";
|
|
@@ -369,20 +369,7 @@ export const upCommand = new Command("up")
|
|
|
369
369
|
const basePort = STANDARD_PORTS.tiltUi;
|
|
370
370
|
const watchedPort = configuredTiltPort ?? basePort;
|
|
371
371
|
const candidatePorts = options.only ? [...new Set([basePort, watchedPort])] : [watchedPort];
|
|
372
|
-
const running =
|
|
373
|
-
const foreign = [];
|
|
374
|
-
for (const candidate of candidatePorts) {
|
|
375
|
-
if ((await tiltGetUiResources(candidate)) === null)
|
|
376
|
-
continue;
|
|
377
|
-
// A Tilt on this port may belong to another project; that is not "this environment is running".
|
|
378
|
-
const owner = findTiltPortOwner(candidate);
|
|
379
|
-
if (owner && isForeignTilt(owner, projectRoot)) {
|
|
380
|
-
foreign.push({ port: candidate, root: owner.root });
|
|
381
|
-
}
|
|
382
|
-
else {
|
|
383
|
-
running.push(candidate);
|
|
384
|
-
}
|
|
385
|
-
}
|
|
372
|
+
const { running, foreign } = await classifyTiltPorts(candidatePorts, projectRoot, async (candidate) => (await tiltGetUiResources(candidate)) !== null);
|
|
386
373
|
if (foreign.length > 0 && configuredTiltPort !== undefined) {
|
|
387
374
|
const other = foreign[0];
|
|
388
375
|
const message = `TILT_PORT=${other?.port} is used by another project's Tilt (${other?.root}). Choose a free port, or run tdk down in that project.`;
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
// Copyright (c) 2026 TDK Landscape contributors
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
import { execFile } from "node:child_process";
|
|
4
|
-
import { realpathSync } from "node:fs";
|
|
4
|
+
import { realpathSync, statfsSync } from "node:fs";
|
|
5
|
+
import { homedir } from "node:os";
|
|
5
6
|
import { join } from "node:path";
|
|
6
7
|
import { promisify } from "node:util";
|
|
7
|
-
import {
|
|
8
|
+
import { DISK_WARN_GIB } from "./doctor-host.js";
|
|
9
|
+
import { findProjectTiltUpPids, projectNetworkNames, tdkNetworkOwner, tdkNetworkPrefixes, } from "./down-cleanup.js";
|
|
10
|
+
import { tiltGetUiResources } from "./up-readiness.js";
|
|
8
11
|
import { findAllOnPath } from "./which.js";
|
|
9
12
|
const execFilePromise = promisify(execFile);
|
|
10
13
|
const DOCKER_TIMEOUT_MS = 15_000;
|
|
@@ -22,6 +25,51 @@ function firstLine(error) {
|
|
|
22
25
|
const stderr = String(error.stderr ?? error).trim();
|
|
23
26
|
return stderr.split(/\r?\n/)[0] ?? "";
|
|
24
27
|
}
|
|
28
|
+
const STALE_TILT_MIN_AGE_S = 120;
|
|
29
|
+
/** `ps -o etime=` ("[[dd-]hh:]mm:ss") in seconds. */
|
|
30
|
+
export function parseElapsed(etime) {
|
|
31
|
+
const match = /^\s*(?:(?:(\d+)-)?(\d+):)?(\d+):(\d+)\s*$/.exec(etime);
|
|
32
|
+
if (!match)
|
|
33
|
+
return null;
|
|
34
|
+
const [, days = "0", hours = "0", minutes = "0", seconds = "0"] = match;
|
|
35
|
+
return ((Number(days) * 24 + Number(hours)) * 60 + Number(minutes)) * 60 + Number(seconds);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Whether any of `ports` answers as a Tilt UI. A busy Tilt can miss one request, and a wrong "stale" ends in a kill,
|
|
39
|
+
* so a port gets a second try before it counts as silent.
|
|
40
|
+
*/
|
|
41
|
+
export async function anyTiltAnswers(ports, probe = tiltGetUiResources, attempts = 2, wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms))) {
|
|
42
|
+
for (let attempt = 0; attempt < attempts; attempt++) {
|
|
43
|
+
if (attempt > 0)
|
|
44
|
+
await wait(1000);
|
|
45
|
+
for (const port of ports) {
|
|
46
|
+
if ((await probe(port)) !== null)
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
async function tiltProcessStatus(pid) {
|
|
53
|
+
if (process.platform === "win32")
|
|
54
|
+
return "unknown";
|
|
55
|
+
try {
|
|
56
|
+
const { stdout: etime } = await execFilePromise("ps", ["-p", String(pid), "-o", "etime="], {
|
|
57
|
+
encoding: "utf-8",
|
|
58
|
+
timeout: 5000,
|
|
59
|
+
});
|
|
60
|
+
const age = parseElapsed(etime);
|
|
61
|
+
// A Tilt that is still starting has not bound its port yet.
|
|
62
|
+
if (age === null || age < STALE_TILT_MIN_AGE_S)
|
|
63
|
+
return "unknown";
|
|
64
|
+
const { stdout } = await execFilePromise("lsof", ["-nP", "-a", "-p", String(pid), "-iTCP", "-sTCP:LISTEN", "-Fn"], { encoding: "utf-8", timeout: 5000 });
|
|
65
|
+
const ports = [...stdout.matchAll(/^n.*:(\d+)$/gm)].map((m) => Number(m[1]));
|
|
66
|
+
return (await anyTiltAnswers(ports)) ? "healthy" : "stale";
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
// No lsof, or the process is gone: do not guess.
|
|
70
|
+
return "unknown";
|
|
71
|
+
}
|
|
72
|
+
}
|
|
25
73
|
export function createDockerFixDeps(run = runDocker) {
|
|
26
74
|
return {
|
|
27
75
|
async listNetworks() {
|
|
@@ -58,6 +106,54 @@ export function createDockerFixDeps(run = runDocker) {
|
|
|
58
106
|
throw new Error(firstLine(error));
|
|
59
107
|
}
|
|
60
108
|
},
|
|
109
|
+
freeDiskGib() {
|
|
110
|
+
try {
|
|
111
|
+
const stats = statfsSync(homedir());
|
|
112
|
+
return (Number(stats.bavail) * Number(stats.bsize)) / 1024 ** 3;
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
async reclaimable() {
|
|
119
|
+
let buildCache = null;
|
|
120
|
+
try {
|
|
121
|
+
const df = await run(["system", "df", "--format", "{{.Type}}\t{{.Reclaimable}}"]);
|
|
122
|
+
for (const line of df.split(/\r?\n/)) {
|
|
123
|
+
const [type, size] = line.split("\t");
|
|
124
|
+
// "1.531GB" or "1.5GB (39%)"; the percentage is of the whole, not useful here.
|
|
125
|
+
if (type === "Build Cache" && size)
|
|
126
|
+
buildCache = size.replace(/\s*\(.*\)/, "").trim();
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
// Leave unknown.
|
|
131
|
+
}
|
|
132
|
+
let danglingImages = 0;
|
|
133
|
+
try {
|
|
134
|
+
const ids = await run(["images", "-q", "-f", "dangling=true"]);
|
|
135
|
+
danglingImages = ids.split(/\r?\n/).filter(Boolean).length;
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
// Leave zero.
|
|
139
|
+
}
|
|
140
|
+
return { buildCache, danglingImages };
|
|
141
|
+
},
|
|
142
|
+
async prune(kind) {
|
|
143
|
+
try {
|
|
144
|
+
// Never `volume prune`, never `-a`: only the build cache and untagged images.
|
|
145
|
+
await run(kind === "builder" ? ["builder", "prune", "-f"] : ["image", "prune", "-f"]);
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
throw new Error(firstLine(error));
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
async tiltStatus(pid) {
|
|
152
|
+
return tiltProcessStatus(pid);
|
|
153
|
+
},
|
|
154
|
+
stopProcess(pid) {
|
|
155
|
+
process.kill(pid, "SIGTERM");
|
|
156
|
+
},
|
|
61
157
|
isProcessAlive(pid) {
|
|
62
158
|
try {
|
|
63
159
|
process.kill(pid, 0);
|
|
@@ -72,17 +168,66 @@ export function createDockerFixDeps(run = runDocker) {
|
|
|
72
168
|
}
|
|
73
169
|
export const dockerFixDeps = createDockerFixDeps();
|
|
74
170
|
/**
|
|
75
|
-
* What `--fix` would do
|
|
76
|
-
* (the exact names the engine creates,
|
|
171
|
+
* What `--fix` would do: stop this project's stale Tilt; remove unused networks that are a leftover doctor probe or
|
|
172
|
+
* this project's own (the exact names the engine creates, skipped while its Tilt runs). Other TDK projects' unused
|
|
173
|
+
* networks are planned only with `context.machine`, each naming its owner.
|
|
77
174
|
*/
|
|
78
|
-
export async function planDoctorFixes(projectName, deps = dockerFixDeps,
|
|
79
|
-
const networks = await deps.listNetworks();
|
|
175
|
+
export async function planDoctorFixes(projectName, deps = dockerFixDeps, context = {}) {
|
|
80
176
|
const actions = [];
|
|
177
|
+
// A Tilt that has run a while without serving its UI is stale and may be stopped; any other Tilt of this project is
|
|
178
|
+
// running or starting, and its networks are not to be touched.
|
|
179
|
+
const liveTilt = [];
|
|
180
|
+
for (const pid of context.tiltPids ?? []) {
|
|
181
|
+
if ((await deps.tiltStatus(pid)) === "stale") {
|
|
182
|
+
actions.push({
|
|
183
|
+
id: `stop-tilt:${pid}`,
|
|
184
|
+
check: "Tilt processes",
|
|
185
|
+
scope: "project",
|
|
186
|
+
command: `kill -TERM ${pid}`,
|
|
187
|
+
status: "planned",
|
|
188
|
+
reason: "this project's Tilt is running but its UI does not answer",
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
liveTilt.push(pid);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
const projectBusy = liveTilt.length > 0
|
|
196
|
+
? `Tilt is running for this project (pid ${liveTilt.join(", ")}); run tdk down first`
|
|
197
|
+
: undefined;
|
|
198
|
+
const networks = await deps.listNetworks();
|
|
199
|
+
const freeGib = deps.freeDiskGib();
|
|
200
|
+
if (freeGib !== null && freeGib < DISK_WARN_GIB) {
|
|
201
|
+
const { buildCache, danglingImages } = await deps.reclaimable();
|
|
202
|
+
const low = `${freeGib.toFixed(1)} GB free`;
|
|
203
|
+
if (buildCache && !/^0(\.0+)?\s*B$/i.test(buildCache)) {
|
|
204
|
+
actions.push({
|
|
205
|
+
id: "prune:builder",
|
|
206
|
+
check: "Disk Space",
|
|
207
|
+
scope: "host",
|
|
208
|
+
command: "docker builder prune -f",
|
|
209
|
+
status: "planned",
|
|
210
|
+
reason: `${low}; ${buildCache} of build cache reclaimable`,
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
if (danglingImages > 0) {
|
|
214
|
+
actions.push({
|
|
215
|
+
id: "prune:images",
|
|
216
|
+
check: "Disk Space",
|
|
217
|
+
scope: "host",
|
|
218
|
+
command: "docker image prune -f",
|
|
219
|
+
status: "planned",
|
|
220
|
+
reason: `${low}; ${danglingImages} untagged image${danglingImages === 1 ? "" : "s"}`,
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
}
|
|
81
224
|
const own = new Set(projectName ? projectNetworkNames(projectName) : []);
|
|
225
|
+
const prefixes = tdkNetworkPrefixes(networks.map((network) => network.name));
|
|
82
226
|
for (const network of networks) {
|
|
83
227
|
const probe = PROBE_NETWORK.exec(network.name);
|
|
84
228
|
let scope;
|
|
85
229
|
let check;
|
|
230
|
+
let reason = "unused network";
|
|
86
231
|
if (probe) {
|
|
87
232
|
scope = "probe";
|
|
88
233
|
check = "Orphaned probe networks";
|
|
@@ -91,6 +236,11 @@ export async function planDoctorFixes(projectName, deps = dockerFixDeps, project
|
|
|
91
236
|
scope = "project";
|
|
92
237
|
check = "Docker networks";
|
|
93
238
|
}
|
|
239
|
+
else if (context.machine && tdkNetworkOwner(network.name, prefixes)) {
|
|
240
|
+
scope = "machine";
|
|
241
|
+
check = "Docker networks";
|
|
242
|
+
reason = `unused network of project "${tdkNetworkOwner(network.name, prefixes)}"`;
|
|
243
|
+
}
|
|
94
244
|
else {
|
|
95
245
|
continue;
|
|
96
246
|
}
|
|
@@ -100,7 +250,7 @@ export async function planDoctorFixes(projectName, deps = dockerFixDeps, project
|
|
|
100
250
|
scope,
|
|
101
251
|
command: `docker network rm ${network.name}`,
|
|
102
252
|
status: "planned",
|
|
103
|
-
reason
|
|
253
|
+
reason,
|
|
104
254
|
};
|
|
105
255
|
if (network.containers > 0) {
|
|
106
256
|
action.status = "skipped";
|
|
@@ -147,9 +297,18 @@ export async function applyDoctorFixes(actions, options, deps = dockerFixDeps) {
|
|
|
147
297
|
results.push({ ...action, status: "skipped", reason: "not confirmed" });
|
|
148
298
|
continue;
|
|
149
299
|
}
|
|
150
|
-
const name = action.id.slice("remove-network:".length);
|
|
151
300
|
try {
|
|
152
|
-
|
|
301
|
+
if (action.id.startsWith("prune:")) {
|
|
302
|
+
await deps.prune(action.id === "prune:builder" ? "builder" : "images");
|
|
303
|
+
results.push({ ...action, status: "applied", reason: "pruned" });
|
|
304
|
+
continue;
|
|
305
|
+
}
|
|
306
|
+
if (action.id.startsWith("stop-tilt:")) {
|
|
307
|
+
deps.stopProcess(Number(action.id.slice("stop-tilt:".length)));
|
|
308
|
+
results.push({ ...action, status: "applied", reason: "stopped" });
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
311
|
+
await deps.removeNetwork(action.id.slice("remove-network:".length));
|
|
153
312
|
results.push({ ...action, status: "applied", reason: "removed" });
|
|
154
313
|
}
|
|
155
314
|
catch (error) {
|
|
@@ -17,6 +17,32 @@ export function projectNetworkNames(projectName) {
|
|
|
17
17
|
const prefix = projectName.replace(/-/g, "_");
|
|
18
18
|
return PROJECT_NETWORK_SUFFIXES.map((suffix) => `${prefix}_${suffix}`);
|
|
19
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* Prefixes of TDK projects among `networkNames`, from the networks the engine creates. Generic suffixes such as
|
|
22
|
+
* `backend` also occur in non-TDK compose projects, so a prefix counts only if it has a Traefik network, which
|
|
23
|
+
* only TDK creates.
|
|
24
|
+
*/
|
|
25
|
+
export function tdkNetworkPrefixes(networkNames) {
|
|
26
|
+
const prefixes = new Set();
|
|
27
|
+
for (const name of networkNames) {
|
|
28
|
+
for (const marker of ["traefik-public", "traefik-private"]) {
|
|
29
|
+
if (name.endsWith(`_${marker}`))
|
|
30
|
+
prefixes.add(name.slice(0, -(marker.length + 1)));
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return prefixes;
|
|
34
|
+
}
|
|
35
|
+
/** The TDK project prefix a network belongs to (`umami` for `umami_backend`), or null. */
|
|
36
|
+
export function tdkNetworkOwner(name, prefixes) {
|
|
37
|
+
for (const suffix of PROJECT_NETWORK_SUFFIXES) {
|
|
38
|
+
if (name.endsWith(`_${suffix}`)) {
|
|
39
|
+
const prefix = name.slice(0, -(suffix.length + 1));
|
|
40
|
+
if (prefixes.has(prefix))
|
|
41
|
+
return prefix;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
20
46
|
/**
|
|
21
47
|
* Remove this project's own networks. Only the exact names the engine creates are touched, and `docker network rm`
|
|
22
48
|
* itself refuses a network that still has containers attached, so another project's networks and in-use networks
|
|
@@ -47,3 +47,17 @@ function canonical(path) {
|
|
|
47
47
|
export function isForeignTilt(owner, projectRoot) {
|
|
48
48
|
return owner !== null && canonical(owner.root) !== canonical(projectRoot);
|
|
49
49
|
}
|
|
50
|
+
/** Split the candidate Tilt UI ports that answer into this project's and other projects'. */
|
|
51
|
+
export async function classifyTiltPorts(candidatePorts, projectRoot, isServing, ownerOf = findTiltPortOwner) {
|
|
52
|
+
const result = { running: [], foreign: [] };
|
|
53
|
+
for (const port of candidatePorts) {
|
|
54
|
+
if (!(await isServing(port)))
|
|
55
|
+
continue;
|
|
56
|
+
const owner = ownerOf(port);
|
|
57
|
+
if (owner && isForeignTilt(owner, projectRoot))
|
|
58
|
+
result.foreign.push({ port, root: owner.root });
|
|
59
|
+
else
|
|
60
|
+
result.running.push(port);
|
|
61
|
+
}
|
|
62
|
+
return result;
|
|
63
|
+
}
|
package/cli/dist/utils/tilt.js
CHANGED
|
@@ -92,9 +92,9 @@ export function buildTiltUpArgs(serviceNames, options = {}) {
|
|
|
92
92
|
export function buildTiltDownArgs(options = {}) {
|
|
93
93
|
const args = [];
|
|
94
94
|
addTiltfilePath(args);
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
95
|
+
// Note: `tilt down` has no `--force` flag. The `-f/--force` option is accepted
|
|
96
|
+
// by `tdk down` for script compatibility and skipping confirmation, but must
|
|
97
|
+
// not be passed to `tilt down` as Tilt will exit with an "unknown flag: --force" error.
|
|
98
98
|
// `tilt down` re-evaluates the Tiltfile and removes only the resources that evaluation registers. Without `--focus`
|
|
99
99
|
// the Tiltfile falls back to its default phase and never registers a stack's own compose project, so `tdk up shop`
|
|
100
100
|
// containers survived `tdk down`. Pass the same selection `tdk up` used (every stack) after `--`.
|