From 6ebc9616bed6847bc8016ba464e085113da8c625 Mon Sep 17 00:00:00 2001 From: James Date: Tue, 22 Aug 2023 14:32:10 -0500 Subject: [PATCH] C3: Automatically use the latest version of c3 if not running latest --- .changeset/nervous-insects-happen.md | 5 +++ .../create-cloudflare/e2e-tests/helpers.ts | 7 +++- packages/create-cloudflare/src/cli.ts | 42 +++++++++++++++++-- .../src/helpers/interactive.ts | 12 ++++-- packages/create-cloudflare/src/types.ts | 1 + 5 files changed, 59 insertions(+), 8 deletions(-) create mode 100644 .changeset/nervous-insects-happen.md diff --git a/.changeset/nervous-insects-happen.md b/.changeset/nervous-insects-happen.md new file mode 100644 index 000000000000..266576c4a746 --- /dev/null +++ b/.changeset/nervous-insects-happen.md @@ -0,0 +1,5 @@ +--- +"create-cloudflare": patch +--- + +C3: Checks for a newer version of create-cloudflare and uses it if available. This behavior can be suppressed with the --no-auto-update flag. diff --git a/packages/create-cloudflare/e2e-tests/helpers.ts b/packages/create-cloudflare/e2e-tests/helpers.ts index 53abcd322969..94e5fcd58323 100644 --- a/packages/create-cloudflare/e2e-tests/helpers.ts +++ b/packages/create-cloudflare/e2e-tests/helpers.ts @@ -4,6 +4,7 @@ import { tmpdir } from "os"; import { join } from "path"; import { spawn } from "cross-spawn"; import { spinnerFrames } from "helpers/interactive"; +import type { SpinnerStyle } from "helpers/interactive"; export const C3_E2E_PREFIX = "c3-e2e-"; @@ -122,8 +123,10 @@ export const condenseOutput = (lines: string[]) => { const filterLine = (line: string) => { // Remove all lines with spinners - for (const frame of spinnerFrames) { - if (line.includes(frame)) return false; + for (const spinnerType of Object.keys(spinnerFrames)) { + for (const frame of spinnerFrames[spinnerType as SpinnerStyle]) { + if (line.includes(frame)) return false; + } } // Remove empty lines diff --git a/packages/create-cloudflare/src/cli.ts b/packages/create-cloudflare/src/cli.ts index c8c247a09d56..33acbaeca6dd 100644 --- a/packages/create-cloudflare/src/cli.ts +++ b/packages/create-cloudflare/src/cli.ts @@ -1,9 +1,10 @@ #!/usr/bin/env node -// import { TextPrompt, SelectPrompt, ConfirmPrompt } from "@clack/core"; import Haikunator from "haikunator"; import { crash, logRaw, startSection } from "helpers/cli"; import { dim } from "helpers/colors"; -import { processArgument } from "helpers/interactive"; +import { runCommand } from "helpers/command"; +import { processArgument, spinner, spinnerFrames } from "helpers/interactive"; +import semver from "semver"; import yargs from "yargs"; import { hideBin } from "yargs/helpers"; import { version } from "../package.json"; @@ -16,6 +17,7 @@ export const C3_DEFAULTS = { projectName: new Haikunator().haikunate({ tokenHex: true }), type: "hello-world", framework: "angular", + autoUpdate: true, deploy: true, git: true, open: true, @@ -30,6 +32,39 @@ const WRANGLER_DEFAULTS = { export const main = async (argv: string[]) => { const args = await parseArgs(argv); + // Print a newline + logRaw(""); + + if (args.autoUpdate && (await isUpdateAvailable())) { + await runLatest(); + } else { + await runCli(args); + } +}; + +const isUpdateAvailable = async () => { + if (process.env.VITEST) { + return false; + } + + // Use a spinner when running this check since it may take some time + const s = spinner(spinnerFrames.vertical); + s.start("Checking for newer version"); + const latestVersion = await runCommand( + `npm info create-cloudflare@latest dist-tags.latest`, + { silent: true, useSpinner: false } + ); + s.stop(); + + return semver.gt(latestVersion, version); +}; + +export const runLatest = async () => { + const args = process.argv.slice(2); + await runCommand(`npm create cloudflare@latest ${args.join(" ")}`); +}; + +export const runCli = async (args: Partial) => { printBanner(); const projectName = await processArgument(args, "projectName", { @@ -79,7 +114,7 @@ export const main = async (argv: string[]) => { }; const printBanner = () => { - logRaw(dim(`\nusing create-cloudflare version ${version}\n`)); + logRaw(dim(`using create-cloudflare version ${version}\n`)); startSection(`Create an application with Cloudflare`, "Step 1 of 3"); }; @@ -91,6 +126,7 @@ export const parseArgs = async (argv: string[]): Promise> => { .option("type", { type: "string" }) .option("framework", { type: "string" }) .option("deploy", { type: "boolean" }) + .option("auto-update", { type: "boolean", default: C3_DEFAULTS.autoUpdate }) .option("ts", { type: "boolean" }) .option("git", { type: "boolean" }) .option("open", { diff --git a/packages/create-cloudflare/src/helpers/interactive.ts b/packages/create-cloudflare/src/helpers/interactive.ts index eda0fdc55c1c..fc582238a694 100644 --- a/packages/create-cloudflare/src/helpers/interactive.ts +++ b/packages/create-cloudflare/src/helpers/interactive.ts @@ -259,10 +259,16 @@ const getConfirmRenderers = (config: ConfirmPromptConfig) => { }; }; -export const spinnerFrames = ["┤", "┘", "┴", "└", "├", "┌", "┬", "┐"]; +export type SpinnerStyle = keyof typeof spinnerFrames; + +export const spinnerFrames = { + clockwise: ["┤", "┘", "┴", "└", "├", "┌", "┬", "┐"], + vertical: ["▁", "▃", "▄", "▅", "▆", "▇", "▆", "▅", "▄", "▃"], +}; + const ellipsisFrames = ["", ".", "..", "...", " ..", " .", ""]; -export const spinner = () => { +export const spinner = (frames: string[] = spinnerFrames.clockwise) => { // Alternative animations we considered. Keeping around in case we // introduce different animations for different use cases. // const frames = ["▁", "▃", "▄", "▅", "▆", "▇", "▆", "▅", "▄", "▃"]; @@ -296,7 +302,7 @@ export const spinner = () => { clearLoop(); loop = setInterval(() => { index++; - const spinnerFrame = spinnerFrames[index % spinnerFrames.length]; + const spinnerFrame = frames[index % frames.length]; const ellipsisFrame = ellipsisFrames[index % ellipsisFrames.length]; if (msg) { diff --git a/packages/create-cloudflare/src/types.ts b/packages/create-cloudflare/src/types.ts index e213b9df3de0..1bd43140279a 100644 --- a/packages/create-cloudflare/src/types.ts +++ b/packages/create-cloudflare/src/types.ts @@ -8,6 +8,7 @@ export type C3Args = { deploy?: boolean; open?: boolean; git?: boolean; + autoUpdate?: boolean; // pages specific framework?: string; // workers specific