diff --git a/.changeset/pages-project-commands-env-account-id.md b/.changeset/pages-project-commands-env-account-id.md new file mode 100644 index 0000000000..4053cb14d2 --- /dev/null +++ b/.changeset/pages-project-commands-env-account-id.md @@ -0,0 +1,9 @@ +--- +"wrangler": patch +--- + +Let `CLOUDFLARE_ACCOUNT_ID` override the cached account id in `wrangler pages project` commands + +`pages project list`, `pages project create` and `pages project delete` passed the internal Pages cache (`pages.json`) straight to account selection, which treats `account_id` as user-authored configuration and therefore ranks it above `CLOUDFLARE_ACCOUNT_ID`. In a multi-account setup a stale cached account won silently, so the command targeted the wrong account and failed with `Authentication error [code: 10000]`. + +These three commands now overlay the environment account id on top of the cache before resolving auth, which is what `pages deploy`, `pages deployment list`, `pages deployment delete`, `pages download config` and `pages secret` already do. The cache is still used as a fallback when the environment variable is unset. diff --git a/packages/wrangler/src/__tests__/pages/project-create.test.ts b/packages/wrangler/src/__tests__/pages/project-create.test.ts index ace31374ca..f1810dbff4 100644 --- a/packages/wrangler/src/__tests__/pages/project-create.test.ts +++ b/packages/wrangler/src/__tests__/pages/project-create.test.ts @@ -1,11 +1,14 @@ import { runInTempDir } from "@cloudflare/workers-utils/test-helpers"; import { http, HttpResponse } from "msw"; import { afterEach, describe, it, vi } from "vitest"; +import { saveToConfigCache } from "../../config-cache"; +import { PAGES_CONFIG_CACHE_FILENAME } from "../../pages/constants"; import { endEventLoop } from "../helpers/end-event-loop"; import { mockAccountId, mockApiToken } from "./../helpers/mock-account-id"; import { mockConsoleMethods } from "./../helpers/mock-console"; import { msw } from "./../helpers/msw"; import { runWrangler } from "./../helpers/run-wrangler"; +import type { PagesConfigCache } from "../../pages/types"; describe("pages project create", () => { const std = mockConsoleMethods(); @@ -186,11 +189,9 @@ describe("pages project create", () => { { once: true } ) ); - vi.mock("getConfigCache", () => { - return { - account_id: "original-account-id", - project_name: "an-existing-project", - }; + saveToConfigCache(PAGES_CONFIG_CACHE_FILENAME, { + account_id: "original-account-id", + project_name: "an-existing-project", }); vi.stubEnv("CLOUDFLARE_ACCOUNT_ID", "new-account-id"); await runWrangler( diff --git a/packages/wrangler/src/__tests__/pages/project-delete.test.ts b/packages/wrangler/src/__tests__/pages/project-delete.test.ts index 48b64476f9..3ece4c4013 100644 --- a/packages/wrangler/src/__tests__/pages/project-delete.test.ts +++ b/packages/wrangler/src/__tests__/pages/project-delete.test.ts @@ -1,6 +1,8 @@ import { runInTempDir } from "@cloudflare/workers-utils/test-helpers"; import { http, HttpResponse } from "msw"; import { afterEach, beforeEach, describe, it, vi } from "vitest"; +import { saveToConfigCache } from "../../config-cache"; +import { PAGES_CONFIG_CACHE_FILENAME } from "../../pages/constants"; import { endEventLoop } from "../helpers/end-event-loop"; import { mockAccountId, mockApiToken } from "../helpers/mock-account-id"; import { mockConsoleMethods } from "../helpers/mock-console"; @@ -8,6 +10,7 @@ import { clearDialogs, mockConfirm } from "../helpers/mock-dialogs"; import { useMockIsTTY } from "../helpers/mock-istty"; import { msw } from "../helpers/msw"; import { runWrangler } from "../helpers/run-wrangler"; +import type { PagesConfigCache } from "../../pages/types"; describe("pages project delete", () => { const std = mockConsoleMethods(); @@ -151,11 +154,9 @@ describe("pages project delete", () => { text: `Are you sure you want to delete "an-existing-project"? This action cannot be undone.`, result: true, }); - vi.mock("getConfigCache", () => { - return { - account_id: "original-account-id", - project_name: "an-existing-project", - }; + saveToConfigCache(PAGES_CONFIG_CACHE_FILENAME, { + account_id: "original-account-id", + project_name: "an-existing-project", }); vi.stubEnv("CLOUDFLARE_ACCOUNT_ID", "new-account-id"); await runWrangler("pages project delete an-existing-project"); diff --git a/packages/wrangler/src/__tests__/pages/project-list.test.ts b/packages/wrangler/src/__tests__/pages/project-list.test.ts index 2667eb11fb..da22c07129 100644 --- a/packages/wrangler/src/__tests__/pages/project-list.test.ts +++ b/packages/wrangler/src/__tests__/pages/project-list.test.ts @@ -1,12 +1,14 @@ import { runInTempDir } from "@cloudflare/workers-utils/test-helpers"; import { http, HttpResponse } from "msw"; import { afterEach, describe, it, vi } from "vitest"; +import { saveToConfigCache } from "../../config-cache"; +import { PAGES_CONFIG_CACHE_FILENAME } from "../../pages/constants"; import { endEventLoop } from "../helpers/end-event-loop"; import { mockConsoleMethods } from "../helpers/mock-console"; import { mockAccountId, mockApiToken } from "./../helpers/mock-account-id"; import { msw } from "./../helpers/msw"; import { runWrangler } from "./../helpers/run-wrangler"; -import type { Project } from "./../../pages/types"; +import type { PagesConfigCache, Project } from "./../../pages/types"; import type { ExpectStatic } from "vitest"; describe("pages project list", () => { @@ -83,11 +85,9 @@ describe("pages project list", () => { it("should override cached accountId with CLOUDFLARE_ACCOUNT_ID environmental variable if provided", async ({ expect, }) => { - vi.mock("getConfigCache", () => { - return { - account_id: "original-account-id", - project_name: "an-existing-project", - }; + saveToConfigCache(PAGES_CONFIG_CACHE_FILENAME, { + account_id: "original-account-id", + project_name: "an-existing-project", }); vi.stubEnv("CLOUDFLARE_ACCOUNT_ID", "new-account-id"); const requests = mockProjectListRequest(expect, [], "new-account-id"); diff --git a/packages/wrangler/src/pages/projects.ts b/packages/wrangler/src/pages/projects.ts index 415fd2cda4..e1f9b06036 100644 --- a/packages/wrangler/src/pages/projects.ts +++ b/packages/wrangler/src/pages/projects.ts @@ -1,4 +1,5 @@ import { execSync } from "node:child_process"; +import { getCloudflareAccountIdFromEnv } from "@cloudflare/workers-auth"; import { COMPLIANCE_REGION_CONFIG_PUBLIC, UserError, @@ -42,7 +43,11 @@ export const pagesProjectListCommand = createCommand({ PAGES_CONFIG_CACHE_FILENAME ); - const accountId = await requireAuth(config); + const envAccountId = getCloudflareAccountIdFromEnv(); + const accountId = await requireAuth({ + ...config, + ...(envAccountId ? { account_id: envAccountId } : {}), + }); const projects: Array = await listProjects({ accountId }); @@ -148,7 +153,11 @@ export const pagesProjectCreateCommand = createCommand({ const config = getConfigCache( PAGES_CONFIG_CACHE_FILENAME ); - const accountId = await requireAuth(config); + const envAccountId = getCloudflareAccountIdFromEnv(); + const accountId = await requireAuth({ + ...config, + ...(envAccountId ? { account_id: envAccountId } : {}), + }); // When run by an AI agent, delegate new static Pages projects to a Workers // static-assets deploy of the current directory. Accounts that already @@ -303,7 +312,11 @@ export const pagesProjectDeleteCommand = createCommand({ const config = getConfigCache( PAGES_CONFIG_CACHE_FILENAME ); - const accountId = await requireAuth(config); + const envAccountId = getCloudflareAccountIdFromEnv(); + const accountId = await requireAuth({ + ...config, + ...(envAccountId ? { account_id: envAccountId } : {}), + }); const confirmed = args.yes ||