Skip to content

Commit d40cf82

Browse files
committed
Add additional metrics (#7549)
* Add additional metrics * Detect Pages & Workers CI * Filter out default args (e.g. --x-versions, --x-dev-env, and --latest) * Create selfish-yaks-hope.md * fix format * extract CI check * sanitise wrangler login
1 parent b403435 commit d40cf82

File tree

9 files changed

+130
-38
lines changed

9 files changed

+130
-38
lines changed

.changeset/selfish-yaks-hope.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
"wrangler": patch
3+
---
4+
5+
Expand metrics collection to:
6+
7+
- Detect Pages & Workers CI
8+
- Filter out default args (e.g. `--x-versions`, `--x-dev-env`, and `--latest`) by only including args that were in `argv`

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

Lines changed: 48 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -186,15 +186,11 @@ describe("metrics", () => {
186186
isFirstUsage: false,
187187
configFileType: "toml",
188188
isCI: false,
189+
isPagesCI: false,
190+
isWorkersCI: false,
189191
isInteractive: true,
190-
argsUsed: [
191-
"j",
192-
"search",
193-
"xGradualRollouts",
194-
"xJsonConfig",
195-
"xVersions",
196-
],
197-
argsCombination: "j, search, xGradualRollouts, xJsonConfig, xVersions",
192+
argsUsed: [],
193+
argsCombination: "",
198194
command: "wrangler docs",
199195
args: {
200196
xJsonConfig: true,
@@ -343,6 +339,50 @@ describe("metrics", () => {
343339
expect(std.debug).toContain('isCI":true');
344340
});
345341

342+
it("should mark isPagesCI as true if running in Pages CI", async () => {
343+
vi.stubEnv("CF_PAGES", "1");
344+
const requests = mockMetricRequest();
345+
346+
await runWrangler("docs arg");
347+
348+
expect(requests.count).toBe(2);
349+
expect(std.debug).toContain('isPagesCI":true');
350+
});
351+
352+
it("should mark isWorkersCI as true if running in Workers CI", async () => {
353+
vi.stubEnv("WORKERS_CI", "1");
354+
const requests = mockMetricRequest();
355+
356+
await runWrangler("docs arg");
357+
358+
expect(requests.count).toBe(2);
359+
expect(std.debug).toContain('isWorkersCI":true');
360+
});
361+
362+
it("should not send arguments with wrangler login", async () => {
363+
const requests = mockMetricRequest();
364+
365+
await expect(
366+
runWrangler("login username password")
367+
).rejects.toThrowErrorMatchingInlineSnapshot(
368+
`[Error: Unknown arguments: username, password]`
369+
);
370+
371+
expect(requests.count).toBe(2);
372+
expect(std.debug).toContain('"argsCombination":""');
373+
expect(std.debug).toContain('"command":"wrangler login"');
374+
});
375+
376+
it("should include args provided by the user", async () => {
377+
const requests = mockMetricRequest();
378+
379+
await runWrangler("docs arg --search 'some search term'");
380+
381+
expect(requests.count).toBe(2);
382+
// Notably, this _doesn't_ include default args (e.g. --x-versions)
383+
expect(std.debug).toContain('argsCombination":"search"');
384+
});
385+
346386
it("should mark as non-interactive if running in non-interactive environment", async () => {
347387
setIsTTY(false);
348388
const requests = mockMetricRequest();

packages/wrangler/src/__tests__/vitest.setup.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,8 +135,9 @@ vi.mock("../user/generate-auth-url", () => {
135135
};
136136
});
137137

138-
vi.mock("../is-ci", () => {
139-
return { CI: { isCI: vi.fn().mockImplementation(() => false) } };
138+
vi.mock("../is-ci", async (importOriginal) => {
139+
const original = await importOriginal<typeof import("../is-ci")>();
140+
return { ...original, CI: { isCI: vi.fn().mockImplementation(() => false) } };
140141
});
141142

142143
vi.mock("../user/generate-random-state", () => {

packages/wrangler/src/index.ts

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1169,10 +1169,14 @@ export async function main(argv: string[]): Promise<void> {
11691169
addBreadcrumb(command);
11701170
// NB despite 'applyBeforeValidation = true', this runs *after* yargs 'validates' options,
11711171
// e.g. if a required arg is missing, yargs will error out before we send any events :/
1172-
dispatcher?.sendCommandEvent("wrangler command started", {
1173-
command,
1174-
args,
1175-
});
1172+
dispatcher?.sendCommandEvent(
1173+
"wrangler command started",
1174+
{
1175+
command,
1176+
args,
1177+
},
1178+
argv
1179+
);
11761180
}, /* applyBeforeValidation */ true);
11771181

11781182
let cliHandlerThrew = false;
@@ -1181,13 +1185,17 @@ export async function main(argv: string[]): Promise<void> {
11811185

11821186
const durationMs = Date.now() - startTime;
11831187

1184-
dispatcher?.sendCommandEvent("wrangler command completed", {
1185-
command,
1186-
args: metricsArgs,
1187-
durationMs,
1188-
durationSeconds: durationMs / 1000,
1189-
durationMinutes: durationMs / 1000 / 60,
1190-
});
1188+
dispatcher?.sendCommandEvent(
1189+
"wrangler command completed",
1190+
{
1191+
command,
1192+
args: metricsArgs,
1193+
durationMs,
1194+
durationSeconds: durationMs / 1000,
1195+
durationMinutes: durationMs / 1000 / 60,
1196+
},
1197+
argv
1198+
);
11911199
} catch (e) {
11921200
cliHandlerThrew = true;
11931201
let mayReport = true;
@@ -1297,15 +1305,19 @@ export async function main(argv: string[]): Promise<void> {
12971305

12981306
const durationMs = Date.now() - startTime;
12991307

1300-
dispatcher?.sendCommandEvent("wrangler command errored", {
1301-
command,
1302-
args: metricsArgs,
1303-
durationMs,
1304-
durationSeconds: durationMs / 1000,
1305-
durationMinutes: durationMs / 1000 / 60,
1306-
errorType:
1307-
errorType ?? (e instanceof Error ? e.constructor.name : undefined),
1308-
});
1308+
dispatcher?.sendCommandEvent(
1309+
"wrangler command errored",
1310+
{
1311+
command,
1312+
args: metricsArgs,
1313+
durationMs,
1314+
durationSeconds: durationMs / 1000,
1315+
durationMinutes: durationMs / 1000 / 60,
1316+
errorType:
1317+
errorType ?? (e instanceof Error ? e.constructor.name : undefined),
1318+
},
1319+
argv
1320+
);
13091321

13101322
throw e;
13111323
} finally {

packages/wrangler/src/is-ci.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
import isCI from "is-ci";
22

3+
export function isPagesCI() {
4+
return process.env.CF_PAGES === "1";
5+
}
6+
7+
export function isWorkersCI() {
8+
return process.env.WORKERS_CI === "1";
9+
}
310
/**
411
* Use this object to find out if we are currently running in a continuous integration environment.
512
*
@@ -9,6 +16,6 @@ import isCI from "is-ci";
916
export const CI = {
1017
/** Is Wrangler currently running in a CI? */
1118
isCI() {
12-
return isCI;
19+
return isCI || isPagesCI() || isWorkersCI();
1320
},
1421
};

packages/wrangler/src/is-interactive.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import { isInteractive as __isInteractive } from "@cloudflare/cli/interactive";
2-
import { CI } from "./is-ci";
2+
import { CI, isPagesCI, isWorkersCI } from "./is-ci";
33

44
/**
55
* Test whether the process is "interactive".
66
* Reasons it may not be interactive: it could be running in CI,
77
* or you're piping values from / to another process, etc
88
*/
99
export default function isInteractive(): boolean {
10-
if (process.env.CF_PAGES === "1") {
10+
if (isPagesCI() || isWorkersCI()) {
1111
return false;
1212
}
1313

packages/wrangler/src/metrics/metrics-dispatcher.ts

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { configFormat } from "../config";
44
import isInteractive from "../is-interactive";
55
import { logger } from "../logger";
66
import { sniffUserAgent } from "../package-manager";
7-
import { CI } from "./../is-ci";
7+
import { CI, isPagesCI, isWorkersCI } from "./../is-ci";
88
import {
99
getNodeVersion,
1010
getOS,
@@ -73,9 +73,13 @@ export function getMetricsDispatcher(options: MetricsConfigOptions) {
7373
properties: Omit<
7474
Extract<Events, { name: EventName }>["properties"],
7575
keyof CommonEventProperties
76-
>
76+
>,
77+
argv?: string[]
7778
) {
7879
try {
80+
if (properties.command?.startsWith("wrangler login")) {
81+
properties.command = "wrangler login";
82+
}
7983
if (
8084
properties.command === "wrangler telemetry disable" ||
8185
properties.command === "wrangler metrics disable"
@@ -91,7 +95,7 @@ export function getMetricsDispatcher(options: MetricsConfigOptions) {
9195
printMetricsBanner();
9296
}
9397

94-
const argsUsed = sanitiseUserInput(properties.args ?? {});
98+
const argsUsed = sanitiseUserInput(properties.args ?? {}, argv);
9599
const argsCombination = argsUsed.sort().join(", ");
96100
const commonEventProperties: CommonEventProperties = {
97101
amplitude_session_id,
@@ -104,6 +108,8 @@ export function getMetricsDispatcher(options: MetricsConfigOptions) {
104108
isFirstUsage: readMetricsConfig().permission === undefined,
105109
configFileType: configFormat(options.configPath),
106110
isCI: CI.isCI(),
111+
isPagesCI: isPagesCI(),
112+
isWorkersCI: isWorkersCI(),
107113
isInteractive: isInteractive(),
108114
argsUsed,
109115
argsCombination,
@@ -213,11 +219,20 @@ const normalise = (arg: string) => {
213219
};
214220

215221
const exclude = new Set(["$0", "_"]);
216-
/** just some pretty naive cleaning so we don't send "experimental-versions", "experimentalVersions", "x-versions" and "xVersions" etc. */
217-
const sanitiseUserInput = (argsWithValues: Record<string, unknown>) => {
222+
/**
223+
* just some pretty naive cleaning so we don't send duplicates of "experimental-versions", "experimentalVersions", "x-versions" and "xVersions" etc.
224+
* optionally, if an argv is provided remove all args that were not specified in argv (which means that default values will be filtered out)
225+
*/
226+
const sanitiseUserInput = (
227+
argsWithValues: Record<string, unknown>,
228+
argv?: string[]
229+
) => {
218230
const result: string[] = [];
219231
const args = Object.keys(argsWithValues);
220232
for (const arg of args) {
233+
if (Array.isArray(argv) && !argv.some((a) => a.includes(arg))) {
234+
continue;
235+
}
221236
if (exclude.has(arg)) {
222237
continue;
223238
}

packages/wrangler/src/metrics/types.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,14 @@ export type CommonEventProperties = {
4040
* Whether the Wrangler client is running in CI
4141
*/
4242
isCI: boolean;
43+
/**
44+
* Whether the Wrangler client is running in Pages CI
45+
*/
46+
isPagesCI: boolean;
47+
/**
48+
* Whether the Wrangler client is running in Workers CI
49+
*/
50+
isWorkersCI: boolean;
4351
/**
4452
* Whether the Wrangler client is running in an interactive instance
4553
*/

packages/wrangler/turbo.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
"EXPERIMENTAL_MIDDLEWARE",
3737
"FORMAT_WRANGLER_ERRORS",
3838
"CF_PAGES",
39+
"WORKERS_CI",
3940
"CI",
4041
"CF_PAGES_UPLOAD_JWT",
4142
"EXPERIMENTAL_MIDDLEWARE",

0 commit comments

Comments
 (0)