From a80d5a004fa00de292f7326a961b37799fa9156b Mon Sep 17 00:00:00 2001 From: Chaitanya Mishra Date: Tue, 13 Jan 2026 20:08:31 +0530 Subject: [PATCH] wrangler: handle registry ports in digest checks --- .changeset/spicy-buttons-heal.md | 9 +++++ .../src/__tests__/cloudchamber/build.test.ts | 36 +++++++++++++++++++ packages/wrangler/src/cloudchamber/build.ts | 15 +++++--- 3 files changed, 56 insertions(+), 4 deletions(-) create mode 100644 .changeset/spicy-buttons-heal.md diff --git a/.changeset/spicy-buttons-heal.md b/.changeset/spicy-buttons-heal.md new file mode 100644 index 000000000000..02f0504a07ca --- /dev/null +++ b/.changeset/spicy-buttons-heal.md @@ -0,0 +1,9 @@ +--- +"wrangler": patch +--- + +Handle registry ports when matching container image digests + +Wrangler now strips tags without breaking registry ports when comparing local +images to remote digests. This prevents unnecessary pushes for tags like +`localhost:5000/app:tag`. diff --git a/packages/wrangler/src/__tests__/cloudchamber/build.test.ts b/packages/wrangler/src/__tests__/cloudchamber/build.test.ts index 4a40318044f4..270a55cad2dc 100644 --- a/packages/wrangler/src/__tests__/cloudchamber/build.test.ts +++ b/packages/wrangler/src/__tests__/cloudchamber/build.test.ts @@ -331,6 +331,42 @@ describe("buildAndMaybePush", () => { expect(dockerLoginImageRegistry).toHaveBeenCalledOnce(); }); + it("should match digests for images with registry ports", async () => { + vi.mocked(runDockerCmd).mockResolvedValueOnce({ + abort: () => {}, + ready: Promise.resolve({ aborted: false }), + }); + vi.mocked(dockerImageInspect).mockReset(); + vi.mocked(dockerImageInspect) + .mockResolvedValueOnce( + '["localhost:5000/test-app@sha256:three"]' + ) + .mockResolvedValueOnce("53387881 2"); + vi.mocked(runDockerCmdWithOutput).mockReset(); + vi.mocked(runDockerCmdWithOutput).mockImplementationOnce(() => { + return '{"Descriptor":{"digest":"sha256:three"}}'; + }); + + await runWrangler( + "containers build ./container-context -t localhost:5000/test-app:tag -p" + ); + + expect(runDockerCmdWithOutput).toHaveBeenCalledOnce(); + expect(runDockerCmdWithOutput).toHaveBeenCalledWith("docker", [ + "manifest", + "inspect", + "-v", + "localhost:5000/test-app@sha256:three", + ]); + expect(runDockerCmd).toHaveBeenCalledTimes(1); + expect(runDockerCmd).toHaveBeenCalledWith("docker", [ + "image", + "rm", + "localhost:5000/test-app:tag", + ]); + expect(dockerLoginImageRegistry).toHaveBeenCalledOnce(); + }); + it("should be able to build image and not push", async () => { await runWrangler("containers build ./container-context -t test-app"); expect(dockerBuild).toHaveBeenCalledTimes(1); diff --git a/packages/wrangler/src/cloudchamber/build.ts b/packages/wrangler/src/cloudchamber/build.ts index b55569d491e3..ff16450a3078 100644 --- a/packages/wrangler/src/cloudchamber/build.ts +++ b/packages/wrangler/src/cloudchamber/build.ts @@ -31,6 +31,14 @@ import type { } from "@cloudflare/containers-shared"; import type { Config } from "@cloudflare/workers-utils"; +function stripImageTag(image: string): string { + const [name] = image.split("@"); + const lastColon = name.lastIndexOf(":"); + const lastSlash = name.lastIndexOf("/"); + + return lastColon > lastSlash ? name.slice(0, lastColon) : name; +} + export function buildYargs(yargs: CommonYargsArgv) { return yargs .positional("PATH", { @@ -162,10 +170,9 @@ export async function buildAndMaybePush( ); } - const repositoryOnly = resolveImageName( - account.external_account_id, - imageTag - ).split(":")[0]; + const repositoryOnly = stripImageTag( + resolveImageName(account.external_account_id, imageTag) + ); logger.debug("respositoryOnly:", repositoryOnly);