Skip to content

Commit

Permalink
feat(create-cloudflare): telemetry collection (#6484)
Browse files Browse the repository at this point in the history
Co-authored-by: Nevi Shah <76798024+nevikashah@users.noreply.github.com>
Co-authored-by: lrapoport-cf <107272160+lrapoport-cf@users.noreply.github.com>
  • Loading branch information
3 people committed Sep 23, 2024
1 parent ba4ac82 commit 5632968
Show file tree
Hide file tree
Showing 38 changed files with 2,279 additions and 372 deletions.
21 changes: 21 additions & 0 deletions .changeset/eighty-guests-join.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
"create-cloudflare": minor
---

feat: telemetry collection

Cloudflare will collect telemetry about your usage of `create-cloudflare` to improve the experience.

If you would like to disable telemetry, you can run:

```sh
npm create cloudflare telemetry disable
```

Alternatively, you can set an environment variable:

```sh
export CREATE_CLOUDFLARE_TELEMETRY_DISABLED=1
```

Read more about our data policy at https://github.com/cloudflare/workers-sdk/blob/main/packages/create-cloudflare/telemetry.md.
11 changes: 11 additions & 0 deletions packages/cli/error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/**
* Error thrown when an operation is cancelled
*/
export class CancelError extends Error {
constructor(
message?: string,
readonly signal?: NodeJS.Signals
) {
super(message);
}
}
41 changes: 24 additions & 17 deletions packages/cli/interactive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
} from "@clack/core";
import { createLogUpdate } from "log-update";
import { blue, bold, brandColor, dim, gray, white } from "./colors";
import { CancelError } from "./error";
import SelectRefreshablePrompt from "./select-list";
import { stdout } from "./streams";
import {
Expand Down Expand Up @@ -56,6 +57,8 @@ export type BasePromptConfig = {
validate?: (value: Arg) => string | void;
// Override some/all renderers (can be used for custom renderers before hoisting back into shared code)
renderers?: Partial<ReturnType<typeof getRenderers>>;
// Whether to throw an error if the prompt is crashed or cancelled
throwOnError?: boolean;
};

export type TextPromptConfig = BasePromptConfig & {
Expand Down Expand Up @@ -108,7 +111,11 @@ function acceptDefault<T>(
): T {
const error = promptConfig.validate?.(initialValue as Arg);
if (error) {
crash(error);
if (promptConfig.throwOnError) {
throw new Error(error);
} else {
crash(error);
}
}

const lines = renderers.submit({ value: initialValue as Arg });
Expand Down Expand Up @@ -226,8 +233,12 @@ export const inputPrompt = async <T = string>(
const input = (await prompt.prompt()) as T;

if (isCancel(input)) {
cancel("Operation cancelled.");
process.exit(0);
if (promptConfig.throwOnError) {
throw new CancelError("Operation cancelled");
} else {
cancel("Operation cancelled");
process.exit(0);
}
}

return input;
Expand Down Expand Up @@ -258,11 +269,6 @@ const renderSubmit = (config: PromptConfig, value: string) => {
return [`${leftT} ${question}`, content, `${grayBar}`];
};

const handleCancel = () => {
cancel("Operation cancelled.");
process.exit(0);
};

export const getRenderers = (config: PromptConfig) => {
switch (config.type) {
case "select":
Expand All @@ -283,18 +289,19 @@ const getTextRenderers = (config: TextPromptConfig) => {
const helpText = config.helpText ?? "";
const format = config.format ?? ((val: Arg) => String(val));
const defaultValue = config.defaultValue?.toString() ?? "";
const activeRenderer = ({ value }: { value: Arg }) => [
`${blCorner} ${bold(question)} ${dim(helpText)}`,
`${space(2)}${format(value || dim(defaultValue))}`,
``, // extra line for readability
];

return {
initial: () => [
`${blCorner} ${bold(question)} ${dim(helpText)}`,
`${space(2)}${gray(format(defaultValue))}`,
``, // extra line for readability
],
active: ({ value }: { value: Arg }) => [
`${blCorner} ${bold(question)} ${dim(helpText)}`,
`${space(2)}${format(value || dim(defaultValue))}`,
``, // extra line for readability
],
active: activeRenderer,
error: ({ value, error }: { value: Arg; error: string }) => [
`${leftT} ${status.error} ${dim(error)}`,
`${grayBar}`,
Expand All @@ -304,7 +311,7 @@ const getTextRenderers = (config: TextPromptConfig) => {
],
submit: ({ value }: { value: Arg }) =>
renderSubmit(config, format(value ?? "")),
cancel: handleCancel,
cancel: activeRenderer,
};
};

Expand Down Expand Up @@ -435,7 +442,7 @@ const getSelectRenderers = (
options.find((o) => o.value === value)?.label as string
);
},
cancel: handleCancel,
cancel: defaultRenderer,
};
};

Expand Down Expand Up @@ -559,7 +566,7 @@ const getSelectListRenderers = (config: ListPromptConfig) => {
options.find((o) => o.value === value)?.value as string
);
},
cancel: handleCancel,
cancel: defaultRenderer,
};
};

Expand All @@ -586,7 +593,7 @@ const getConfirmRenderers = (config: ConfirmPromptConfig) => {
error: defaultRenderer,
submit: ({ value }: { value: Arg }) =>
renderSubmit(config, value ? "yes" : "no"),
cancel: handleCancel,
cancel: defaultRenderer,
};
};

Expand Down
1 change: 1 addition & 0 deletions packages/create-cloudflare/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
"which-pm-runs": "^1.1.0",
"wrangler": "workspace:*",
"wrap-ansi": "^9.0.0",
"xdg-app-paths": "^8.3.0",
"yargs": "^17.7.2"
},
"engines": {
Expand Down
5 changes: 5 additions & 0 deletions packages/create-cloudflare/scripts/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ const run = async () => {
// This is required to support jsonc-parser. See https://github.com/microsoft/node-jsonc-parser/issues/57
mainFields: ["module", "main"],
format: "cjs",
define: {
"process.env.SPARROW_SOURCE_KEY": JSON.stringify(
process.env.SPARROW_SOURCE_KEY ?? "",
),
},
};

const runBuild = async () => {
Expand Down
13 changes: 6 additions & 7 deletions packages/create-cloudflare/src/__tests__/deploy.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { crash } from "@cloudflare/cli";
import { inputPrompt } from "@cloudflare/cli/interactive";
import { mockPackageManager, mockSpinner } from "helpers/__tests__/mocks";
import { runCommand } from "helpers/command";
Expand All @@ -12,7 +11,6 @@ vi.mock("helpers/command");
vi.mock("../wrangler/accounts");
vi.mock("@cloudflare/cli/interactive");
vi.mock("which-pm-runs");
vi.mock("@cloudflare/cli");
vi.mock("helpers/files");

const mockInsideGitRepo = (isInside = true) => {
Expand Down Expand Up @@ -135,7 +133,6 @@ describe("deploy helpers", async () => {
vi.mocked(runCommand).mockResolvedValueOnce(deployedUrl);

await runDeploy(ctx);
expect(crash).not.toHaveBeenCalled();
expect(runCommand).toHaveBeenCalledWith(
["npm", "run", "deploy", "--", "--commit-message", `"${commitMsg}"`],
expect.any(Object),
Expand All @@ -146,8 +143,9 @@ describe("deploy helpers", async () => {
test("no account in ctx", async () => {
const ctx = createTestContext();
ctx.account = undefined;
await runDeploy(ctx);
expect(crash).toHaveBeenCalledWith("Failed to read Cloudflare account.");
await expect(() => runDeploy(ctx)).rejects.toThrow(
"Failed to read Cloudflare account.",
);
});

test("Failed deployment", async () => {
Expand All @@ -158,8 +156,9 @@ describe("deploy helpers", async () => {
mockInsideGitRepo(false);
vi.mocked(runCommand).mockResolvedValueOnce("");

await runDeploy(ctx);
expect(crash).toHaveBeenCalledWith("Failed to find deployment url.");
await expect(() => runDeploy(ctx)).rejects.toThrow(
"Failed to find deployment url.",
);
});
});
});
125 changes: 63 additions & 62 deletions packages/create-cloudflare/src/__tests__/dialog.test.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,45 @@
import { afterEach, beforeAll, describe, expect, test } from "vitest";
import { afterAll, afterEach, beforeAll, describe, expect, test } from "vitest";
import { collectCLIOutput, normalizeOutput } from "../../../cli/test-util";
import { printSummary, printWelcomeMessage } from "../dialog";
import type { C3Context } from "types";

describe("dialog helpers", () => {
const std = collectCLIOutput();
const originalColumns = process.stdout.columns;

test("printWelcomeMessage", () => {
printWelcomeMessage("0.0.0");
beforeAll(() => {
process.stdout.columns = 60;
});

afterAll(() => {
process.stdout.columns = originalColumns;
});

test("printWelcomeMessage with telemetry disabled", () => {
printWelcomeMessage("0.0.0", false);

expect(normalizeOutput(std.out)).toMatchInlineSnapshot(`
"────────────────────────────────────────────────────────────
👋 Welcome to create-cloudflare v0.0.0!
🧡 Let's get started.
────────────────────────────────────────────────────────────
"
`);
});

test("printWelcomeMessage with telemetry enabled", () => {
printWelcomeMessage("0.0.0", true);

expect(normalizeOutput(std.out)).toMatchInlineSnapshot(`
" ╭──────────────────────────────────────────────────────────────╮
│ 👋 Welcome to create-cloudflare v0.0.0! │
│ 🧡 Let's get started. │
╰──────────────────────────────────────────────────────────────╯
"────────────────────────────────────────────────────────────
👋 Welcome to create-cloudflare v0.0.0!
🧡 Let's get started.
📊 Cloudflare collects telemetry about your usage of Create-Cloudflare.
Learn more at: https://github.com/cloudflare/workers-sdk/blob/main/packages/create-cloudflare/telemetry.md
────────────────────────────────────────────────────────────
"
`);
});
Expand Down Expand Up @@ -55,23 +81,24 @@ describe("dialog helpers", () => {
await printSummary(ctx);

expect(normalizeOutput(std.out)).toMatchInlineSnapshot(`
" ╭───────────────────────────────────────────────────────────────────────────────────────╮
│ 🎉 SUCCESS Application deployed successfully! │
│ │
│ 🔍 View Project │
│ Visit: https://example.test.workers.dev │
│ Dash: https://dash.cloudflare.com/?to=/:account/workers/services/view/test-project │
│ │
│ 💻 Continue Developing │
│ Start dev server: pnpm run start │
│ Deploy again: pnpm run deploy │
│ │
│ 📖 Explore Documentation │
│ https://developers.cloudflare.com/workers │
│ │
│ 💬 Join our Community │
│ https://discord.cloudflare.com │
╰───────────────────────────────────────────────────────────────────────────────────────╯
"────────────────────────────────────────────────────────────
🎉 SUCCESS Application deployed successfully!
🔍 View Project
Visit: https://example.test.workers.dev
Dash: https://dash.cloudflare.com/?to=/:account/workers/services/view/test-project
💻 Continue Developing
Start dev server: pnpm run start
Deploy again: pnpm run deploy
📖 Explore Documentation
https://developers.cloudflare.com/workers
💬 Join our Community
https://discord.cloudflare.com
────────────────────────────────────────────────────────────
"
`);
});
Expand All @@ -89,47 +116,21 @@ describe("dialog helpers", () => {
});

expect(normalizeOutput(std.out)).toMatchInlineSnapshot(`
" ╭──────────────────────────────────────────────────────────────╮
│ 🎉 SUCCESS Application created successfully! │
│ │
│ 💻 Continue Developing │
│ Change directories: cd ../example │
│ Start dev server: pnpm run start │
│ Deploy: pnpm run deploy │
│ │
│ 📖 Explore Documentation │
│ https://developers.cloudflare.com/pages │
│ │
│ 💬 Join our Community │
│ https://discord.cloudflare.com │
╰──────────────────────────────────────────────────────────────╯
"
`);
});
"────────────────────────────────────────────────────────────
🎉 SUCCESS Application created successfully!
test("with lines truncated", async () => {
process.stdout.columns = 40;
💻 Continue Developing
Change directories: cd ../example
Start dev server: pnpm run start
Deploy: pnpm run deploy
await printSummary(ctx);
📖 Explore Documentation
https://developers.cloudflare.com/pages
💬 Join our Community
https://discord.cloudflare.com
────────────────────────────────────────────────────────────
expect(normalizeOutput(std.out)).toMatchInlineSnapshot(`
" ╭─────────────────────────────────────╮
│ 🎉 SUCCESS Application deploye... │
│ │
│ 🔍 View Project │
│ Visit: https://example.test.w... │
│ Dash: https://dash.cloudflare... │
│ │
│ 💻 Continue Developing │
│ Start dev server: pnpm run start │
│ Deploy again: pnpm run deploy │
│ │
│ 📖 Explore Documentation │
│ https://developers.cloudflare... │
│ │
│ 💬 Join our Community │
│ https://discord.cloudflare.com │
╰─────────────────────────────────────╯
"
`);
});
Expand Down
4 changes: 2 additions & 2 deletions packages/create-cloudflare/src/__tests__/git.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { updateStatus } from "@cloudflare/cli";
import { processArgument } from "@cloudflare/cli/args";
import { mockSpinner } from "helpers/__tests__/mocks";
import { processArgument } from "helpers/args";
import { runCommand } from "helpers/command";
import { beforeEach, describe, expect, test, vi } from "vitest";
import {
Expand All @@ -15,7 +15,7 @@ import {
import { createTestContext } from "./helpers";

vi.mock("helpers/command");
vi.mock("@cloudflare/cli/args");
vi.mock("helpers/args");
vi.mock("@cloudflare/cli/interactive");
vi.mock("@cloudflare/cli");

Expand Down
Loading

0 comments on commit 5632968

Please sign in to comment.