cdk-local 0.72.0 → 0.73.0
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 +16 -0
- package/dist/cli.js +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/internal.d.ts +23 -3
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +2 -2
- package/dist/{local-list-D-TP9Cy0.d.ts → local-list-8PbawOd2.d.ts} +2 -2
- package/dist/{local-list-D-TP9Cy0.d.ts.map → local-list-8PbawOd2.d.ts.map} +1 -1
- package/dist/{local-list-BOmsOq0r.js → local-list-Ch0RglJl.js} +271 -33
- package/dist/{local-list-BOmsOq0r.js.map → local-list-Ch0RglJl.js.map} +1 -1
- package/package.json +1 -1
|
@@ -24248,6 +24248,42 @@ function describePinnedImageUri(service) {
|
|
|
24248
24248
|
if (image.kind === "cdk-asset") return void 0;
|
|
24249
24249
|
return image.uri;
|
|
24250
24250
|
}
|
|
24251
|
+
/**
|
|
24252
|
+
* Issue #242 / N1 — dedupe pinned-target detection across the two
|
|
24253
|
+
* sites that need it during a `cdkl start-service` / `cdkl start-alb`
|
|
24254
|
+
* boot:
|
|
24255
|
+
*
|
|
24256
|
+
* 1. The `--image-override` engine's pre-boot resolution
|
|
24257
|
+
* ({@link resolveImageOverrides}) — needs the pinned set + per-
|
|
24258
|
+
* target deployed-registry URI labels so the picker + boot-prompt
|
|
24259
|
+
* hint can name the image the user is overriding.
|
|
24260
|
+
* 2. The post-boot WARN loop in `runEcsServiceEmulator` — surfaces a
|
|
24261
|
+
* per-target WARN naming the deployed-registry URI for every
|
|
24262
|
+
* pinned target the override engine did NOT cover.
|
|
24263
|
+
*
|
|
24264
|
+
* Before this helper both sites re-walked `isLocalCdkAssetImage` +
|
|
24265
|
+
* `describePinnedImageUri` independently. The semantic computation is
|
|
24266
|
+
* identical — given a `ResolvedEcsService`, decide pinned vs local-
|
|
24267
|
+
* asset and (for pinned targets) surface the URI label. Hoisting the
|
|
24268
|
+
* walk here keeps the two call sites in lock-step and gives both a
|
|
24269
|
+
* single test surface.
|
|
24270
|
+
*
|
|
24271
|
+
* Returns one entry per pinned target in the input's iteration order.
|
|
24272
|
+
* Targets whose representative image is a local CDK asset are
|
|
24273
|
+
* filtered out — the caller never sees them.
|
|
24274
|
+
*/
|
|
24275
|
+
function listPinnedTargets(resolvedServices) {
|
|
24276
|
+
const out = [];
|
|
24277
|
+
for (const { target, service } of resolvedServices) {
|
|
24278
|
+
if (isLocalCdkAssetImage(service)) continue;
|
|
24279
|
+
const label = describePinnedImageUri(service);
|
|
24280
|
+
out.push({
|
|
24281
|
+
target,
|
|
24282
|
+
...label !== void 0 && { label }
|
|
24283
|
+
});
|
|
24284
|
+
}
|
|
24285
|
+
return out;
|
|
24286
|
+
}
|
|
24251
24287
|
|
|
24252
24288
|
//#endregion
|
|
24253
24289
|
//#region src/local/image-override-engine.ts
|
|
@@ -24274,16 +24310,76 @@ var ImageOverrideError = class ImageOverrideError extends CdkLocalError {
|
|
|
24274
24310
|
* - `--image-override <svc>=<dockerfile>` -> `explicit.set(svc, dockerfile)`
|
|
24275
24311
|
* - `--image-override <dockerfile>` (no `=`) -> `pickerPaths.push(...)`
|
|
24276
24312
|
* - `--image-build-arg KEY=VAL` -> `globals.buildArgs.set('KEY', 'VAL')`
|
|
24313
|
+
* - `--image-build-arg <svc>:KEY=VAL` -> `perService(svc).buildArgs.set('KEY', 'VAL')` (issue #240)
|
|
24277
24314
|
* - `--image-build-secret id=src` -> `globals.buildSecrets.set('id', 'src')`
|
|
24315
|
+
* - `--image-build-secret <svc>:id=src` -> `perService(svc).buildSecrets.set('id', 'src')` (issue #240)
|
|
24278
24316
|
* - `--image-target <stage>` -> `globals.targetStage = '<stage>'`
|
|
24317
|
+
* - `--image-target <svc>=<stage>` -> `perService(svc).targetStage = '<stage>'` (issue #240)
|
|
24318
|
+
*
|
|
24319
|
+
* Per-service syntax convention (issue #240):
|
|
24320
|
+
* - Flags whose payload already contains `=` (build-arg, build-secret)
|
|
24321
|
+
* use `:` to separate the service prefix from the `<key>=<value>`
|
|
24322
|
+
* payload — `<svc>:KEY=VAL`. The FIRST `:` before the FIRST `=` (if
|
|
24323
|
+
* any) is treated as the prefix delimiter.
|
|
24324
|
+
* - Flags whose payload is a single token (target) use `=` to separate
|
|
24325
|
+
* the service prefix from the stage — `<svc>=stage`. Matches the
|
|
24326
|
+
* `--image-override <svc>=<dockerfile>` convention.
|
|
24327
|
+
* - Per-service entries override the global entry per-key when both
|
|
24328
|
+
* forms set the same key on the same target. Globals still apply to
|
|
24329
|
+
* every OTHER overridden target.
|
|
24279
24330
|
*
|
|
24280
24331
|
* Collision detection: a service target named more than once in
|
|
24281
24332
|
* `--image-override <svc>=...` is an error (last-write-wins would
|
|
24282
24333
|
* silently drop the earlier mapping; explicit error is clearer).
|
|
24334
|
+
* Repeated per-service `--image-build-arg <svc>:KEY=...` entries on
|
|
24335
|
+
* the same `<svc>:KEY` pair are last-write-wins (the same shape the
|
|
24336
|
+
* global form already uses for repeated `KEY=...` entries — both
|
|
24337
|
+
* forms behave identically inside their respective bucket).
|
|
24338
|
+
*
|
|
24339
|
+
* Empty-value semantics:
|
|
24340
|
+
* - `--image-build-arg KEY=` (empty value) is ACCEPTED. The empty
|
|
24341
|
+
* string is forwarded verbatim to `docker build --build-arg KEY=`,
|
|
24342
|
+
* which docker itself accepts (the canonical way to unset a
|
|
24343
|
+
* Dockerfile `ARG`'s default). `KEY=` (empty key) is rejected.
|
|
24344
|
+
* Per-service form `<svc>:KEY=` is similarly accepted (same empty-
|
|
24345
|
+
* value semantics applied to the per-service entry).
|
|
24346
|
+
* - `--image-target ""` (empty value) is REJECTED — an empty
|
|
24347
|
+
* `--target` is meaningless to `docker build` (`--target` is a
|
|
24348
|
+
* single stage name, not a list). Per-service `<svc>=` is similarly
|
|
24349
|
+
* rejected (empty stage).
|
|
24350
|
+
* - `--image-override <svc>=` and `--image-override =<dockerfile>`
|
|
24351
|
+
* are REJECTED — both halves must be non-empty.
|
|
24352
|
+
* - `--image-build-secret id=` and `--image-build-secret =src` are
|
|
24353
|
+
* REJECTED — both halves must be non-empty. Per-service form
|
|
24354
|
+
* `<svc>:id=` and `<svc>:=src` are similarly rejected.
|
|
24283
24355
|
*/
|
|
24284
24356
|
function parseImageOverrideFlags(input) {
|
|
24285
24357
|
const explicit = /* @__PURE__ */ new Map();
|
|
24286
24358
|
const pickerPaths = [];
|
|
24359
|
+
const perService = /* @__PURE__ */ new Map();
|
|
24360
|
+
const getPerSvc = (svc) => {
|
|
24361
|
+
let entry = perService.get(svc);
|
|
24362
|
+
if (!entry) {
|
|
24363
|
+
entry = {
|
|
24364
|
+
buildArgs: /* @__PURE__ */ new Map(),
|
|
24365
|
+
buildSecrets: /* @__PURE__ */ new Map()
|
|
24366
|
+
};
|
|
24367
|
+
perService.set(svc, entry);
|
|
24368
|
+
}
|
|
24369
|
+
return entry;
|
|
24370
|
+
};
|
|
24371
|
+
const splitPerServicePrefix = (raw) => {
|
|
24372
|
+
const colon = raw.indexOf(":");
|
|
24373
|
+
const eq = raw.indexOf("=");
|
|
24374
|
+
if (colon < 0) return null;
|
|
24375
|
+
if (eq >= 0 && eq < colon) return null;
|
|
24376
|
+
const svc = raw.slice(0, colon).trim();
|
|
24377
|
+
if (!svc) return null;
|
|
24378
|
+
return {
|
|
24379
|
+
svc,
|
|
24380
|
+
rest: raw.slice(colon + 1)
|
|
24381
|
+
};
|
|
24382
|
+
};
|
|
24287
24383
|
for (const raw of input.imageOverride ?? []) {
|
|
24288
24384
|
const eq = raw.indexOf("=");
|
|
24289
24385
|
if (eq < 0) {
|
|
@@ -24300,6 +24396,16 @@ function parseImageOverrideFlags(input) {
|
|
|
24300
24396
|
}
|
|
24301
24397
|
const buildArgs = /* @__PURE__ */ new Map();
|
|
24302
24398
|
for (const raw of input.imageBuildArg ?? []) {
|
|
24399
|
+
const prefix = splitPerServicePrefix(raw);
|
|
24400
|
+
if (prefix) {
|
|
24401
|
+
const eq = prefix.rest.indexOf("=");
|
|
24402
|
+
if (eq <= 0) throw new ImageOverrideError(`Invalid --image-build-arg value "${raw}": expected <service>:KEY=VAL.`);
|
|
24403
|
+
const key = prefix.rest.slice(0, eq).trim();
|
|
24404
|
+
const value = prefix.rest.slice(eq + 1);
|
|
24405
|
+
if (!key) throw new ImageOverrideError(`Invalid --image-build-arg value "${raw}": empty key (after service prefix).`);
|
|
24406
|
+
getPerSvc(prefix.svc).buildArgs.set(key, value);
|
|
24407
|
+
continue;
|
|
24408
|
+
}
|
|
24303
24409
|
const eq = raw.indexOf("=");
|
|
24304
24410
|
if (eq <= 0) throw new ImageOverrideError(`Invalid --image-build-arg value "${raw}": expected KEY=VAL.`);
|
|
24305
24411
|
const key = raw.slice(0, eq).trim();
|
|
@@ -24309,6 +24415,17 @@ function parseImageOverrideFlags(input) {
|
|
|
24309
24415
|
}
|
|
24310
24416
|
const buildSecrets = /* @__PURE__ */ new Map();
|
|
24311
24417
|
for (const raw of input.imageBuildSecret ?? []) {
|
|
24418
|
+
const prefix = splitPerServicePrefix(raw);
|
|
24419
|
+
if (prefix) {
|
|
24420
|
+
const eq = prefix.rest.indexOf("=");
|
|
24421
|
+
if (eq <= 0) throw new ImageOverrideError(`Invalid --image-build-secret value "${raw}": expected <service>:id=src (e.g. AppService:npmrc=./.npmrc).`);
|
|
24422
|
+
const id = prefix.rest.slice(0, eq).trim();
|
|
24423
|
+
const src = prefix.rest.slice(eq + 1).trim();
|
|
24424
|
+
if (!id || !src) throw new ImageOverrideError(`Invalid --image-build-secret value "${raw}": id and src must both be non-empty (after service prefix).`);
|
|
24425
|
+
const absSrcPS = isAbsolute(src) ? src : resolve(process.cwd(), src);
|
|
24426
|
+
getPerSvc(prefix.svc).buildSecrets.set(id, absSrcPS);
|
|
24427
|
+
continue;
|
|
24428
|
+
}
|
|
24312
24429
|
const eq = raw.indexOf("=");
|
|
24313
24430
|
if (eq <= 0) throw new ImageOverrideError(`Invalid --image-build-secret value "${raw}": expected id=src (e.g. npmrc=./.npmrc).`);
|
|
24314
24431
|
const id = raw.slice(0, eq).trim();
|
|
@@ -24321,14 +24438,26 @@ function parseImageOverrideFlags(input) {
|
|
|
24321
24438
|
buildArgs,
|
|
24322
24439
|
buildSecrets
|
|
24323
24440
|
};
|
|
24324
|
-
|
|
24325
|
-
|
|
24326
|
-
|
|
24441
|
+
const imageTargetList = input.imageTarget === void 0 ? [] : Array.isArray(input.imageTarget) ? input.imageTarget : [input.imageTarget];
|
|
24442
|
+
for (const raw of imageTargetList) {
|
|
24443
|
+
if (raw === void 0 || raw === null) continue;
|
|
24444
|
+
if (!raw) throw new ImageOverrideError("Invalid --image-target value: empty string.");
|
|
24445
|
+
const eq = raw.indexOf("=");
|
|
24446
|
+
if (eq < 0) {
|
|
24447
|
+
globals.targetStage = raw.trim();
|
|
24448
|
+
continue;
|
|
24449
|
+
}
|
|
24450
|
+
const svc = raw.slice(0, eq).trim();
|
|
24451
|
+
const stage = raw.slice(eq + 1).trim();
|
|
24452
|
+
if (!svc) throw new ImageOverrideError(`Invalid --image-target value "${raw}": left side (service target) is empty.`);
|
|
24453
|
+
if (!stage) throw new ImageOverrideError(`Invalid --image-target value "${raw}": right side (stage) is empty.`);
|
|
24454
|
+
getPerSvc(svc).targetStage = stage;
|
|
24327
24455
|
}
|
|
24328
24456
|
return {
|
|
24329
24457
|
explicit,
|
|
24330
24458
|
pickerPaths,
|
|
24331
|
-
globals
|
|
24459
|
+
globals,
|
|
24460
|
+
perService
|
|
24332
24461
|
};
|
|
24333
24462
|
}
|
|
24334
24463
|
/**
|
|
@@ -24370,7 +24499,7 @@ async function resolveImageOverrides(args) {
|
|
|
24370
24499
|
logger.warn(`--image-override: service '${svc}' is not in the pinned-target set (no deployed-registry pin detected for it). Mapping ignored.`);
|
|
24371
24500
|
continue;
|
|
24372
24501
|
}
|
|
24373
|
-
out.set(svc, makeEntryFromPath(dockerfileRaw, rawFlags
|
|
24502
|
+
out.set(svc, makeEntryFromPath(dockerfileRaw, svc, rawFlags, cwd));
|
|
24374
24503
|
}
|
|
24375
24504
|
if (rawFlags.pickerPaths.length > 0) if (!(isInteractive() && noInteractive !== true)) logger.warn("--image-override <dockerfile> (picker form) requires an interactive TTY and --no-interactive-overrides not to be set. Skipping picker-form mapping(s).");
|
|
24376
24505
|
else for (const dockerfileRaw of rawFlags.pickerPaths) {
|
|
@@ -24395,8 +24524,7 @@ async function resolveImageOverrides(args) {
|
|
|
24395
24524
|
logger.warn(`--image-override ${dockerfileRaw}: empty selection. Skipped.`);
|
|
24396
24525
|
continue;
|
|
24397
24526
|
}
|
|
24398
|
-
const
|
|
24399
|
-
for (const t of chosen) out.set(t, entry);
|
|
24527
|
+
for (const t of chosen) out.set(t, makeEntryFromPath(dockerfileRaw, t, rawFlags, cwd));
|
|
24400
24528
|
}
|
|
24401
24529
|
if (args.interactiveBootPrompt === true && isInteractive() && noInteractive !== true) for (const target of pinnedTargets) {
|
|
24402
24530
|
if (out.has(target)) continue;
|
|
@@ -24410,28 +24538,61 @@ async function resolveImageOverrides(args) {
|
|
|
24410
24538
|
if (!value) continue;
|
|
24411
24539
|
const lower = value.toLowerCase();
|
|
24412
24540
|
if (lower === "n" || lower === "no") continue;
|
|
24413
|
-
out.set(target, makeEntryFromPath(value, rawFlags
|
|
24541
|
+
out.set(target, makeEntryFromPath(value, target, rawFlags, cwd));
|
|
24414
24542
|
}
|
|
24415
24543
|
return out;
|
|
24416
24544
|
}
|
|
24417
24545
|
/**
|
|
24418
|
-
*
|
|
24546
|
+
* Issue #240 — produce the EFFECTIVE per-target build inputs by layering
|
|
24547
|
+
* a per-service overlay on top of the global baseline. Per-service
|
|
24548
|
+
* entries override the global per-key (and per-service `targetStage`
|
|
24549
|
+
* overrides the global `targetStage`).
|
|
24550
|
+
*
|
|
24551
|
+
* The returned Maps are fresh copies — the caller may mutate the
|
|
24552
|
+
* resulting {@link ImageOverrideEntry}'s Maps without bleeding back
|
|
24553
|
+
* into the shared global Maps inside {@link RawImageOverrideFlags}.
|
|
24554
|
+
* Per-service entries are merged AFTER globals so a key shared between
|
|
24555
|
+
* the two ends up with the per-service value (Map's `set` overwrites
|
|
24556
|
+
* on duplicate key).
|
|
24557
|
+
*/
|
|
24558
|
+
function mergeForService(serviceTarget, globals, perService) {
|
|
24559
|
+
const buildArgs = new Map(globals.buildArgs);
|
|
24560
|
+
const buildSecrets = new Map(globals.buildSecrets);
|
|
24561
|
+
let targetStage = globals.targetStage;
|
|
24562
|
+
const overlay = perService.get(serviceTarget);
|
|
24563
|
+
if (overlay) {
|
|
24564
|
+
for (const [k, v] of overlay.buildArgs.entries()) buildArgs.set(k, v);
|
|
24565
|
+
for (const [k, v] of overlay.buildSecrets.entries()) buildSecrets.set(k, v);
|
|
24566
|
+
if (overlay.targetStage !== void 0) targetStage = overlay.targetStage;
|
|
24567
|
+
}
|
|
24568
|
+
return {
|
|
24569
|
+
buildArgs,
|
|
24570
|
+
buildSecrets,
|
|
24571
|
+
...targetStage !== void 0 && { targetStage }
|
|
24572
|
+
};
|
|
24573
|
+
}
|
|
24574
|
+
/**
|
|
24575
|
+
* Promote a per-target Dockerfile path + raw flags into a full
|
|
24419
24576
|
* {@link ImageOverrideEntry}. Resolves the path against `cwd`, asserts
|
|
24420
24577
|
* the Dockerfile exists and is a regular file (so the user sees
|
|
24421
24578
|
* "file not found" up-front rather than mid-`docker build`), and
|
|
24422
24579
|
* derives the build context as the Dockerfile's parent directory
|
|
24423
24580
|
* (the v1 simplification — custom contexts are tracked separately).
|
|
24581
|
+
*
|
|
24582
|
+
* Issue #240 — `serviceTarget` is now part of the entry-construction
|
|
24583
|
+
* input so the global + per-service merge runs per target.
|
|
24424
24584
|
*/
|
|
24425
|
-
function makeEntryFromPath(raw,
|
|
24585
|
+
function makeEntryFromPath(raw, serviceTarget, rawFlags, cwd) {
|
|
24426
24586
|
const abs = isAbsolute(raw) ? raw : resolve(cwd, raw);
|
|
24427
24587
|
if (!existsSync(abs)) throw new ImageOverrideError(`--image-override: Dockerfile '${raw}' does not exist (resolved to '${abs}').`);
|
|
24428
24588
|
if (!statSync(abs).isFile()) throw new ImageOverrideError(`--image-override: '${raw}' is not a regular file (resolved to '${abs}').`);
|
|
24589
|
+
const merged = mergeForService(serviceTarget, rawFlags.globals, rawFlags.perService);
|
|
24429
24590
|
return {
|
|
24430
24591
|
dockerfile: abs,
|
|
24431
24592
|
contextDir: dirname(abs),
|
|
24432
|
-
buildArgs:
|
|
24433
|
-
buildSecrets:
|
|
24434
|
-
...
|
|
24593
|
+
buildArgs: merged.buildArgs,
|
|
24594
|
+
buildSecrets: merged.buildSecrets,
|
|
24595
|
+
...merged.targetStage !== void 0 && { targetStage: merged.targetStage }
|
|
24435
24596
|
};
|
|
24436
24597
|
}
|
|
24437
24598
|
/**
|
|
@@ -24493,10 +24654,26 @@ function buildImageOverrideTag(serviceTarget, entry) {
|
|
|
24493
24654
|
* order maximizes cache reuse). On any failure the function rejects
|
|
24494
24655
|
* with {@link ImageOverrideError}; the emulator surfaces this before
|
|
24495
24656
|
* any container is started.
|
|
24657
|
+
*
|
|
24658
|
+
* Partial-failure rollback (issue #242 / N3): when build N (1-indexed)
|
|
24659
|
+
* fails, the 1..(N-1) successfully built local-only tags from THIS run
|
|
24660
|
+
* are best-effort `docker image rm`'d before re-throwing. Not a leak
|
|
24661
|
+
* (the tags are deterministic per
|
|
24662
|
+
* {@link buildImageOverrideTag} so a re-run collides safely with any
|
|
24663
|
+
* leftover), but the cleanup keeps the local Docker daemon tidy after
|
|
24664
|
+
* a failed boot — disk-pressure scenarios that occasionally surface
|
|
24665
|
+
* on dev laptops never accumulate orphan override images across a
|
|
24666
|
+
* day of iterations. Cleanup failures are logged at `debug` (another
|
|
24667
|
+
* container could be using the image, the image could already have
|
|
24668
|
+
* been removed by a parallel `docker system prune`, etc.) and the
|
|
24669
|
+
* loop continues so one un-removable tag doesn't shadow the others.
|
|
24670
|
+
* The originating {@link ImageOverrideError} is always the thrown
|
|
24671
|
+
* value — a cleanup-step error never replaces it.
|
|
24496
24672
|
*/
|
|
24497
24673
|
async function runImageOverrideBuilds(overrides) {
|
|
24498
24674
|
const logger = getLogger();
|
|
24499
24675
|
const out = /* @__PURE__ */ new Map();
|
|
24676
|
+
const builtTags = [];
|
|
24500
24677
|
for (const [target, entry] of overrides.entries()) {
|
|
24501
24678
|
const tag = buildImageOverrideTag(target, entry);
|
|
24502
24679
|
const args = [
|
|
@@ -24518,12 +24695,69 @@ async function runImageOverrideBuilds(overrides) {
|
|
|
24518
24695
|
});
|
|
24519
24696
|
} catch (err) {
|
|
24520
24697
|
const e = err;
|
|
24521
|
-
|
|
24698
|
+
const wrapped = new ImageOverrideError(`docker build failed for --image-override '${target}' (Dockerfile=${entry.dockerfile}): ` + (e.stderr?.trim() || e.message || String(err)));
|
|
24699
|
+
for (const priorTag of builtTags) try {
|
|
24700
|
+
await runDockerStreaming([
|
|
24701
|
+
"image",
|
|
24702
|
+
"rm",
|
|
24703
|
+
priorTag
|
|
24704
|
+
], {});
|
|
24705
|
+
} catch (rmErr) {
|
|
24706
|
+
logger.debug(`Image-override rollback: \`docker image rm ${priorTag}\` failed: ${rmErr instanceof Error ? rmErr.message : String(rmErr)}.`);
|
|
24707
|
+
}
|
|
24708
|
+
throw wrapped;
|
|
24522
24709
|
}
|
|
24523
24710
|
out.set(target, tag);
|
|
24711
|
+
builtTags.push(tag);
|
|
24524
24712
|
}
|
|
24525
24713
|
return out;
|
|
24526
24714
|
}
|
|
24715
|
+
/**
|
|
24716
|
+
* Issue #240 — orphan validation. A per-service build-input flag
|
|
24717
|
+
* (`--image-build-arg <svc>:KEY=VAL`, `--image-build-secret
|
|
24718
|
+
* <svc>:id=src`, or `--image-target <svc>=stage`) names a service that
|
|
24719
|
+
* MUST appear in the resolved override map — otherwise the per-service
|
|
24720
|
+
* value silently gets discarded because no `docker build` runs for
|
|
24721
|
+
* that service.
|
|
24722
|
+
*
|
|
24723
|
+
* Called AFTER {@link resolveImageOverrides} (Stage 3 boot prompt
|
|
24724
|
+
* complete) so a service the user added via the prompt counts as
|
|
24725
|
+
* covered. A still-orphan per-service flag at this point means the
|
|
24726
|
+
* user typo'd the service name OR forgot a corresponding
|
|
24727
|
+
* `--image-override <svc>=<dockerfile>` mapping (and either chose `N`
|
|
24728
|
+
* in the boot prompt or ran with `--no-interactive-overrides`).
|
|
24729
|
+
*
|
|
24730
|
+
* Throws {@link LocalStartServiceError} naming every offending
|
|
24731
|
+
* `<flag>` + `<service>` pair so the user can fix all in one go
|
|
24732
|
+
* (matches the `enforceStrictOverrides` pattern). Mutates nothing.
|
|
24733
|
+
*
|
|
24734
|
+
* Host-side use case: cdkd and other shim hosts that wrap the engine
|
|
24735
|
+
* re-export this via `cdk-local/internal` and call it right after
|
|
24736
|
+
* their own `resolveImageOverrides` invocation to inherit the same
|
|
24737
|
+
* orphan-detection semantics.
|
|
24738
|
+
*
|
|
24739
|
+
* @param rawFlags Output of {@link parseImageOverrideFlags}.
|
|
24740
|
+
* @param resolvedOverrides Output of {@link resolveImageOverrides}
|
|
24741
|
+
* (after the boot prompt has run).
|
|
24742
|
+
*/
|
|
24743
|
+
function enforceImageOverrideOrphans(rawFlags, resolvedOverrides) {
|
|
24744
|
+
if (rawFlags.perService.size === 0) return;
|
|
24745
|
+
const offenders = [];
|
|
24746
|
+
for (const [svc, overlay] of rawFlags.perService.entries()) {
|
|
24747
|
+
if (resolvedOverrides.has(svc)) continue;
|
|
24748
|
+
if (overlay.buildArgs.size > 0) {
|
|
24749
|
+
const keys = Array.from(overlay.buildArgs.keys()).join(", ");
|
|
24750
|
+
offenders.push(`--image-build-arg ${svc}:${keys} references a service with no --image-override mapping.`);
|
|
24751
|
+
}
|
|
24752
|
+
if (overlay.buildSecrets.size > 0) {
|
|
24753
|
+
const ids = Array.from(overlay.buildSecrets.keys()).join(", ");
|
|
24754
|
+
offenders.push(`--image-build-secret ${svc}:${ids} references a service with no --image-override mapping.`);
|
|
24755
|
+
}
|
|
24756
|
+
if (overlay.targetStage !== void 0) offenders.push(`--image-target ${svc}=${overlay.targetStage} references a service with no --image-override mapping.`);
|
|
24757
|
+
}
|
|
24758
|
+
if (offenders.length === 0) return;
|
|
24759
|
+
throw new LocalStartServiceError(`Per-service image-override flag(s) target a service with no --image-override coverage. Add the matching --image-override <service>=<dockerfile>, drop the per-service flag, or fix the service name:\n ${offenders.join("\n ")}`);
|
|
24760
|
+
}
|
|
24527
24761
|
|
|
24528
24762
|
//#endregion
|
|
24529
24763
|
//#region src/cli/commands/ecs-service-emulator.ts
|
|
@@ -24676,15 +24910,15 @@ async function runEcsServiceEmulator(targets, options, strategy, extraStateProvi
|
|
|
24676
24910
|
logEndpointsBanner(perTarget, frontDoorServers, logger);
|
|
24677
24911
|
logger.info("Press ^C to shut down.");
|
|
24678
24912
|
const uncoveredPinnedTargets = [];
|
|
24679
|
-
|
|
24680
|
-
|
|
24681
|
-
|
|
24682
|
-
|
|
24683
|
-
|
|
24684
|
-
|
|
24685
|
-
const uriDisplay =
|
|
24686
|
-
logger.warn(`'${
|
|
24687
|
-
uncoveredPinnedTargets.push(
|
|
24913
|
+
const pinnedEntries = listPinnedTargets(perTarget.filter((pt) => pt.controller?.service).map((pt) => ({
|
|
24914
|
+
target: pt.boot.target,
|
|
24915
|
+
service: pt.controller.service
|
|
24916
|
+
})));
|
|
24917
|
+
for (const entry of pinnedEntries) {
|
|
24918
|
+
if (imageOverrideTags.has(entry.target)) continue;
|
|
24919
|
+
const uriDisplay = entry.label ? `\`${entry.label}\`` : "a deployed registry";
|
|
24920
|
+
logger.warn(`'${entry.target}': running image is pinned to a deployed registry (${uriDisplay}). To iterate on local source, either pass \`--image-override <dockerfile>\` (or \`<service>=<dockerfile>\`) to substitute a local build, or drop \`--from-cfn-stack\` and switch the CDK app to \`ContainerImage.fromAsset(...)\`.`);
|
|
24921
|
+
uncoveredPinnedTargets.push(entry.target);
|
|
24688
24922
|
}
|
|
24689
24923
|
enforceStrictOverrides(options.strictOverrides === true, uncoveredPinnedTargets);
|
|
24690
24924
|
if (options.watch === true && strategy.supportsWatch === true) {
|
|
@@ -25554,8 +25788,7 @@ async function resolveSharedSidecarCredentials(options) {
|
|
|
25554
25788
|
*/
|
|
25555
25789
|
async function resolveAndBuildImageOverrides(args) {
|
|
25556
25790
|
const { perTarget, stacks, options, extraStateProviders, logger } = args;
|
|
25557
|
-
const
|
|
25558
|
-
const pinnedLabels = /* @__PURE__ */ new Map();
|
|
25791
|
+
const resolvedForPeek = [];
|
|
25559
25792
|
for (const target of perTarget) {
|
|
25560
25793
|
const candidate = pickCandidateStack(parseEcsTarget(target).stackPattern, stacks);
|
|
25561
25794
|
const stateProvider = createLocalStateProvider(options, candidate?.stackName ?? "", await resolveCfnFallbackRegion(options, candidate?.region), extraStateProviders);
|
|
@@ -25571,18 +25804,22 @@ async function resolveAndBuildImageOverrides(args) {
|
|
|
25571
25804
|
if (stateProvider) stateProvider.dispose();
|
|
25572
25805
|
}
|
|
25573
25806
|
if (!service) continue;
|
|
25574
|
-
|
|
25575
|
-
|
|
25576
|
-
|
|
25577
|
-
|
|
25807
|
+
resolvedForPeek.push({
|
|
25808
|
+
target,
|
|
25809
|
+
service
|
|
25810
|
+
});
|
|
25578
25811
|
}
|
|
25812
|
+
const pinnedEntries = listPinnedTargets(resolvedForPeek);
|
|
25813
|
+
const pinnedTargets = pinnedEntries.map((e) => e.target);
|
|
25814
|
+
const pinnedLabels = /* @__PURE__ */ new Map();
|
|
25815
|
+
for (const entry of pinnedEntries) if (entry.label !== void 0) pinnedLabels.set(entry.target, entry.label);
|
|
25579
25816
|
const rawFlags = parseImageOverrideFlags({
|
|
25580
25817
|
...options.imageOverride && { imageOverride: options.imageOverride },
|
|
25581
25818
|
...options.imageBuildArg && { imageBuildArg: options.imageBuildArg },
|
|
25582
25819
|
...options.imageBuildSecret && { imageBuildSecret: options.imageBuildSecret },
|
|
25583
25820
|
...options.imageTarget && { imageTarget: options.imageTarget }
|
|
25584
25821
|
});
|
|
25585
|
-
if (pinnedTargets.length === 0 && rawFlags.explicit.size === 0 && rawFlags.pickerPaths.length === 0) return /* @__PURE__ */ new Map();
|
|
25822
|
+
if (pinnedTargets.length === 0 && rawFlags.explicit.size === 0 && rawFlags.pickerPaths.length === 0 && rawFlags.perService.size === 0) return /* @__PURE__ */ new Map();
|
|
25586
25823
|
const overrides = await resolveImageOverrides({
|
|
25587
25824
|
rawFlags,
|
|
25588
25825
|
pinnedTargets,
|
|
@@ -25590,6 +25827,7 @@ async function resolveAndBuildImageOverrides(args) {
|
|
|
25590
25827
|
interactiveBootPrompt: true,
|
|
25591
25828
|
noInteractive: options.interactiveOverrides === false
|
|
25592
25829
|
});
|
|
25830
|
+
enforceImageOverrideOrphans(rawFlags, overrides);
|
|
25593
25831
|
if (overrides.size === 0) return /* @__PURE__ */ new Map();
|
|
25594
25832
|
return runImageOverrideBuilds(overrides);
|
|
25595
25833
|
}
|
|
@@ -25639,7 +25877,7 @@ function addCommonEcsServiceOptions(cmd) {
|
|
|
25639
25877
|
* factories inherit the flag set without duplication.
|
|
25640
25878
|
*/
|
|
25641
25879
|
function addImageOverrideOptions(cmd) {
|
|
25642
|
-
return cmd.addOption(new Option("--image-override <service=dockerfile or dockerfile...>", "Replace a service target's deployed-registry image with a local `docker build` of the supplied Dockerfile (repeatable). Two forms: <service>=<dockerfile> binds the service explicitly; a bare <dockerfile> opens a multi-select picker against the still-uncovered pinned targets (one Dockerfile across N services). Mix freely. Lets `--from-cfn-stack` still reach real AWS state while you iterate locally on the application container.")).addOption(new Option("--image-build-arg <KEY=VAL...>", "Global `docker build --build-arg KEY=VAL` pair applied to every --image-override build (repeatable). Lets a CDK Dockerfile that branches on an ARG (env target, base image tag, package mirror) honor that ARG locally without editing the Dockerfile.")).addOption(new Option("--image-build-secret <id=src...>", "Global `docker build --secret id=<id>,src=<src>` entry applied to every --image-override build (repeatable). Enables `RUN --mount=type=secret,id=<id>` in the Dockerfile — the standard private-registry / npm-token recipe (e.g. `--image-build-secret npmrc=./.npmrc`).")).addOption(new Option("--image-target <stage
|
|
25880
|
+
return cmd.addOption(new Option("--image-override <service=dockerfile or dockerfile...>", "Replace a service target's deployed-registry image with a local `docker build` of the supplied Dockerfile (repeatable). Two forms: <service>=<dockerfile> binds the service explicitly; a bare <dockerfile> opens a multi-select picker against the still-uncovered pinned targets (one Dockerfile across N services). Mix freely. Lets `--from-cfn-stack` still reach real AWS state while you iterate locally on the application container.")).addOption(new Option("--image-build-arg <KEY=VAL...>", "Global `docker build --build-arg KEY=VAL` pair applied to every --image-override build (repeatable). Lets a CDK Dockerfile that branches on an ARG (env target, base image tag, package mirror) honor that ARG locally without editing the Dockerfile.")).addOption(new Option("--image-build-secret <id=src...>", "Global `docker build --secret id=<id>,src=<src>` entry applied to every --image-override build (repeatable). Enables `RUN --mount=type=secret,id=<id>` in the Dockerfile — the standard private-registry / npm-token recipe (e.g. `--image-build-secret npmrc=./.npmrc`).")).addOption(new Option("--image-target <stage or svc=stage...>", "Repeatable. Bare `<stage>` is a global --target for every --image-override build; `<service>=<stage>` (issue #240) scopes the --target to one overridden target so a monorepo with different multi-stage Dockerfiles per service can stop each at its own intermediate stage. Per-service form overrides the global on the named target.")).addOption(new Option("--no-interactive-overrides", "Suppress the interactive boot prompt that walks each pinned target asking for a Dockerfile path, and the multi-select prompt fired by `--image-override <dockerfile>` (picker form). Useful for CI / scripted invocations.")).addOption(new Option("--strict-overrides", "Fail fast at boot when any pinned target remains uncovered after `--image-override` + the boot prompt resolve. Off by default; the boot WARN per uncovered pinned target still fires regardless.").default(false));
|
|
25643
25881
|
}
|
|
25644
25882
|
|
|
25645
25883
|
//#endregion
|
|
@@ -26662,5 +26900,5 @@ function addListSpecificOptions(cmd) {
|
|
|
26662
26900
|
}
|
|
26663
26901
|
|
|
26664
26902
|
//#endregion
|
|
26665
|
-
export {
|
|
26666
|
-
//# sourceMappingURL=local-list-
|
|
26903
|
+
export { groupRoutesByServer as $, tryResolveImageFnJoin as $n, AGENTCORE_SESSION_ID_HEADER as $t, isLocalCdkAssetImage as A, collectSsmParameterRefs as An, buildCognitoJwksUrl as At, addInvokeAgentCoreSpecificOptions as B, pickRefLogicalId as Bn, isFunctionUrlOacFronted as Bt, buildImageOverrideTag as C, createLocalStateProvider as Cn, ConnectionRegistry as Ct, resolveImageOverrides as D, resolveCfnRegion as Dn, buildConnectEvent as Dt, parseImageOverrideFlags as E, resolveCfnFallbackRegion as En, parseConnectionsPath as Et, SOFT_RELOAD_COMPLETION_LOG_SUFFIX as F, listTargets as Fn, verifyJwtViaDiscovery as Ft, resolveApiTargetSubset as G, AGENTCORE_MCP_PROTOCOL as Gn, a2aInvokeOnce as Gt, addStartApiSpecificOptions as H, AGENTCORE_A2A_PROTOCOL as Hn, invokeAgentCoreWs as Ht, getContainerNetworkIp as I, discoverWebSocketApis as In, attachAuthorizers as It, attachStageContext as J, pickAgentCoreCandidateStack as Jn, MCP_PROTOCOL_VERSION as Jt, createAuthorizerCache as K, AGENTCORE_RUNTIME_TYPE as Kn, MCP_CONTAINER_PORT as Kt, addRunTaskSpecificOptions as L, discoverWebSocketApisOrThrow as Ln, applyCorsResponseHeaders as Lt, buildCloudMapIndex as M, resolveWatchConfig as Mn, createJwksCache as Mt, CloudMapRegistry as N, resolveSingleTarget as Nn, verifyCognitoJwt as Nt, runImageOverrideBuilds as O, resolveCfnStackName as On, buildDisconnectEvent as Ot, classifySourceChange as P, countTargets as Pn, verifyJwtAuthorizer as Pt, filterRoutesByApiIdentifiers as Q, substituteImagePlaceholders as Qn, signAgentCoreInvocation as Qt, createLocalRunTaskCommand as R, parseSelectionExpressionPath as Rn, buildCorsConfigByApiId as Rt, ImageOverrideError as S, LocalStateSourceError as Sn, bufferToBody as St, mergeForService as T, rejectExplicitCfnStackWithMultipleStacks as Tn, handleConnectionsRequest as Tt, createLocalStartApiCommand as U, AGENTCORE_AGUI_PROTOCOL as Un, A2A_CONTAINER_PORT as Ut, createLocalInvokeAgentCoreCommand as V, resolveLambdaArnIntrinsic as Vn, matchPreflight as Vt, createWatchPredicates as W, AGENTCORE_HTTP_PROTOCOL as Wn, A2A_PATH as Wt, availableApiIdentifiers as X, derivePseudoParametersFromRegion as Xn, parseSseForJsonRpc as Xt, buildStageMap as Y, resolveAgentCoreTarget as Yn, mcpInvokeOnce as Yt, filterRoutesByApiIdentifier as Z, formatStateRemedy as Zn, AGENTCORE_SIGV4_SERVICE as Zt, buildEcsImageResolutionContext as _, substituteAgainstStateAsync as _n, selectIntegrationResponse as _t, albStrategy as a, computeCodeImageTag as an, buildMethodArn as at, resolveSharedSidecarCredentials as b, resolveEnvVars as bn, HOST_GATEWAY_MIN_VERSION as bt, resolveAlbTarget as c, addInvokeSpecificOptions as cn, invokeRequestAuthorizer as ct, addStartServiceSpecificOptions as d, buildContainerImage as dn, translateLambdaResponse as dt, invokeAgentCore as en, LocalInvokeBuildError as er, readMtlsMaterialsFromDisk as et, createLocalStartServiceCommand as f, resolveRuntimeCodeMountPath as fn, applyAuthorizerOverlay as ft, addImageOverrideOptions as g, substituteAgainstState as gn, pickResponseTemplate as gt, addCommonEcsServiceOptions as h, EcsTaskResolutionError as hn, evaluateResponseParameters as ht, addAlbSpecificOptions as i, buildAgentCoreCodeImage as in, defaultCredentialsLoader as it, listPinnedTargets as j, resolveSsmParameters as jn, buildJwksUrlFromIssuer as jt, describePinnedImageUri as k, CfnLocalStateProvider as kn, buildMessageEvent as kt, isApplicationLoadBalancer as l, createLocalInvokeCommand as ln, invokeTokenAuthorizer as lt, MAX_TASKS_SUBNET_RANGE_CAP as m, resolveRuntimeImage as mn, buildRestV1Event as mt, createLocalListCommand as n, downloadAndExtractS3Bundle as nn, resolveSelectionExpression as nt, createLocalStartAlbCommand as o, renderCodeDockerfile as on, computeRequestIdentityHash as ot, serviceStrategy as p, resolveRuntimeFileExtension as pn, buildHttpApiV2Event as pt, createFileWatcher as q, AgentCoreResolutionError as qn, MCP_PATH as qt, formatTargetListing as r, SUPPORTED_CODE_RUNTIMES as rn, resolveServiceIntegrationParameters as rt, parseLbPortOverrides as s, toCmdArgv as sn, evaluateCachedLambdaPolicy as st, addListSpecificOptions as t, waitForAgentCorePing as tn, startApiServer as tt, resolveAlbFrontDoor as u, architectureToPlatform as un, matchRoute as ut, parseMaxTasks as v, substituteEnvVarsFromState as vn, tryParseStatus as vt, enforceImageOverrideOrphans as w, isCfnFlagPresent as wn, buildMgmtEndpointEnvUrl as wt, runEcsServiceEmulator as x, materializeLayerFromArn as xn, probeHostGatewaySupport as xt, parseRestartPolicy as y, substituteEnvVarsFromStateAsync as yn, VtlEvaluationError as yt, attachContainerLogStreamer as z, discoverRoutes as zn, buildCorsConfigFromCloudFrontChain as zt };
|
|
26904
|
+
//# sourceMappingURL=local-list-Ch0RglJl.js.map
|