Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"wrangler": patch
---

Fix `wrangler deploy` aborting in CI for autoconfigured projects

A recent change guarded non-interactive deploys against overwriting a same-named Worker whenever there was no config file naming it. This was too broad: a plain `wrangler deploy` run in CI without a config file (for example an autoconfigured project whose generated config PR has not been merged) would fail with "A Worker named ... already exists in your account", even though re-deploying to that Worker is the intended behaviour.

The guard is now limited to the Pages-to-Workers delegation, where the target name is a Pages project name that must not clobber an unrelated Worker. Plain deploys once again deploy normally.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { run } from "../../experimental-flags";
import { mockAccountId, mockApiToken } from "../helpers/mock-account-id";
import { mockConsoleMethods } from "../helpers/mock-console";
import { useMockIsTTY } from "../helpers/mock-istty";
import { mockUploadWorkerRequest } from "../helpers/mock-upload-worker";
import { mockSubDomainRequest } from "../helpers/mock-workers-subdomain";
import { createFetchResult, msw } from "../helpers/msw";
import { writeWorkerSource } from "../helpers/write-worker-source";

Expand Down Expand Up @@ -122,4 +124,39 @@ describe("deploy name-collision guard (non-interactive)", () => {
'A Worker named "existing-worker" already exists in your account.'
);
});

it("does not guard a plain non-interactive deploy (no delegation)", async ({
expect,
}) => {
// Regression test for #14312: a plain `wrangler deploy` in CI with no
// config file and no --name (e.g. an autoconfigured project whose
// generated config PR has not been merged) is routinely re-run against
// the same, already-deployed Worker. Only the Pages-to-Workers delegation
// guards name ownership, so this must deploy normally instead of aborting.
const args = delegatedDeployArgs({});
const config = readConfig(args, { useRedirectIfAvailable: true });
config.name = "existing-worker";
config.main = "index.js";
config.compatibility_date = "2024-01-01";
mockExistingWorker();
mockSubDomainRequest();
mockUploadWorkerRequest({ expectedScriptName: "existing-worker" });
// The existing Worker means deploy checks its latest deployment before
// overwriting; an empty list means there is nothing to confirm.
msw.use(
http.get(
"*/accounts/:accountId/workers/scripts/:scriptName/deployments",
() => HttpResponse.json(createFetchResult({ deployments: [] }))
)
);

await expect(
run(experimentalFlags, () =>
runDeployCommandHandler(args, {
config,
// pagesToWorkersDelegation defaults to false
})
)
).resolves.toBeUndefined();
});
});
28 changes: 13 additions & 15 deletions packages/wrangler/src/deploy/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,23 +125,21 @@ export async function runDeployCommandHandler(
// Capture whether this project can prove it owns the target Worker name,
// BEFORE autoconfig generates or rewrites the config. Ownership is proven by
// a config file that names the Worker; without one a same-named remote Worker
// is a probable collision rather than a redeploy. We only guard
// non-interactive deploys (agents, CI, the Pages-to-Workers delegation):
// there we cannot prompt to resolve the collision, and silently overwriting
// an unrelated Worker is the worst outcome. Interactive users pick the name
// at a prompt and keep the existing confirmation flow.
// could be a collision rather than a redeploy.
//
// For the Pages-to-Workers delegation we guard even when a name was passed:
// the name is a Pages project name carried across, and an existing Worker of
// the same name is a different resource we must not clobber. Repeat
// delegations are unaffected because the first one writes a config file
// (so `configPath` is then set). Outside the delegation, an explicit `--name`
// is treated as deliberate ownership so plain `wrangler deploy --name foo`
// keeps working in CI. See `failIfWorkerNameTaken` in preUploadApiChecks.
// We only guard the Pages-to-Workers delegation: there the name is a Pages
// project name carried across (or auto-generated), so an existing Worker of
// the same name is a different resource we must not clobber, and being
// non-interactive there is no prompt to resolve it. Repeat delegations are
// unaffected because the first one writes a config file (so `configPath` is
// then set).
//
// Plain `wrangler deploy` is NOT guarded, even in CI with an autoconfigured
// name: autoconfigured projects are routinely redeployed in CI (e.g. when the
// auto-generated config PR has not been merged), and blocking that regressed
// those workflows. See `failIfWorkerNameTaken` in preUploadApiChecks.
const nameOwnershipUnverified =
!config.configPath &&
isNonInteractiveOrCI() &&
(pagesToWorkersDelegation || !args.name);
!config.configPath && isNonInteractiveOrCI() && pagesToWorkersDelegation;

// --- Step 0. Auto-config --- //
const autoConfigResult = await maybeRunAutoConfig(args, config, {
Expand Down
Loading