Skip to content

Commit 40a5f50

Browse files
committed
non-TTY check for required variables:
Added a check in non-TTY environments for `account_id`, `CLOUDFLARE_ACCOUNT_ID` and `CLOUDFLARE_API_TOKEN`. If `account_id` exists in `wrangler.toml` then `CLOUDFLARE_ACCOUNT_ID` is not needed in non-TTY scope. The `CLOUDFLARE_API_TOKEN` is necessary in non-TTY scope and will always error if missing. resolves #827
1 parent 50cbe61 commit 40a5f50

File tree

5 files changed

+49
-21
lines changed

5 files changed

+49
-21
lines changed

packages/wrangler/src/__tests__/publish.test.ts

+26-4
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@ describe("publish", () => {
6060
});
6161

6262
it("drops a user into the login flow if they're unauthenticated", async () => {
63-
// Should not throw missing Errors in TTY environment
6463
writeWranglerToml();
6564
writeWorkerSource();
6665
mockSubDomainRequest();
@@ -178,7 +177,7 @@ describe("publish", () => {
178177
expect(std.err).toMatchInlineSnapshot(`""`);
179178
});
180179

181-
it("should throw an error in non-TTY if 'account_id' & 'CLOUDFLARE_ACCOUNT_ID' is missing", async () => {
180+
it("should throw an error in non-TTY & there is more than one account associated with API token", async () => {
182181
setIsTTY(false);
183182
process.env = {
184183
CLOUDFLARE_API_TOKEN: "hunter2",
@@ -233,10 +232,33 @@ describe("publish", () => {
233232
await expect(runWrangler("publish index.js")).rejects.toThrowError();
234233

235234
expect(std.err).toMatchInlineSnapshot(`
236-
"[31mX [41;31m[[41;97mERROR[41;31m][0m [1mMissing 'CLOUDFLARE_API_TOKEN' from non-TTY environment, please see docs for more info: TBD[0m
235+
"[31mX [41;31m[[41;97mERROR[41;31m][0m [1mIn a non-interactive environment, it's necessary to set a CLOUDFLARE_API_TOKEN environment variable for wrangler to work. Please go to https://developers.cloudflare.com/api/tokens/create/ for instructions on how to create an api token, and assign its value to CLOUDFLARE_API_TOKEN.[0m
237236
237+
"
238+
`);
239+
});
240+
it("should throw error with no account ID provided and no members retrieved", async () => {
241+
setIsTTY(false);
242+
writeWranglerToml({
243+
account_id: undefined,
244+
});
245+
process.env = {
246+
CLOUDFLARE_API_TOKEN: "picard",
247+
CLOUDFLARE_ACCOUNT_ID: undefined,
248+
};
249+
writeWorkerSource();
250+
mockSubDomainRequest();
251+
mockUploadWorkerRequest();
252+
mockOAuthServerCallback();
253+
mockGetMemberships({
254+
success: false,
255+
result: [],
256+
});
238257

239-
X [ERROR] Did not login, quitting...
258+
await expect(runWrangler("publish index.js")).rejects.toThrowError();
259+
260+
expect(std.err).toMatchInlineSnapshot(`
261+
"X [ERROR] Failed to automatically retrieve account IDs for the logged in user. In a non-interactive environment, it is mandatory to specify an account ID, either by assigning its value to CLOUDFLARE_ACCOUNT_ID, or as \`account_id\` in your \`wrangler.toml\` file.
240262
241263
"
242264
`);

packages/wrangler/src/__tests__/secret.test.ts

+2-5
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { mockAccountId, mockApiToken } from "./helpers/mock-account-id";
44
import { setMockResponse, unsetAllMocks } from "./helpers/mock-cfetch";
55
import { mockConsoleMethods } from "./helpers/mock-console";
66
import { mockConfirm, mockPrompt } from "./helpers/mock-dialogs";
7-
import { useMockIsTTY } from "./helpers/mock-istty";
87
import { mockOAuthFlow } from "./helpers/mock-oauth-flow";
98
import { useMockStdin } from "./helpers/mock-stdin";
109
import { runInTempDir } from "./helpers/run-in-tmp";
@@ -13,14 +12,13 @@ import { runWrangler } from "./helpers/run-wrangler";
1312
describe("wrangler secret", () => {
1413
const std = mockConsoleMethods();
1514
const { mockGetMemberships } = mockOAuthFlow();
16-
const { setIsTTY } = useMockIsTTY();
15+
1716
runInTempDir();
1817
mockAccountId();
1918
mockApiToken();
2019

2120
afterEach(() => {
2221
unsetAllMocks();
23-
setIsTTY(true);
2422
});
2523

2624
describe("put", () => {
@@ -180,7 +178,7 @@ describe("wrangler secret", () => {
180178
await expect(
181179
runWrangler("secret put the-key --name script-name")
182180
).rejects.toThrowErrorMatchingInlineSnapshot(
183-
`"No account id found, quitting..."`
181+
`"Failed to automatically retrieve account IDs for the logged in user. In a non-interactive environment, it is mandatory to specify an account ID, either by assigning its value to CLOUDFLARE_ACCOUNT_ID, or as \`account_id\` in your \`wrangler.toml\` file."`
184182
);
185183
});
186184

@@ -204,7 +202,6 @@ describe("wrangler secret", () => {
204202
});
205203

206204
it("should error if a user has multiple accounts, and has not specified an account in wrangler.toml", async () => {
207-
setIsTTY(false);
208205
mockGetMemberships({
209206
success: true,
210207
result: [

packages/wrangler/src/__tests__/whoami.test.tsx

+6
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,18 @@ import { writeAuthConfigFile } from "../user";
44
import { getUserInfo, WhoAmI } from "../whoami";
55
import { setMockResponse } from "./helpers/mock-cfetch";
66
import { mockConsoleMethods } from "./helpers/mock-console";
7+
import { useMockIsTTY } from "./helpers/mock-istty";
78
import { runInTempDir } from "./helpers/run-in-tmp";
89
import type { UserInfo } from "../whoami";
910

1011
describe("getUserInfo()", () => {
1112
runInTempDir({ homedir: "./home" });
1213
const std = mockConsoleMethods();
14+
const { setIsTTY } = useMockIsTTY();
15+
16+
beforeEach(() => {
17+
setIsTTY(true);
18+
});
1319

1420
it("should return undefined if there is no config file", async () => {
1521
const userInfo = await getUserInfo();

packages/wrangler/src/pages.tsx

+8-8
Original file line numberDiff line numberDiff line change
@@ -842,11 +842,11 @@ const createDeployment: CommandModule<
842842
const config = getConfigCache<PagesConfigCache>(
843843
PAGES_CONFIG_CACHE_FILENAME
844844
);
845-
const isInteractive = process.stdin.isTTY;
846-
const accountId = await requireAuth(config, isInteractive);
845+
const accountId = await requireAuth(config);
847846

848847
projectName ??= config.project_name;
849848

849+
const isInteractive = process.stdin.isTTY;
850850
if (!projectName && isInteractive) {
851851
const existingOrNew = await new Promise<"new" | "existing">((resolve) => {
852852
const { unmount } = render(
@@ -1605,8 +1605,8 @@ export const pages: BuilderCallback<unknown, unknown> = (yargs) => {
16051605
const config = getConfigCache<PagesConfigCache>(
16061606
PAGES_CONFIG_CACHE_FILENAME
16071607
);
1608-
const isInteractive = process.stdin.isTTY;
1609-
const accountId = await requireAuth(config, isInteractive);
1608+
1609+
const accountId = await requireAuth(config);
16101610

16111611
const projects: Array<Project> = await listProjects({ accountId });
16121612

@@ -1650,9 +1650,9 @@ export const pages: BuilderCallback<unknown, unknown> = (yargs) => {
16501650
const config = getConfigCache<PagesConfigCache>(
16511651
PAGES_CONFIG_CACHE_FILENAME
16521652
);
1653-
const isInteractive = process.stdin.isTTY;
1654-
const accountId = await requireAuth(config, isInteractive);
1653+
const accountId = await requireAuth(config);
16551654

1655+
const isInteractive = process.stdin.isTTY;
16561656
if (!projectName && isInteractive) {
16571657
projectName = await prompt("Enter the name of your new project:");
16581658
}
@@ -1735,11 +1735,11 @@ export const pages: BuilderCallback<unknown, unknown> = (yargs) => {
17351735
const config = getConfigCache<PagesConfigCache>(
17361736
PAGES_CONFIG_CACHE_FILENAME
17371737
);
1738-
const isInteractive = process.stdin.isTTY;
1739-
const accountId = await requireAuth(config, isInteractive);
1738+
const accountId = await requireAuth(config);
17401739

17411740
projectName ??= config.project_name;
17421741

1742+
const isInteractive = process.stdin.isTTY;
17431743
if (!projectName && isInteractive) {
17441744
const projects = await listProjects({ accountId });
17451745
projectName = await new Promise((resolve) => {

packages/wrangler/src/user.tsx

+7-4
Original file line numberDiff line numberDiff line change
@@ -418,11 +418,9 @@ export function getAPIToken(): string | undefined {
418418
!localAPIToken &&
419419
!LocalState.accessToken?.value
420420
) {
421-
logger.error(
422-
"Missing 'CLOUDFLARE_API_TOKEN' from non-TTY environment, please see docs for more info: TBD"
421+
throw new Error(
422+
"In a non-interactive environment, it's necessary to set a CLOUDFLARE_API_TOKEN environment variable for wrangler to work. Please go to https://developers.cloudflare.com/api/tokens/create/ for instructions on how to create an api token, and assign its value to CLOUDFLARE_API_TOKEN."
423423
);
424-
425-
return;
426424
}
427425

428426
return localAPIToken ?? LocalState.accessToken?.value;
@@ -1142,6 +1140,11 @@ export async function getAccountId(
11421140
.join("\n")
11431141
);
11441142
}
1143+
} else {
1144+
if (!isInteractive)
1145+
throw new Error(
1146+
`Failed to automatically retrieve account IDs for the logged in user. In a non-interactive environment, it is mandatory to specify an account ID, either by assigning its value to CLOUDFLARE_ACCOUNT_ID, or as \`account_id\` in your \`wrangler.toml\` file.`
1147+
);
11451148
}
11461149
return accountId;
11471150
}

0 commit comments

Comments
 (0)