container updates respect the new image's command/entrypoint

This commit is contained in:
jarek committed 2026-08-09 10:31:28 +02:00
1 parent 1c1d7979d7
commit d33185d1fa
4 files changed
+292 -90

No files matched your search

+33
View File
@@ -33,6 +33,39 @@
export interface ImageEnvLabels {
Env?: string[];
Labels?: Record<string, string>;
/** The image's baked CMD / ENTRYPOINT, for the same value-aware rebase (#1371). */
Cmd?: string[] | null;
Entrypoint?: string[] | null;
}
/**
* Rebase a container's Cmd/Entrypoint onto the new image (#1371). Same flattening problem
* as Env/Labels: a container's Config.Cmd is set at create time whether it came from the
* image's CMD or a user `command:` override - spreading it verbatim onto a new image freezes
* the OLD image's command, so a container can start with a command the NEW image no longer
* provides (missing entrypoint script -> exit 127 -> restart loop).
*
* The recoverable signal is the same VALUE comparison:
* container == oldImage -> came from the old image, user never overrode it -> adopt newImage
* container != oldImage -> a real user/Compose override -> keep container
* Cmd/Entrypoint are ordered arrays, so equality is order-sensitive (JSON compare). When the
* old image value is unknown (null/undefined - caller couldn't inspect it) we KEEP the
* container's value (verbatim fallback), exactly as the env/label rebase does.
*/
export function rebaseCommand(
containerValue: string[] | null | undefined,
oldImageValue: string[] | null | undefined,
newImageValue: string[] | null | undefined
): { value: string[] | null | undefined; adopted: boolean } {
// Old image not inspectable -> can't tell image-default from user override -> keep verbatim.
if (oldImageValue == null) return { value: containerValue, adopted: false };
const eq = (a: string[] | null | undefined, b: string[] | null | undefined) =>
JSON.stringify(a ?? null) === JSON.stringify(b ?? null);
// User overrode it (differs from the old image's baked value) -> keep the override.
if (!eq(containerValue, oldImageValue)) return { value: containerValue, adopted: false };
// Image-baked and untouched -> follow the new image. (Same value -> no-op, adopted:false.)
if (eq(newImageValue, oldImageValue)) return { value: containerValue, adopted: false };
return { value: newImageValue, adopted: true };
}
/**