cdk-local 0.149.4 → 0.149.5
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/dist/cli.js +2 -2
- package/dist/docker-cmd-B5yU__zC.js +1429 -0
- package/dist/docker-cmd-B5yU__zC.js.map +1 -0
- package/dist/index.js +2 -2
- package/dist/internal.js +2 -1
- package/dist/{local-studio-Cdq8klwg.js → local-studio-CV21zLBM.js} +14 -875
- package/dist/local-studio-CV21zLBM.js.map +1 -0
- package/package.json +1 -1
- package/dist/docker-cmd-SocNyC9q.js +0 -560
- package/dist/docker-cmd-SocNyC9q.js.map +0 -1
- package/dist/local-studio-Cdq8klwg.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cdk-local",
|
|
3
|
-
"version": "0.149.
|
|
3
|
+
"version": "0.149.5",
|
|
4
4
|
"description": "Run your CDK-built app locally — standalone, or against the real AWS resources and data it depends on. A CDK-native alternative to sam local.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -1,560 +0,0 @@
|
|
|
1
|
-
import { spinner } from "@clack/prompts";
|
|
2
|
-
import { spawn } from "node:child_process";
|
|
3
|
-
|
|
4
|
-
//#region \0rolldown/runtime.js
|
|
5
|
-
var __defProp = Object.defineProperty;
|
|
6
|
-
var __exportAll = (all, no_symbols) => {
|
|
7
|
-
let target = {};
|
|
8
|
-
for (var name in all) {
|
|
9
|
-
__defProp(target, name, {
|
|
10
|
-
get: all[name],
|
|
11
|
-
enumerable: true
|
|
12
|
-
});
|
|
13
|
-
}
|
|
14
|
-
if (!no_symbols) {
|
|
15
|
-
__defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
16
|
-
}
|
|
17
|
-
return target;
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
//#endregion
|
|
21
|
-
//#region src/local/embed-config.ts
|
|
22
|
-
const DEFAULTS = {
|
|
23
|
-
cliName: "cdkl",
|
|
24
|
-
binaryName: "cdkl",
|
|
25
|
-
productName: "cdk-local",
|
|
26
|
-
resourceNamePrefix: "cdkl",
|
|
27
|
-
awsBindMountPath: "/cdk-local-aws",
|
|
28
|
-
envPrefix: "CDKL",
|
|
29
|
-
sigV4StrictByDefault: false,
|
|
30
|
-
sigV4OptFlag: "--strict-sigv4"
|
|
31
|
-
};
|
|
32
|
-
let current = DEFAULTS;
|
|
33
|
-
/**
|
|
34
|
-
* Resolve and install the active embed config. Called once per command
|
|
35
|
-
* factory with the host's overrides (or `undefined` for native cdkl
|
|
36
|
-
* behavior). Idempotent: re-calling with the same overrides is a no-op,
|
|
37
|
-
* which is why all four factories may safely set the same config.
|
|
38
|
-
*/
|
|
39
|
-
function setEmbedConfig(config) {
|
|
40
|
-
current = {
|
|
41
|
-
cliName: config?.cliName ?? DEFAULTS.cliName,
|
|
42
|
-
binaryName: config?.binaryName ?? DEFAULTS.binaryName,
|
|
43
|
-
productName: config?.productName ?? DEFAULTS.productName,
|
|
44
|
-
resourceNamePrefix: config?.resourceNamePrefix ?? DEFAULTS.resourceNamePrefix,
|
|
45
|
-
awsBindMountPath: config?.awsBindMountPath ?? DEFAULTS.awsBindMountPath,
|
|
46
|
-
envPrefix: config?.envPrefix ?? DEFAULTS.envPrefix,
|
|
47
|
-
sigV4StrictByDefault: config?.sigV4StrictByDefault ?? DEFAULTS.sigV4StrictByDefault,
|
|
48
|
-
sigV4OptFlag: config?.sigV4OptFlag ?? DEFAULTS.sigV4OptFlag
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
/** The active resolved embed config. */
|
|
52
|
-
function getEmbedConfig() {
|
|
53
|
-
return current;
|
|
54
|
-
}
|
|
55
|
-
/** Restore cdk-local defaults. Primarily a test-isolation helper. */
|
|
56
|
-
function resetEmbedConfig() {
|
|
57
|
-
current = DEFAULTS;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
//#endregion
|
|
61
|
-
//#region src/utils/logger.ts
|
|
62
|
-
/**
|
|
63
|
-
* ANSI color codes
|
|
64
|
-
*
|
|
65
|
-
* Kept internal — `ConsoleLogger.formatMessage` references these for the
|
|
66
|
-
* verbose/compact mode level prefixes.
|
|
67
|
-
*/
|
|
68
|
-
const colors = {
|
|
69
|
-
reset: "\x1B[0m",
|
|
70
|
-
bright: "\x1B[1m",
|
|
71
|
-
dim: "\x1B[2m",
|
|
72
|
-
red: "\x1B[31m",
|
|
73
|
-
green: "\x1B[32m",
|
|
74
|
-
yellow: "\x1B[33m",
|
|
75
|
-
blue: "\x1B[34m",
|
|
76
|
-
cyan: "\x1B[36m",
|
|
77
|
-
gray: "\x1B[90m"
|
|
78
|
-
};
|
|
79
|
-
function formatTimestamp() {
|
|
80
|
-
return (/* @__PURE__ */ new Date()).toISOString();
|
|
81
|
-
}
|
|
82
|
-
/**
|
|
83
|
-
* Resolve whether ANSI color should be emitted by default.
|
|
84
|
-
*
|
|
85
|
-
* Color is appropriate for an interactive terminal but is noise (literal
|
|
86
|
-
* `\x1b[31m...` escapes) when the output is a pipe — e.g. when `cdkl studio`
|
|
87
|
-
* spawns `cdkl invoke` / a serve command as a child and captures its output to
|
|
88
|
-
* render in the browser, where the raw escapes leak as visible text. So the
|
|
89
|
-
* default tracks the stdout TTY-ness, with the two standard env overrides:
|
|
90
|
-
*
|
|
91
|
-
* - `NO_COLOR` (any non-empty value) forces colors OFF (https://no-color.org).
|
|
92
|
-
* - `FORCE_COLOR` (non-empty, not `'0'` / `'false'`) forces colors ON, even
|
|
93
|
-
* when not a TTY (the convention many CLIs honor for CI / log capture).
|
|
94
|
-
* - otherwise: on when `process.stdout.isTTY`.
|
|
95
|
-
*
|
|
96
|
-
* We gate on `process.stdout.isTTY` even though warn / error go to stderr via
|
|
97
|
-
* `console.error` / `console.warn`. In the case this fix targets — a piped
|
|
98
|
-
* child (e.g. studio) — NEITHER stdout nor stderr is a TTY, so gating on stdout
|
|
99
|
-
* still yields colorless output. Tracking stdout matches common CLI tooling and
|
|
100
|
-
* keeps a single, predictable signal; an explicit `useColors` argument (passed
|
|
101
|
-
* by child loggers) always overrides this default.
|
|
102
|
-
*/
|
|
103
|
-
function resolveDefaultUseColors() {
|
|
104
|
-
const noColor = process.env["NO_COLOR"];
|
|
105
|
-
if (noColor !== void 0 && noColor !== "") return false;
|
|
106
|
-
const forceColor = process.env["FORCE_COLOR"];
|
|
107
|
-
if (forceColor !== void 0 && forceColor !== "" && forceColor !== "0" && forceColor !== "false") return true;
|
|
108
|
-
return !!process.stdout.isTTY;
|
|
109
|
-
}
|
|
110
|
-
/**
|
|
111
|
-
* Console logger implementation
|
|
112
|
-
*
|
|
113
|
-
* Supports two output modes:
|
|
114
|
-
* - verbose (debug level): timestamps, module prefixes, all details
|
|
115
|
-
* - compact (info level): clean output without timestamps or prefixes
|
|
116
|
-
*/
|
|
117
|
-
var ConsoleLogger = class {
|
|
118
|
-
level;
|
|
119
|
-
useColors;
|
|
120
|
-
constructor(level = "info", useColors = resolveDefaultUseColors()) {
|
|
121
|
-
this.level = level;
|
|
122
|
-
this.useColors = useColors;
|
|
123
|
-
}
|
|
124
|
-
shouldLog(level) {
|
|
125
|
-
const levels = [
|
|
126
|
-
"debug",
|
|
127
|
-
"info",
|
|
128
|
-
"warn",
|
|
129
|
-
"error"
|
|
130
|
-
];
|
|
131
|
-
const currentLevelIndex = levels.indexOf(this.level);
|
|
132
|
-
return levels.indexOf(level) >= currentLevelIndex;
|
|
133
|
-
}
|
|
134
|
-
formatMessage(level, message, ...args) {
|
|
135
|
-
const formattedArgs = args.length > 0 ? " " + args.map((a) => JSON.stringify(a)).join(" ") : "";
|
|
136
|
-
if (this.level === "debug") {
|
|
137
|
-
const timestamp = formatTimestamp();
|
|
138
|
-
const levelStr = level.toUpperCase().padEnd(5);
|
|
139
|
-
if (this.useColors) {
|
|
140
|
-
const levelColor = {
|
|
141
|
-
debug: colors.gray,
|
|
142
|
-
info: colors.blue,
|
|
143
|
-
warn: colors.yellow,
|
|
144
|
-
error: colors.red
|
|
145
|
-
}[level];
|
|
146
|
-
return `${colors.dim}${timestamp}${colors.reset} ${levelColor}${levelStr}${colors.reset} ${message}${formattedArgs}`;
|
|
147
|
-
}
|
|
148
|
-
return `${timestamp} ${levelStr} ${message}${formattedArgs}`;
|
|
149
|
-
}
|
|
150
|
-
if (level === "error") {
|
|
151
|
-
const line = `ERROR: ${message}${formattedArgs}`;
|
|
152
|
-
return this.useColors ? `${colors.red}${line}${colors.reset}` : line;
|
|
153
|
-
}
|
|
154
|
-
if (level === "warn") {
|
|
155
|
-
const line = `WARN: ${message}${formattedArgs}`;
|
|
156
|
-
return this.useColors ? `${colors.yellow}${line}${colors.reset}` : line;
|
|
157
|
-
}
|
|
158
|
-
return `${message}${formattedArgs}`;
|
|
159
|
-
}
|
|
160
|
-
emit(level, formatted) {
|
|
161
|
-
if (process.env["CDKL_LOG_STREAM"] === "stdout") {
|
|
162
|
-
console.log(formatted);
|
|
163
|
-
return;
|
|
164
|
-
}
|
|
165
|
-
if (level === "error") console.error(formatted);
|
|
166
|
-
else if (level === "warn") console.warn(formatted);
|
|
167
|
-
else if (level === "info") console.info(formatted);
|
|
168
|
-
else console.debug(formatted);
|
|
169
|
-
}
|
|
170
|
-
debug(message, ...args) {
|
|
171
|
-
if (this.shouldLog("debug")) this.emit("debug", this.formatMessage("debug", message, ...args));
|
|
172
|
-
}
|
|
173
|
-
info(message, ...args) {
|
|
174
|
-
if (this.shouldLog("info")) this.emit("info", this.formatMessage("info", message, ...args));
|
|
175
|
-
}
|
|
176
|
-
warn(message, ...args) {
|
|
177
|
-
if (this.shouldLog("warn")) this.emit("warn", this.formatMessage("warn", message, ...args));
|
|
178
|
-
}
|
|
179
|
-
error(message, ...args) {
|
|
180
|
-
if (this.shouldLog("error")) this.emit("error", this.formatMessage("error", message, ...args));
|
|
181
|
-
}
|
|
182
|
-
setLevel(level) {
|
|
183
|
-
this.level = level;
|
|
184
|
-
}
|
|
185
|
-
getLevel() {
|
|
186
|
-
return this.level;
|
|
187
|
-
}
|
|
188
|
-
child(prefix) {
|
|
189
|
-
return new ChildLogger(prefix, this.useColors);
|
|
190
|
-
}
|
|
191
|
-
};
|
|
192
|
-
/**
|
|
193
|
-
* Child logger that always syncs level from global logger
|
|
194
|
-
*/
|
|
195
|
-
var ChildLogger = class extends ConsoleLogger {
|
|
196
|
-
prefix;
|
|
197
|
-
constructor(prefix, useColors) {
|
|
198
|
-
super("info", useColors);
|
|
199
|
-
this.prefix = prefix;
|
|
200
|
-
}
|
|
201
|
-
syncLevel() {
|
|
202
|
-
if (globalLogger) this.setLevel(globalLogger.getLevel());
|
|
203
|
-
}
|
|
204
|
-
debug(message, ...args) {
|
|
205
|
-
this.syncLevel();
|
|
206
|
-
super.debug(`[${this.prefix}] ${message}`, ...args);
|
|
207
|
-
}
|
|
208
|
-
info(message, ...args) {
|
|
209
|
-
this.syncLevel();
|
|
210
|
-
const msg = this.getLevel() === "debug" ? `[${this.prefix}] ${message}` : message;
|
|
211
|
-
super.info(msg, ...args);
|
|
212
|
-
}
|
|
213
|
-
warn(message, ...args) {
|
|
214
|
-
this.syncLevel();
|
|
215
|
-
const msg = this.getLevel() === "debug" ? `[${this.prefix}] ${message}` : message;
|
|
216
|
-
super.warn(msg, ...args);
|
|
217
|
-
}
|
|
218
|
-
error(message, ...args) {
|
|
219
|
-
this.syncLevel();
|
|
220
|
-
const msg = this.getLevel() === "debug" ? `[${this.prefix}] ${message}` : message;
|
|
221
|
-
super.error(msg, ...args);
|
|
222
|
-
}
|
|
223
|
-
};
|
|
224
|
-
/**
|
|
225
|
-
* Resolve the initial log level from the `CDKL_LOG_LEVEL` env var, falling
|
|
226
|
-
* back to `'info'`. This is primarily an internal contract: `cdkl studio`
|
|
227
|
-
* spawns its single-shot `cdkl invoke` child with `CDKL_LOG_LEVEL=warn` so
|
|
228
|
-
* cdk-local's OWN synth / orchestration progress (toolkit "Successfully
|
|
229
|
-
* synthesized to ...", asset-bundling lines, info-level status) is silenced
|
|
230
|
-
* in the child — leaving the studio LOGS panel showing only the Lambda
|
|
231
|
-
* container's runtime logs (which stream straight from `docker logs` and are
|
|
232
|
-
* unaffected by this level) plus the response. `--verbose` still overrides to
|
|
233
|
-
* `debug` at the command layer. An invalid value is ignored.
|
|
234
|
-
*/
|
|
235
|
-
function resolveConfiguredLogLevel() {
|
|
236
|
-
const env = process.env["CDKL_LOG_LEVEL"];
|
|
237
|
-
if (env === "debug" || env === "info" || env === "warn" || env === "error") return env;
|
|
238
|
-
return "info";
|
|
239
|
-
}
|
|
240
|
-
let globalLogger = null;
|
|
241
|
-
function getLogger() {
|
|
242
|
-
if (!globalLogger) globalLogger = new ConsoleLogger(resolveConfiguredLogLevel());
|
|
243
|
-
return globalLogger;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
//#endregion
|
|
247
|
-
//#region src/utils/docker-cmd.ts
|
|
248
|
-
var docker_cmd_exports = /* @__PURE__ */ __exportAll({
|
|
249
|
-
allowSecretsOnArgvEnvName: () => allowSecretsOnArgvEnvName,
|
|
250
|
-
finchSecretArgvRefusal: () => finchSecretArgvRefusal,
|
|
251
|
-
formatDockerLoginError: () => formatDockerLoginError,
|
|
252
|
-
getDockerCmd: () => getDockerCmd,
|
|
253
|
-
isFinchVmClient: () => isFinchVmClient,
|
|
254
|
-
runDockerForeground: () => runDockerForeground,
|
|
255
|
-
runDockerStreaming: () => runDockerStreaming,
|
|
256
|
-
spawnForeground: () => spawnForeground,
|
|
257
|
-
spawnStreaming: () => spawnStreaming,
|
|
258
|
-
warnFinchArgvExposure: () => warnFinchArgvExposure
|
|
259
|
-
});
|
|
260
|
-
/**
|
|
261
|
-
* Shared helpers for invoking the docker-compatible CLI binary across cdk-local.
|
|
262
|
-
*
|
|
263
|
-
* Two parity decisions with `aws-cdk-cli`'s `cdk-assets-lib`:
|
|
264
|
-
* 1. `CDK_DOCKER` env var swaps the binary so podman / finch users can
|
|
265
|
-
* run cdk-local without code changes (`CDK_DOCKER=podman cdkl invoke`).
|
|
266
|
-
* 2. `runDockerStreaming` uses streaming spawn rather than `execFile`'s
|
|
267
|
-
* buffered `maxBuffer` ceiling. BuildKit's progress output can run to
|
|
268
|
-
* tens of MB on multi-stage builds with `# syntax=docker/dockerfile:1`
|
|
269
|
-
* frontend downloads + heredoc / `RUN --mount=...` features; the 50 MB
|
|
270
|
-
* `execFile` ceiling cdk-local used to set silently killed those builds
|
|
271
|
-
* with `ERR_CHILD_PROCESS_STDIO_MAXBUFFER`.
|
|
272
|
-
*
|
|
273
|
-
* Output handling: stdout/stderr are collected in memory unconditionally so
|
|
274
|
-
* `runDockerStreaming` can return them to the caller for error wrapping.
|
|
275
|
-
* When the logger is at debug level (i.e. the user passed `--verbose`),
|
|
276
|
-
* the chunks are ALSO mirrored to `process.stdout` / `process.stderr` so
|
|
277
|
-
* the user sees live build progress.
|
|
278
|
-
*/
|
|
279
|
-
/**
|
|
280
|
-
* Return the docker-compatible CLI binary to invoke. Matches CDK CLI:
|
|
281
|
-
* `CDK_DOCKER` env var overrides the default `docker` so users on
|
|
282
|
-
* podman / finch / nerdctl can swap without changing cdk-local code.
|
|
283
|
-
*/
|
|
284
|
-
function getDockerCmd() {
|
|
285
|
-
const override = process.env["CDK_DOCKER"];
|
|
286
|
-
return override && override.length > 0 ? override : "docker";
|
|
287
|
-
}
|
|
288
|
-
/**
|
|
289
|
-
* Is the container client finch running its Lima VM (macOS / Windows)? There
|
|
290
|
-
* the value-less `-e KEY` form (`appendEnvFlags` in `local/docker-runner.ts`)
|
|
291
|
-
* does NOT keep a value off the process command line (issue #749): finch's
|
|
292
|
-
* `handleEnv` resolves each bare `KEY` against its own environment and
|
|
293
|
-
* re-emits `-e KEY=<value>` on the argv of the `limactl shell finch sudo -E
|
|
294
|
-
* nerdctl ...` child it starts, and `--env-file` is read on the host and
|
|
295
|
-
* re-emitted the same way (runfinch/finch `cmd/finch/nerdctl_remote.go`,
|
|
296
|
-
* `handleEnv` / `handleEnvFile`). Its `passedEnvs` list also puts
|
|
297
|
-
* `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` / `AWS_SESSION_TOKEN` from its
|
|
298
|
-
* environment on that argv for every command. finch on Linux passes the argv
|
|
299
|
-
* to nerdctl unchanged and is NOT matched.
|
|
300
|
-
*
|
|
301
|
-
* Detection is by the BASENAME of the resolved binary (`finch`, `finch.exe`,
|
|
302
|
-
* any case): a wrapper script or a symlink under another name is not
|
|
303
|
-
* recognised, and neither is Lima's own `nerdctl.lima`.
|
|
304
|
-
*/
|
|
305
|
-
function isFinchVmClient(cmd = getDockerCmd(), platform = process.platform) {
|
|
306
|
-
if (platform !== "darwin" && platform !== "win32") return false;
|
|
307
|
-
const base = (cmd.split(/[\\/]/).pop() ?? "").toLowerCase();
|
|
308
|
-
return base === "finch" || base === "finch.exe";
|
|
309
|
-
}
|
|
310
|
-
/**
|
|
311
|
-
* The env var that accepts, while it is set, that a container client puts
|
|
312
|
-
* secret VALUES on a process command line ({@link finchSecretArgvRefusal}):
|
|
313
|
-
* `<envPrefix>_ALLOW_SECRETS_ON_ARGV`, so `CDKL_ALLOW_SECRETS_ON_ARGV` for
|
|
314
|
-
* `cdkl` and the embedding host's own prefix otherwise. Only `1` or `true`
|
|
315
|
-
* (any case) opts in.
|
|
316
|
-
*/
|
|
317
|
-
function allowSecretsOnArgvEnvName() {
|
|
318
|
-
return `${getEmbedConfig().envPrefix}_ALLOW_SECRETS_ON_ARGV`;
|
|
319
|
-
}
|
|
320
|
-
function secretsOnArgvAllowed() {
|
|
321
|
-
const v = process.env[allowSecretsOnArgvEnvName()];
|
|
322
|
-
return v !== void 0 && ["1", "true"].includes(v.trim().toLowerCase());
|
|
323
|
-
}
|
|
324
|
-
const MESSAGE_UNSAFE_CHARS = /[\u0000-\u001f\u007f-\u009f\u2028\u2029]/g;
|
|
325
|
-
/**
|
|
326
|
-
* Render template- or state-sourced text (an env var name, a container name,
|
|
327
|
-
* an image URI) for a message: control and line-separator characters are
|
|
328
|
-
* escaped as `\uXXXX` so a hostile value cannot drive the terminal or forge a
|
|
329
|
-
* log line. Plain text renders unchanged.
|
|
330
|
-
*/
|
|
331
|
-
function escapeForMessage(text) {
|
|
332
|
-
return text.replace(MESSAGE_UNSAFE_CHARS, (c) => `\\u${c.charCodeAt(0).toString(16).padStart(4, "0")}`);
|
|
333
|
-
}
|
|
334
|
-
/**
|
|
335
|
-
* The refusal text when template-sourced secrets (ECS `Secrets`, decrypted
|
|
336
|
-
* `SecureString` values) would be forwarded under {@link isFinchVmClient}, or
|
|
337
|
-
* `undefined` when they may be forwarded: another client, no such secret, or
|
|
338
|
-
* {@link allowSecretsOnArgvEnvName} set. The caller throws it in its own error
|
|
339
|
-
* class BEFORE any `docker run`. The AWS credential set is not refused here
|
|
340
|
-
* but warned about by {@link warnFinchArgvExposure}: forwarding it is what puts
|
|
341
|
-
* credentials that are not in this process's own environment (`--assume-role`,
|
|
342
|
-
* `--profile`, the metadata sidecar's) on that argv, and the warning makes that
|
|
343
|
-
* visible while keeping finch usable for containers that need AWS access. Names
|
|
344
|
-
* only, never a value. `subject` is context such as the container name; it is
|
|
345
|
-
* escaped like the names.
|
|
346
|
-
*/
|
|
347
|
-
function finchSecretArgvRefusal(secretNames, subject) {
|
|
348
|
-
if (secretNames.length === 0 || !isFinchVmClient() || secretsOnArgvAllowed()) return void 0;
|
|
349
|
-
const names = [...new Set(secretNames)];
|
|
350
|
-
return `${escapeForMessage(subject)}: refusing to forward secret(s) ${names.map(escapeForMessage).join(", ")} under CDK_DOCKER=finch on macOS / Windows. finch turns each value-less '-e KEY' into '-e KEY=<value>' on the command line of the limactl process it starts, where other local processes can read the plaintext. Use a container client that keeps the value off the command line (for example the default 'docker', by unsetting CDK_DOCKER), or set ${allowSecretsOnArgvEnvName()}=1 to accept that exposure while it stays set.`;
|
|
351
|
-
}
|
|
352
|
-
/** Key sets already warned about by {@link warnFinchArgvExposure} in this process. */
|
|
353
|
-
const finchArgvWarned = /* @__PURE__ */ new Set();
|
|
354
|
-
/**
|
|
355
|
-
* Warn, once per process per distinct key set, that under
|
|
356
|
-
* {@link isFinchVmClient} the values of `keys` (the sensitive env about to be
|
|
357
|
-
* forwarded as value-less `-e KEY`) reach the `limactl` command line. Names
|
|
358
|
-
* only, never a value.
|
|
359
|
-
*/
|
|
360
|
-
function warnFinchArgvExposure(keys) {
|
|
361
|
-
if (keys.length === 0 || !isFinchVmClient()) return;
|
|
362
|
-
const names = [...new Set(keys)].sort();
|
|
363
|
-
const latch = JSON.stringify(names);
|
|
364
|
-
if (finchArgvWarned.has(latch)) return;
|
|
365
|
-
finchArgvWarned.add(latch);
|
|
366
|
-
getLogger().warn(`CDK_DOCKER=finch on macOS / Windows puts the values of ${names.map(escapeForMessage).join(", ")} on the command line of the limactl process it starts (finch turns a value-less '-e KEY' into '-e KEY=<value>'), where other local processes can read them. The default 'docker' client keeps them off the command line.`);
|
|
367
|
-
}
|
|
368
|
-
/**
|
|
369
|
-
* Spawn a docker-compatible CLI binary (resolved via `getDockerCmd`) with
|
|
370
|
-
* streaming I/O. Collects stdout/stderr in memory and resolves with both
|
|
371
|
-
* on exit code 0; rejects with a `SpawnError` carrying both streams on any
|
|
372
|
-
* non-zero exit so the caller can wrap with its own error class without
|
|
373
|
-
* losing the upstream output.
|
|
374
|
-
*
|
|
375
|
-
* No `maxBuffer` ceiling: BuildKit progress output frequently exceeds the
|
|
376
|
-
* `child_process.execFile` default of 1 MB (cdk-local previously bumped to 50 MB
|
|
377
|
-
* but BuildKit + frontend pulls can still exceed that on first-time builds).
|
|
378
|
-
*/
|
|
379
|
-
async function runDockerStreaming(args, options = {}) {
|
|
380
|
-
return spawnStreaming(getDockerCmd(), args, options);
|
|
381
|
-
}
|
|
382
|
-
/**
|
|
383
|
-
* Generic streaming spawn — used by `runDockerStreaming` AND by the
|
|
384
|
-
* `executable` source mode in `docker-build.ts` (which runs an arbitrary
|
|
385
|
-
* user-supplied build command, not docker).
|
|
386
|
-
*/
|
|
387
|
-
async function spawnStreaming(cmd, args, options = {}) {
|
|
388
|
-
const streamLive = options.streamLive ?? getLogger().getLevel() === "debug";
|
|
389
|
-
const env = options.env ? mergeEnv(options.env) : void 0;
|
|
390
|
-
const spin = startProgressSpinner(options.progressLabel, streamLive);
|
|
391
|
-
return new Promise((resolve, reject) => {
|
|
392
|
-
let child;
|
|
393
|
-
try {
|
|
394
|
-
child = spawn(cmd, args, {
|
|
395
|
-
cwd: options.cwd,
|
|
396
|
-
env,
|
|
397
|
-
stdio: [
|
|
398
|
-
options.input ? "pipe" : "ignore",
|
|
399
|
-
"pipe",
|
|
400
|
-
"pipe"
|
|
401
|
-
]
|
|
402
|
-
});
|
|
403
|
-
} catch (err) {
|
|
404
|
-
stopProgressSpinner(spin, options.progressLabel);
|
|
405
|
-
reject(err);
|
|
406
|
-
return;
|
|
407
|
-
}
|
|
408
|
-
const stdoutChunks = [];
|
|
409
|
-
const stderrChunks = [];
|
|
410
|
-
child.stdout.on("data", (chunk) => {
|
|
411
|
-
stdoutChunks.push(chunk);
|
|
412
|
-
if (streamLive) process.stdout.write(chunk);
|
|
413
|
-
});
|
|
414
|
-
child.stderr.on("data", (chunk) => {
|
|
415
|
-
stderrChunks.push(chunk);
|
|
416
|
-
if (streamLive) process.stderr.write(chunk);
|
|
417
|
-
});
|
|
418
|
-
child.once("error", (err) => {
|
|
419
|
-
stopProgressSpinner(spin, options.progressLabel);
|
|
420
|
-
if (err.code === "ENOENT") {
|
|
421
|
-
const usingOverride = process.env["CDK_DOCKER"] === cmd && cmd !== "docker";
|
|
422
|
-
reject(/* @__PURE__ */ new Error(usingOverride ? `Failed to find and execute '${cmd}' (resolved via CDK_DOCKER). Install '${cmd}' or unset CDK_DOCKER to fall back to 'docker'.` : `Failed to find and execute '${cmd}'. Install Docker (or set the 'CDK_DOCKER' environment variable to a compatible binary such as podman / finch).`));
|
|
423
|
-
} else reject(err);
|
|
424
|
-
});
|
|
425
|
-
child.once("close", (code) => {
|
|
426
|
-
const stdout = Buffer.concat(stdoutChunks).toString("utf-8");
|
|
427
|
-
const stderr = Buffer.concat(stderrChunks).toString("utf-8");
|
|
428
|
-
if (code === 0) {
|
|
429
|
-
stopProgressSpinner(spin, options.progressLabel);
|
|
430
|
-
resolve({
|
|
431
|
-
stdout,
|
|
432
|
-
stderr
|
|
433
|
-
});
|
|
434
|
-
} else {
|
|
435
|
-
stopProgressSpinner(spin, options.progressLabel);
|
|
436
|
-
const message = stderr.trim() || stdout.trim() || `${cmd} ${args[0] ?? ""} exited with code ${code}`;
|
|
437
|
-
const err = new Error(message);
|
|
438
|
-
err.stderr = stderr;
|
|
439
|
-
err.stdout = stdout;
|
|
440
|
-
err.exitCode = code;
|
|
441
|
-
reject(err);
|
|
442
|
-
}
|
|
443
|
-
});
|
|
444
|
-
if (options.input !== void 0) {
|
|
445
|
-
child.stdin.on("error", () => {});
|
|
446
|
-
child.stdin.write(options.input);
|
|
447
|
-
child.stdin.end();
|
|
448
|
-
}
|
|
449
|
-
});
|
|
450
|
-
}
|
|
451
|
-
/**
|
|
452
|
-
* Start an interactive clack spinner for the spawn, but only when the
|
|
453
|
-
* current shell would actually render it (TTY) and the caller isn't
|
|
454
|
-
* already streaming live output. Returns `undefined` when either
|
|
455
|
-
* precondition fails — `stopProgressSpinner` then is a no-op.
|
|
456
|
-
*
|
|
457
|
-
* Test seam: the integration with `@clack/prompts` is mocked in
|
|
458
|
-
* `tests/unit/utils/docker-cmd-progress-spinner.test.ts`.
|
|
459
|
-
*/
|
|
460
|
-
function startProgressSpinner(label, streamLive) {
|
|
461
|
-
if (label === void 0 || streamLive || process.stdout.isTTY !== true) return void 0;
|
|
462
|
-
const spin = spinner();
|
|
463
|
-
spin.start(label);
|
|
464
|
-
return spin;
|
|
465
|
-
}
|
|
466
|
-
function stopProgressSpinner(spin, label) {
|
|
467
|
-
if (spin === void 0) return;
|
|
468
|
-
spin.stop(label ?? "");
|
|
469
|
-
}
|
|
470
|
-
/**
|
|
471
|
-
* Spawn a docker-compatible CLI binary (resolved via `getDockerCmd`) attached
|
|
472
|
-
* to the parent process's stdio so the user sees live output (`docker pull`
|
|
473
|
-
* layer progress, `docker login` interactive prompts that should never fire
|
|
474
|
-
* with `--password-stdin` but still safe to inherit, etc.). Resolves on exit
|
|
475
|
-
* code 0; rejects with a plain `Error` carrying the exit code on any non-zero
|
|
476
|
-
* exit, so the caller can wrap with its own error class.
|
|
477
|
-
*
|
|
478
|
-
* Differs from {@link runDockerStreaming} in two ways:
|
|
479
|
-
* 1. `stdio: 'inherit'` — output is NOT captured, so terminal control codes
|
|
480
|
-
* (color, progress bar overwrites) flow through unchanged. This is the
|
|
481
|
-
* load-bearing reason for the split: `docker pull`'s progress bars only
|
|
482
|
-
* animate properly when stdout is a real TTY connected to the parent.
|
|
483
|
-
* 2. No `input` / `streamLive` options — inherit-mode has nothing to
|
|
484
|
-
* capture and nothing to mirror.
|
|
485
|
-
*
|
|
486
|
-
* Used by the `--verbose`-mode `docker pull` plumbing in `docker-runner.ts`
|
|
487
|
-
* and `ecr-puller.ts` (visible layer progress). Non-verbose pulls go through
|
|
488
|
-
* {@link runDockerStreaming} so stderr can be folded into the error message.
|
|
489
|
-
*/
|
|
490
|
-
async function runDockerForeground(args, options = {}) {
|
|
491
|
-
return spawnForeground(getDockerCmd(), args, options);
|
|
492
|
-
}
|
|
493
|
-
/**
|
|
494
|
-
* Foreground (stdio-inherit) spawn — the inherit-mode counterpart to
|
|
495
|
-
* {@link spawnStreaming}. Used by {@link runDockerForeground} for docker-CLI
|
|
496
|
-
* subprocesses.
|
|
497
|
-
*
|
|
498
|
-
* The ENOENT branch crafts a docker-specific install hint ("Install Docker
|
|
499
|
-
* (or set CDK_DOCKER ...)"), so non-docker callers reusing this helper
|
|
500
|
-
* would see a misleading error on missing-binary failures. Keep the binary
|
|
501
|
-
* docker-shaped, or update the ENOENT message before adding a non-docker
|
|
502
|
-
* call site.
|
|
503
|
-
*/
|
|
504
|
-
async function spawnForeground(cmd, args, options = {}) {
|
|
505
|
-
const env = options.env ? mergeEnv(options.env) : void 0;
|
|
506
|
-
return new Promise((resolve, reject) => {
|
|
507
|
-
const child = spawn(cmd, args, {
|
|
508
|
-
cwd: options.cwd,
|
|
509
|
-
env,
|
|
510
|
-
stdio: "inherit"
|
|
511
|
-
});
|
|
512
|
-
child.once("error", (err) => {
|
|
513
|
-
if (err.code === "ENOENT") {
|
|
514
|
-
const usingOverride = process.env["CDK_DOCKER"] === cmd && cmd !== "docker";
|
|
515
|
-
reject(/* @__PURE__ */ new Error(usingOverride ? `Failed to find and execute '${cmd}' (resolved via CDK_DOCKER). Install '${cmd}' or unset CDK_DOCKER to fall back to 'docker'.` : `Failed to find and execute '${cmd}'. Install Docker (or set the 'CDK_DOCKER' environment variable to a compatible binary such as podman / finch).`));
|
|
516
|
-
} else reject(/* @__PURE__ */ new Error(`${cmd} failed: ${err.message}`));
|
|
517
|
-
});
|
|
518
|
-
child.once("close", (code) => {
|
|
519
|
-
if (code === 0) resolve();
|
|
520
|
-
else reject(/* @__PURE__ */ new Error(`${cmd} exited with code ${code}`));
|
|
521
|
-
});
|
|
522
|
-
});
|
|
523
|
-
}
|
|
524
|
-
/**
|
|
525
|
-
* Format the stderr from a failed `docker login` so the surfaced cdk-local
|
|
526
|
-
* error gives the user an actionable workaround when the underlying
|
|
527
|
-
* failure is a credential-helper persistence bug (which has nothing to
|
|
528
|
-
* do with cdk-local, AWS, or IAM perms — the docker CLI itself fails to
|
|
529
|
-
* save the auth token to the platform's credential store). The most
|
|
530
|
-
* common shape is `osxkeychain` on macOS rejecting an overwrite for
|
|
531
|
-
* an existing entry, but `wincred` (Windows), `pass` (Linux), and
|
|
532
|
-
* `secretservice` (Linux) hit the same class of `Error saving
|
|
533
|
-
* credentials` failure, so the rewritten message stays platform-
|
|
534
|
-
* agnostic — `docker logout <endpoint>` is the correct recovery on
|
|
535
|
-
* every backend.
|
|
536
|
-
*
|
|
537
|
-
* Detected docker / docker-credential-* output patterns:
|
|
538
|
-
* - `error storing credentials - err: exit status 1, out: \`The
|
|
539
|
-
* specified item already exists in the keychain.\`` (osxkeychain)
|
|
540
|
-
* - `Error saving credentials: ...` (any backend)
|
|
541
|
-
*
|
|
542
|
-
* Non-matching failures (genuine IAM / network / endpoint problems)
|
|
543
|
-
* pass through with just the stderr trimmed — the original message
|
|
544
|
-
* stays load-bearing for diagnosis.
|
|
545
|
-
*/
|
|
546
|
-
function formatDockerLoginError(stderr, endpoint) {
|
|
547
|
-
const trimmed = stderr.trim();
|
|
548
|
-
if (trimmed.includes("already exists in the keychain") || trimmed.includes("Error saving credentials")) return `docker's credential helper (osxkeychain on macOS / wincred on Windows / pass / secretservice on Linux) failed to persist the ECR auth token. The "already exists in the keychain" / "Error saving credentials" output is a known docker-credential-helpers issue — unrelated to ${getEmbedConfig().productName}, AWS credentials, or IAM perms. Quick fix: run \`docker logout ${endpoint}\` to clear the stale entry, then retry the ${getEmbedConfig().productName} command. Permanent fix: edit ~/.docker/config.json and remove (or empty) the platform-specific "credsStore" entry (e.g. "osxkeychain" → "" or "desktop" on macOS Docker Desktop). Original docker stderr: ${trimmed}`;
|
|
549
|
-
return trimmed;
|
|
550
|
-
}
|
|
551
|
-
function mergeEnv(overrides) {
|
|
552
|
-
const merged = { ...process.env };
|
|
553
|
-
for (const [k, v] of Object.entries(overrides)) if (v === void 0) delete merged[k];
|
|
554
|
-
else merged[k] = v;
|
|
555
|
-
return merged;
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
//#endregion
|
|
559
|
-
export { runDockerForeground as a, warnFinchArgvExposure as c, getEmbedConfig as d, resetEmbedConfig as f, getDockerCmd as i, getLogger as l, finchSecretArgvRefusal as n, runDockerStreaming as o, setEmbedConfig as p, formatDockerLoginError as r, spawnStreaming as s, docker_cmd_exports as t, resolveConfiguredLogLevel as u };
|
|
560
|
-
//# sourceMappingURL=docker-cmd-SocNyC9q.js.map
|