Skip to content

Commit 1ab1306

Browse files
committed
CI/CD necessary variable checks:
Added a check in CI/CD 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 CI/CD scope. The `CLOUDFLARE_API_TOKEN` is necessary in CI/CD scope and will always error if missing. resolves #827
1 parent 277b254 commit 1ab1306

File tree

8 files changed

+150
-9
lines changed

8 files changed

+150
-9
lines changed

.changeset/eighty-yaks-jump.md

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
"wrangler": patch
3+
---
4+
5+
feat: Added a check in CI/CD environments for `account_id`, `CLOUDFLARE_ACCOUNT_ID` and `CLOUDFLARE_API_TOKEN`. If `account_id` exists in `wrangler.toml`
6+
then `CLOUDFLARE_ACCOUNT_ID` is not needed in CI/CD scope. The `CLOUDFLARE_API_TOKEN` is necessary in CI/CD scope and will always error if missing.
7+
8+
resolves #827

package-lock.json

+7-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/wrangler/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
"@types/ws": "^8.5.3",
6464
"@types/yargs": "^17.0.10",
6565
"chokidar": "^3.5.3",
66+
"ci-info": "^3.3.0",
6667
"clipboardy": "^3.0.0",
6768
"cmd-shim": "^4.1.0",
6869
"command-exists": "^1.2.9",
+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import ci from "ci-info";
2+
import { mockConsoleMethods } from "./helpers/mock-console";
3+
import { runInTempDir } from "./helpers/run-in-tmp";
4+
import { runWrangler } from "./helpers/run-wrangler";
5+
import writeWranglerToml from "./helpers/write-wrangler-toml";
6+
7+
const std = mockConsoleMethods();
8+
void std; // Keeps the console quiet
9+
runInTempDir();
10+
11+
const ENV_COPY = process.env;
12+
13+
beforeEach(() => {
14+
jest.resetModules();
15+
jest.mock("ci-info");
16+
(ci.isCI as jest.Mocked<boolean>) = true;
17+
});
18+
19+
afterEach(() => {
20+
process.env = ENV_COPY;
21+
});
22+
23+
afterAll(() => {
24+
jest.unmock("ci-info");
25+
});
26+
27+
it("should not throw an error in CI if 'CLOUDFLARE_API_TOKEN' & 'account_id' are in scope", async () => {
28+
writeWranglerToml({
29+
account_id: "IG-88",
30+
});
31+
32+
process.env = {
33+
CLOUDFLARE_API_TOKEN: "123456789",
34+
};
35+
36+
await runWrangler().catch((err) => {
37+
expect(err).toMatchInlineSnapshot(`""`);
38+
});
39+
});
40+
41+
it("should not throw an error if 'CLOUDFLARE_ACCOUNT_ID' & 'CLOUDFLARE_API_TOKEN' are in scope", async () => {
42+
process.env = {
43+
CLOUDFLARE_API_TOKEN: "hunter2",
44+
CLOUDFLARE_ACCOUNT_ID: "IG-88",
45+
};
46+
47+
await runWrangler().catch((err) => {
48+
expect(err).toMatchInlineSnapshot(`""`);
49+
});
50+
});
51+
52+
it("should throw an error in CI if 'account_id' & 'CLOUDFLARE_ACCOUNT_ID' is missing", async () => {
53+
writeWranglerToml({
54+
account_id: undefined,
55+
});
56+
57+
process.env = {
58+
CLOUDFLARE_API_TOKEN: "hunter2",
59+
CLOUDFLARE_ACCOUNT_ID: undefined,
60+
};
61+
62+
await runWrangler().catch((err) => {
63+
expect(err).toMatchInlineSnapshot(
64+
`[Error: Missing "account_id" from "wrangler.toml" and "CLOUDFLARE_ACCOUNT_ID" from CI environment, one is required, please see docs for more info: TBD]`
65+
);
66+
});
67+
});
68+
69+
it("should throw error in CI if 'CLOUDFLARE_API_TOKEN' is missing", async () => {
70+
writeWranglerToml({
71+
account_id: undefined,
72+
});
73+
74+
process.env = {
75+
CLOUDFLARE_API_TOKEN: undefined,
76+
CLOUDFLARE_ACCOUNT_ID: "badwolf",
77+
};
78+
await runWrangler().catch((err) => {
79+
expect(err).toMatchInlineSnapshot(
80+
`[Error: Missing "CLOUDFLARE_API_TOKEN" from CI environment, please see docs for more info: TBD]`
81+
);
82+
});
83+
});
84+
85+
it("should throw errors in CI if 'CLOUDFLARE_API_TOKEN', 'account_id' & 'CLOUDFLARE_ACCOUNT_ID is missing", async () => {
86+
await runWrangler().catch((err) => {
87+
expect(err).toMatchInlineSnapshot(
88+
`[Error: Missing "account_id" from "wrangler.toml" and "CLOUDFLARE_ACCOUNT_ID" "CLOUDFLARE_API_TOKEN" from CI environment, please see docs for more info: TBD]`
89+
);
90+
});
91+
});

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

+3-6
Original file line numberDiff line numberDiff line change
@@ -131,12 +131,9 @@ describe("Error Reporting", () => {
131131

132132
await reportError(new Error("test error"), "testFalse");
133133

134-
const { error_tracking_opt, error_tracking_opt_date } = TOML.parse(
135-
await fsp.readFile(path.join(os.homedir(), reportingTOMLPath), "utf-8")
136-
);
137-
138-
expect(error_tracking_opt).toBe(false);
139-
expect(error_tracking_opt_date).toBeTruthy();
134+
expect(
135+
fs.existsSync(path.join(os.homedir(), reportingTOMLPath, "utf-8"))
136+
).toBe(false);
140137

141138
expect(Sentry.captureException).not.toHaveBeenCalledWith(
142139
new Error("test error")

packages/wrangler/src/index.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import path from "node:path";
44
import { setTimeout } from "node:timers/promises";
55
import TOML from "@iarna/toml";
66
import chalk from "chalk";
7+
import ci from "ci-info";
78
import { findUp } from "find-up";
89
import getPort from "get-port";
910
import { render } from "ink";

packages/wrangler/src/reporting.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
Integrations,
1212
setContext,
1313
} from "@sentry/node";
14+
import ci from "ci-info";
1415
import { execaSync } from "execa";
1516
import prompts from "prompts";
1617
import * as pkj from "../package.json";
@@ -92,7 +93,8 @@ function exceptionTransaction(error: Error, origin = "") {
9293
}
9394

9495
export async function reportError(err: Error, origin = "") {
95-
if (!process.stdout.isTTY) return await appendReportingDecision("false");
96+
// If the user has not opted in to error reporting, we don't want to do anything in CI or non-interactive environments
97+
if (!process.stdout.isTTY || ci.isCI) return;
9698

9799
const errorTrackingOpt = await reportingPermission();
98100

packages/wrangler/src/user.tsx

+36
Original file line numberDiff line numberDiff line change
@@ -214,12 +214,14 @@ import path from "node:path";
214214
import url from "node:url";
215215
import { TextEncoder } from "node:util";
216216
import TOML from "@iarna/toml";
217+
import ci from "ci-info";
217218
import { render, Text } from "ink";
218219
import SelectInput from "ink-select-input";
219220
import Table from "ink-table";
220221
import React from "react";
221222
import { fetch } from "undici";
222223
import { getCloudflareApiBaseUrl } from "./cfetch";
224+
import { readConfig } from "./config";
223225
import { getEnvironmentVariableFactory } from "./environment-variables";
224226
import openInBrowser from "./open-in-browser";
225227
import { parseTOML, readFileSync } from "./parse";
@@ -383,6 +385,38 @@ function getAuthTokens(config?: UserAuthConfig): AuthTokens {
383385
}
384386
}
385387

388+
/**
389+
* Inside a CI/CD environment, you want to check if all the required variables are in scope
390+
* and if not, throw a helpful error message with missing variables and documentation link.
391+
*/
392+
function ciCheck() {
393+
if (ci.isCI) {
394+
const config = readConfig(undefined, {});
395+
396+
if (
397+
!process.env.CLOUDFLARE_API_TOKEN &&
398+
!config.account_id &&
399+
!process.env.CLOUDFLARE_ACCOUNT_ID
400+
) {
401+
throw new Error(
402+
`Missing "account_id" from "wrangler.toml" and "CLOUDFLARE_ACCOUNT_ID" "CLOUDFLARE_API_TOKEN" from CI environment, please see docs for more info: TBD`
403+
);
404+
}
405+
406+
if (!process.env.CLOUDFLARE_API_TOKEN) {
407+
throw new Error(
408+
`Missing "CLOUDFLARE_API_TOKEN" from CI environment, please see docs for more info: TBD`
409+
);
410+
}
411+
412+
if (!config.account_id && !process.env.CLOUDFLARE_ACCOUNT_ID) {
413+
throw new Error(
414+
`Missing "account_id" from "wrangler.toml" and "CLOUDFLARE_ACCOUNT_ID" from CI environment, one is required, please see docs for more info: TBD`
415+
);
416+
}
417+
}
418+
}
419+
386420
/**
387421
* Run the initialisation of the auth state, in the case that something changed.
388422
*
@@ -402,6 +436,7 @@ export function reinitialiseAuthTokens(config?: UserAuthConfig): void {
402436
}
403437

404438
export function getAPIToken(): string | undefined {
439+
ciCheck();
405440
if (LocalState.apiToken) {
406441
console.warn(
407442
"It looks like you have used Wrangler 1's `config` command to login with an API token.\n" +
@@ -1071,6 +1106,7 @@ export function listScopes(message = "💁 Available scopes:"): void {
10711106
export async function getAccountId(
10721107
isInteractive = true
10731108
): Promise<string | undefined> {
1109+
ciCheck();
10741110
const apiToken = getAPIToken();
10751111
if (!apiToken) return;
10761112

0 commit comments

Comments
 (0)