From d45a13e4fab864f7fff83cfd7168234b09a6f6d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Jona=C5=A1?= Date: Thu, 18 Jan 2024 20:12:31 +0100 Subject: [PATCH] feat(core): extend nxCloud prompt to include basic CI workflow options (#21094) --- docs/generated/cli/create-nx-workspace.md | 14 +-- .../nx/documents/create-nx-workspace.md | 14 +-- .../1-code-generation.md | 2 +- .../angular-standalone.md | 2 +- .../angular-tutorial/angular-monorepo.md | 2 +- docs/shared/guides/remix.md | 2 +- .../node-server-tutorial/1-code-generation.md | 2 +- .../react-standalone.md | 2 +- docs/shared/react-tutorial/react-monorepo.md | 2 +- docs/shared/recipes/add-stack/add-astro.md | 2 +- docs/shared/recipes/add-stack/add-svelte.md | 6 +- .../recipes/module-federation-with-ssr.md | 2 +- .../vue-standalone-tutorial/vue-standalone.md | 2 +- e2e/nx-init/src/nx-init-react.test.ts | 10 +- e2e/utils/create-project-utils.ts | 9 +- .../src/create-nx-workspace.test.ts | 11 --- .../create-nx-plugin/bin/create-nx-plugin.ts | 17 ++-- .../bin/create-nx-workspace.ts | 12 +-- .../create-nx-workspace/src/create-preset.ts | 2 +- .../src/create-workspace-options.ts | 9 +- .../src/create-workspace.ts | 20 ++-- .../src/internal-utils/prompts.ts | 93 ++++++------------- .../src/internal-utils/yargs-options.ts | 21 ++--- .../src/utils/ci/ci-list.ts | 9 -- .../src/utils/ci/setup-ci.ts | 1 - .../src/utils/nx/ab-testing.ts | 61 +++++++++--- .../src/utils/nx/nx-cloud.ts | 3 +- .../connect/connect-to-nx-cloud.ts | 69 +++++++++----- .../nx/src/command-line/connect/view-logs.ts | 33 ++----- .../init/implementation/add-nx-to-monorepo.ts | 9 +- .../init/implementation/add-nx-to-nest.ts | 14 +-- .../init/implementation/add-nx-to-npm-repo.ts | 9 +- .../init/implementation/angular/index.ts | 5 +- .../angular/legacy-angular-versions.ts | 9 +- .../init/implementation/react/index.ts | 8 +- .../command-line/init/implementation/utils.ts | 24 ----- packages/nx/src/command-line/init/init-v2.ts | 5 +- .../nx/src/command-line/migrate/migrate.ts | 16 +--- packages/nx/src/utils/ab-testing.ts | 63 +++++++++---- .../bin/index.ts__tmpl__ | 2 +- .../files/src/__pluginName__.spec.ts__tmpl__ | 2 +- 41 files changed, 276 insertions(+), 324 deletions(-) delete mode 100644 packages/create-nx-workspace/src/utils/ci/ci-list.ts diff --git a/docs/generated/cli/create-nx-workspace.md b/docs/generated/cli/create-nx-workspace.md index b27439a06406b..a506f37afd824 100644 --- a/docs/generated/cli/create-nx-workspace.md +++ b/docs/generated/cli/create-nx-workspace.md @@ -37,14 +37,6 @@ Type: `string` Bundler to be used to build the app -### ci - -Type: `string` - -Choices: [github, circleci, azure, bitbucket-pipelines, gitlab] - -Generate a CI workflow file - ### commit.email Type: `string` @@ -127,9 +119,11 @@ Generate a 'src/' directory for Next.js ### nxCloud -Type: `boolean` +Type: `string` + +Choices: [yes, github, circleci, skip] -Enable remote caching to make your CI faster +Do you want Nx Cloud to make your CI fast? ### packageManager diff --git a/docs/generated/packages/nx/documents/create-nx-workspace.md b/docs/generated/packages/nx/documents/create-nx-workspace.md index b27439a06406b..a506f37afd824 100644 --- a/docs/generated/packages/nx/documents/create-nx-workspace.md +++ b/docs/generated/packages/nx/documents/create-nx-workspace.md @@ -37,14 +37,6 @@ Type: `string` Bundler to be used to build the app -### ci - -Type: `string` - -Choices: [github, circleci, azure, bitbucket-pipelines, gitlab] - -Generate a CI workflow file - ### commit.email Type: `string` @@ -127,9 +119,11 @@ Generate a 'src/' directory for Next.js ### nxCloud -Type: `boolean` +Type: `string` + +Choices: [yes, github, circleci, skip] -Enable remote caching to make your CI faster +Do you want Nx Cloud to make your CI fast? ### packageManager diff --git a/docs/shared/angular-standalone-tutorial/1-code-generation.md b/docs/shared/angular-standalone-tutorial/1-code-generation.md index 22f8267173e26..3b76c14994999 100644 --- a/docs/shared/angular-standalone-tutorial/1-code-generation.md +++ b/docs/shared/angular-standalone-tutorial/1-code-generation.md @@ -38,7 +38,7 @@ Run the command `npx create-nx-workspace@latest` and when prompted, provide the ✔ Default stylesheet format · css ✔ Would you like to use Standalone Components in your application? · No ✔ Would you like to add routing? · Yes -✔ Enable remote caching to make your CI faster · Yes +✔ Do you want Nx Cloud to make your CI fast? · Yes ``` {% card title="Opting into Nx Cloud" description="You will also be prompted whether to add Nx Cloud to your workspace. We won't address this in this tutorial, but you can see the introduction to Nx Cloud for more details." url="/ci/intro/ci-with-nx" /%} diff --git a/docs/shared/angular-standalone-tutorial/angular-standalone.md b/docs/shared/angular-standalone-tutorial/angular-standalone.md index abaf304f2145d..f12938b218b10 100644 --- a/docs/shared/angular-standalone-tutorial/angular-standalone.md +++ b/docs/shared/angular-standalone-tutorial/angular-standalone.md @@ -40,7 +40,7 @@ Create a new Angular application with the following command: ✔ Default stylesheet format · css ✔ Do you want to enable Server-Side Rendering (SSR) and Static Site Generation (SSG/Prerendering)? · No ✔ Test runner to use for end to end (E2E) tests · cypress -✔ Enable remote caching to make your CI faster · Yes +✔ Do you want Nx Cloud to make your CI fast? · Yes ``` You get asked a few questions that help Nx preconfigure your new Angular application. These include diff --git a/docs/shared/angular-tutorial/angular-monorepo.md b/docs/shared/angular-tutorial/angular-monorepo.md index 16e892e4a6ae8..9c2bdbd017020 100644 --- a/docs/shared/angular-tutorial/angular-monorepo.md +++ b/docs/shared/angular-tutorial/angular-monorepo.md @@ -59,7 +59,7 @@ Create a new Angular monorepo with the following command: ✔ Default stylesheet format · css ✔ Do you want to enable Server-Side Rendering (SSR) and Static Site Generation (SSG/Prerendering)? · No ✔ Test runner to use for end to end (E2E) tests · cypress -✔ Enable remote caching to make your CI faster · Yes +✔ Do you want Nx Cloud to make your CI fast? · Yes ``` Let's name the initial application `angular-store`. In this tutorial we're going to use `cypress` for e2e tests and `css` for styling. The above command generates the following structure: diff --git a/docs/shared/guides/remix.md b/docs/shared/guides/remix.md index de0ec647f245a..e0e4d08671217 100644 --- a/docs/shared/guides/remix.md +++ b/docs/shared/guides/remix.md @@ -7,7 +7,7 @@ In this recipe, we'll show you how to create a [Remix](https://remix.run) applic ```{% command="npx create-nx-workspace acme --preset=apps" path="~/" %} > NX Let's create a new workspace [https://nx.dev/getting-started/intro] -✔ Enable remote caching to make your CI faster · Yes +✔ Do you want Nx Cloud to make your CI fast? · Yes > NX Creating your v16.3.2 workspace. diff --git a/docs/shared/node-server-tutorial/1-code-generation.md b/docs/shared/node-server-tutorial/1-code-generation.md index d3147f6547c74..28ce09252ca1e 100644 --- a/docs/shared/node-server-tutorial/1-code-generation.md +++ b/docs/shared/node-server-tutorial/1-code-generation.md @@ -44,7 +44,7 @@ Run the command `npx create-nx-workspace@latest` and when prompted, provide the ✔ What framework should be used? · express ✔ Standalone project or integrated monorepo? · standalone ✔ Would you like to generate a Dockerfile? [https://docs.docker.com/] · Yes -✔ Enable remote caching to make your CI faster · Yes +✔ Do you want Nx Cloud to make your CI fast? · Yes ``` {% card title="Opting into Nx Cloud" description="You will also be prompted whether to add Nx Cloud to your workspace. We won't address this in this tutorial, but you can see the introduction to Nx Cloud for more details." url="/ci/intro/ci-with-nx" /%} diff --git a/docs/shared/react-standalone-tutorial/react-standalone.md b/docs/shared/react-standalone-tutorial/react-standalone.md index d79f341a515eb..158c215e697df 100644 --- a/docs/shared/react-standalone-tutorial/react-standalone.md +++ b/docs/shared/react-standalone-tutorial/react-standalone.md @@ -44,7 +44,7 @@ Create a new standalone React application with the following command: ✔ Which bundler would you like to use? · vite ✔ Test runner to use for end to end (E2E) tests · cypress ✔ Default stylesheet format · css -✔ Enable remote caching to make your CI faster · Yes +✔ Do you want Nx Cloud to make your CI fast? · Yes ``` You can choose any bundler you like. In this tutorial we're going to use Vite. The above command generates the following structure: diff --git a/docs/shared/react-tutorial/react-monorepo.md b/docs/shared/react-tutorial/react-monorepo.md index 5cdc12a503202..8b6a47e9e3d41 100644 --- a/docs/shared/react-tutorial/react-monorepo.md +++ b/docs/shared/react-tutorial/react-monorepo.md @@ -61,7 +61,7 @@ Create a new React monorepo with the following command: ✔ Which bundler would you like to use? · vite ✔ Test runner to use for end to end (E2E) tests · cypress ✔ Default stylesheet format · css -✔ Enable remote caching to make your CI faster · Yes +✔ Do you want Nx Cloud to make your CI fast? · Yes ``` Let's name the initial application `react-store`. In this tutorial we're going to use `vite` as a bundler, `cypress` for e2e tests and `css` for styling. The above command generates the following structure: diff --git a/docs/shared/recipes/add-stack/add-astro.md b/docs/shared/recipes/add-stack/add-astro.md index e9020f456da4c..db41d689f5937 100644 --- a/docs/shared/recipes/add-stack/add-astro.md +++ b/docs/shared/recipes/add-stack/add-astro.md @@ -39,7 +39,7 @@ We can leverage [`nx init`](/recipes/adopting-nx/adding-to-existing-project#inst ✔ Which of the following scripts are cacheable? (Produce the same output given the same input, e.g. build, test and lint usually are, serve and start are not). You can use spacebar to select one or more scripts. · build ✔ Does the "build" script create any outputs? If not, leave blank, otherwise provide a path (e.g. dist, lib, build, coverage) · dist -✔ Enable remote caching to make your CI faster · No +✔ Would you like remote caching to make your build faster? · Yes > NX 📦 Installing dependencies diff --git a/docs/shared/recipes/add-stack/add-svelte.md b/docs/shared/recipes/add-stack/add-svelte.md index a54eb7d5c5f59..d1cb89d1658b6 100644 --- a/docs/shared/recipes/add-stack/add-svelte.md +++ b/docs/shared/recipes/add-stack/add-svelte.md @@ -28,21 +28,21 @@ Because we are not using a Nx plugin for Svelte, there are a few items we'll hav {%tab label="npm"%} ```shell -npx create-nx-workspace@latest acme --preset=ts-standalone --nx-cloud=true +npx create-nx-workspace@latest acme --preset=ts-standalone --nx-cloud=yes ``` {% /tab %} {%tab label="yarn"%} ```shell -npx create-nx-workspace@latest acme --preset=ts-standalone --nx-cloud=true --pm yarn +npx create-nx-workspace@latest acme --preset=ts-standalone --nx-cloud=yes --pm yarn ``` {% /tab %} {%tab label="pnpm"%} ```shell -npx create-nx-workspace@latest acme --preset=ts-standalone --nx-cloud=true --pm pnpm +npx create-nx-workspace@latest acme --preset=ts-standalone --nx-cloud=yes --pm pnpm ``` {% /tab %} diff --git a/docs/shared/recipes/module-federation-with-ssr.md b/docs/shared/recipes/module-federation-with-ssr.md index d68e97c421922..20c24fc5418fb 100644 --- a/docs/shared/recipes/module-federation-with-ssr.md +++ b/docs/shared/recipes/module-federation-with-ssr.md @@ -15,7 +15,7 @@ Run the following command with the options listed to create an empty workspace. ✔ Where would you like to create your workspace? · myorg ✔ Which stack do you want to use? · none ✔ Package-based or integrated? · integrated -✔ Enable remote caching to make your CI faster · Yes +✔ Do you want Nx Cloud to make your CI fast? · Yes ``` {% card title="Opting into Nx Cloud" description="You will also be prompted whether to add Nx Cloud to your workspace. We won't address this in this recipe, but you can see the introduction to Nx Cloud for more details." url="/ci/intro/ci-with-nx" /%} diff --git a/docs/shared/vue-standalone-tutorial/vue-standalone.md b/docs/shared/vue-standalone-tutorial/vue-standalone.md index dbda3675d5b7b..ca7171706b8de 100644 --- a/docs/shared/vue-standalone-tutorial/vue-standalone.md +++ b/docs/shared/vue-standalone-tutorial/vue-standalone.md @@ -30,7 +30,7 @@ Create a new Vue application with the following command: ✔ Test runner to use for end to end (E2E) tests · cypress ✔ Default stylesheet format · css -✔ Enable remote caching to make your CI faster · Yes +✔ Do you want Nx Cloud to make your CI fast? · Yes > NX Creating your v17.0.0 workspace. diff --git a/e2e/nx-init/src/nx-init-react.test.ts b/e2e/nx-init/src/nx-init-react.test.ts index 063ab8b36144d..b90ce60c1b460 100644 --- a/e2e/nx-init/src/nx-init-react.test.ts +++ b/e2e/nx-init/src/nx-init-react.test.ts @@ -32,7 +32,7 @@ describe('nx init (for React)', () => { const craToNxOutput = runCommand( `${ pmc.runUninstalledPackage - } nx@${getPublishedVersion()} init --nxCloud=false --integrated --vite=false` + } nx@${getPublishedVersion()} init --nxCloud=skip --integrated --vite=false` ); expect(craToNxOutput).toContain('🎉 Done!'); @@ -65,7 +65,7 @@ describe('nx init (for React)', () => { const craToNxOutput = runCommand( `${ pmc.runUninstalledPackage - } nx@${getPublishedVersion()} init --nxCloud=false --integrated` + } nx@${getPublishedVersion()} init --nxCloud=skip --integrated` ); expect(craToNxOutput).toContain('🎉 Done!'); @@ -97,7 +97,7 @@ describe('nx init (for React)', () => { runCommand( `${ pmc.runUninstalledPackage - } nx@${getPublishedVersion()} init --nxCloud=false --force --integrated` + } nx@${getPublishedVersion()} init --nxCloud=skip --force --integrated` ); const viteConfig = readFile(`apps/${appName}/vite.config.js`); @@ -115,7 +115,7 @@ describe('nx init (for React)', () => { const craToNxOutput = runCommand( `${ pmc.runUninstalledPackage - } nx@${getPublishedVersion()} init --nxCloud=false --vite=false` + } nx@${getPublishedVersion()} init --nxCloud=skip --vite=false` ); expect(craToNxOutput).toContain('🎉 Done!'); @@ -137,7 +137,7 @@ describe('nx init (for React)', () => { const craToNxOutput = runCommand( `${ pmc.runUninstalledPackage - } nx@${getPublishedVersion()} init --nxCloud=false --vite` + } nx@${getPublishedVersion()} init --nxCloud=skip --vite` ); expect(craToNxOutput).toContain('🎉 Done!'); diff --git a/e2e/utils/create-project-utils.ts b/e2e/utils/create-project-utils.ts index 0a557c678c7b9..61776a53b0834 100644 --- a/e2e/utils/create-project-utils.ts +++ b/e2e/utils/create-project-utils.ts @@ -221,7 +221,6 @@ export function runCreateWorkspace( base, packageManager, extraArgs, - ci, useDetectedPm = false, cwd = e2eCwd, bundler, @@ -240,7 +239,6 @@ export function runCreateWorkspace( base?: string; packageManager?: 'npm' | 'yarn' | 'pnpm'; extraArgs?: string; - ci?: 'azure' | 'github' | 'circleci'; useDetectedPm?: boolean; cwd?: string; bundler?: 'webpack' | 'vite'; @@ -258,16 +256,13 @@ export function runCreateWorkspace( const pm = getPackageManagerCommand({ packageManager }); - let command = `${pm.createWorkspace} ${name} --preset=${preset} --no-nxCloud --no-interactive`; + let command = `${pm.createWorkspace} ${name} --preset=${preset} --nxCloud=skip --no-interactive`; if (appName) { command += ` --appName=${appName}`; } if (style) { command += ` --style=${style}`; } - if (ci) { - command += ` --ci=${ci}`; - } if (bundler) { command += ` --bundler=${bundler}`; @@ -366,7 +361,7 @@ export function runCreatePlugin( let command = `${ pm.runUninstalledPackage - } create-nx-plugin@${getPublishedVersion()} ${name} --no-nxCloud`; + } create-nx-plugin@${getPublishedVersion()} ${name} --nxCloud=skip`; if (packageManager && !useDetectedPm) { command += ` --package-manager=${packageManager}`; diff --git a/e2e/workspace-create/src/create-nx-workspace.test.ts b/e2e/workspace-create/src/create-nx-workspace.test.ts index a621c57b4e53b..de6e7aa57602b 100644 --- a/e2e/workspace-create/src/create-nx-workspace.test.ts +++ b/e2e/workspace-create/src/create-nx-workspace.test.ts @@ -373,17 +373,6 @@ describe('create-nx-workspace', () => { process.env.SELECTED_PM = packageManager; }); - it('should return error when ci workflow is selected but no cloud is set up', () => { - const wsName = uniq('github'); - runCreateWorkspace(wsName, { - preset: 'apps', - packageManager, - ci: 'circleci', - }); - checkFilesExist('package.json'); - checkFilesDoNotExist('.circleci/config.yml'); - }); - describe('Use detected package manager', () => { function setupProject(envPm: 'npm' | 'yarn' | 'pnpm') { process.env.SELECTED_PM = envPm; diff --git a/packages/create-nx-plugin/bin/create-nx-plugin.ts b/packages/create-nx-plugin/bin/create-nx-plugin.ts index 63335256c891e..ee2c29a34e21b 100644 --- a/packages/create-nx-plugin/bin/create-nx-plugin.ts +++ b/packages/create-nx-plugin/bin/create-nx-plugin.ts @@ -4,14 +4,12 @@ import enquirer = require('enquirer'); import yargs = require('yargs'); import { - determineCI, determineDefaultBase, determineNxCloud, determinePackageManager, } from 'create-nx-workspace/src/internal-utils/prompts'; import { withAllPrompts, - withCI, withGitOptions, withNxCloud, withOptions, @@ -19,7 +17,7 @@ import { } from 'create-nx-workspace/src/internal-utils/yargs-options'; import { createWorkspace, CreateWorkspaceOptions } from 'create-nx-workspace'; import { output } from 'create-nx-workspace/src/utils/output'; -import { CI } from 'create-nx-workspace/src/utils/ci/ci-list'; +import { NxCloud } from 'create-nx-workspace/src/utils/nx/nx-cloud'; import type { PackageManager } from 'create-nx-workspace/src/utils/package-manager'; import { showNxWarning } from 'create-nx-workspace/src/utils/nx/show-nx-warning'; import { printNxCloudSuccessMessage } from 'create-nx-workspace/src/utils/nx/nx-cloud'; @@ -82,9 +80,8 @@ interface CreateNxPluginArguments { pluginName: string; createPackageName?: string; packageManager: PackageManager; - ci: CI; allPrompts: boolean; - nxCloud: boolean; + nxCloud: NxCloud; } export const commandsObject: yargs.Argv = yargs @@ -110,7 +107,6 @@ export const commandsObject: yargs.Argv = yargs type: 'string', }), withNxCloud, - withCI, withAllPrompts, withPackageManager, withGitOptions @@ -160,8 +156,11 @@ async function main(parsedArgs: yargs.Arguments) { await recordStat({ nxVersion, command: 'create-nx-workspace', - useCloud: parsedArgs.nxCloud, - meta: messages.codeOfSelectedPromptMessage('nxCloudCreation'), + useCloud: parsedArgs.nxCloud !== 'skip', + meta: [ + messages.codeOfSelectedPromptMessage('setupCI'), + messages.codeOfSelectedPromptMessage('setupNxCloud'), + ], }); if (parsedArgs.nxCloud && workspaceInfo.nxCloudInfo) { @@ -184,7 +183,6 @@ async function normalizeArgsMiddleware( const packageManager = await determinePackageManager(argv); const defaultBase = await determineDefaultBase(argv); const nxCloud = await determineNxCloud(argv); - const ci = await determineCI(argv, nxCloud); Object.assign(argv, { pluginName, @@ -192,7 +190,6 @@ async function normalizeArgsMiddleware( nxCloud, packageManager, defaultBase, - ci, } as Partial); } catch (e) { console.error(e); diff --git a/packages/create-nx-workspace/bin/create-nx-workspace.ts b/packages/create-nx-workspace/bin/create-nx-workspace.ts index c513910ba706d..976ccdffbca49 100644 --- a/packages/create-nx-workspace/bin/create-nx-workspace.ts +++ b/packages/create-nx-workspace/bin/create-nx-workspace.ts @@ -12,14 +12,12 @@ import { pointToTutorialAndCourse } from '../src/utils/preset/point-to-tutorial- import { yargsDecorator } from './decorator'; import { getThirdPartyPreset } from '../src/utils/preset/get-third-party-preset'; import { - determineCI, determineDefaultBase, determineNxCloud, determinePackageManager, } from '../src/internal-utils/prompts'; import { withAllPrompts, - withCI, withGitOptions, withNxCloud, withOptions, @@ -179,7 +177,6 @@ export const commandsObject: yargs.Argv = yargs type: 'boolean', }), withNxCloud, - withCI, withAllPrompts, withPackageManager, withGitOptions @@ -230,8 +227,11 @@ async function main(parsedArgs: yargs.Arguments) { await recordStat({ nxVersion, command: 'create-nx-workspace', - useCloud: parsedArgs.nxCloud, - meta: messages.codeOfSelectedPromptMessage('nxCloudCreation'), + useCloud: parsedArgs.nxCloud !== 'skip', + meta: [ + messages.codeOfSelectedPromptMessage('setupCI'), + messages.codeOfSelectedPromptMessage('setupNxCloud'), + ], }); if (parsedArgs.nxCloud && workspaceInfo.nxCloudInfo) { @@ -315,13 +315,11 @@ async function normalizeArgsMiddleware( const packageManager = await determinePackageManager(argv); const defaultBase = await determineDefaultBase(argv); const nxCloud = await determineNxCloud(argv); - const ci = await determineCI(argv, nxCloud); Object.assign(argv, { nxCloud, packageManager, defaultBase, - ci, }); } catch (e) { console.error(e); diff --git a/packages/create-nx-workspace/src/create-preset.ts b/packages/create-nx-workspace/src/create-preset.ts index 68955738d47ef..3e03cb9ff04bd 100644 --- a/packages/create-nx-workspace/src/create-preset.ts +++ b/packages/create-nx-workspace/src/create-preset.ts @@ -14,7 +14,7 @@ export async function createPreset( packageManager: PackageManager, directory: string ): Promise { - const { skipGit, ci, commit, nxCloud, ...restArgs } = parsedArgs; + const { skipGit, commit, nxCloud, ...restArgs } = parsedArgs; let args = unparse({ interactive: true, diff --git a/packages/create-nx-workspace/src/create-workspace-options.ts b/packages/create-nx-workspace/src/create-workspace-options.ts index 3ada0b0678176..3cccbbec38d01 100644 --- a/packages/create-nx-workspace/src/create-workspace-options.ts +++ b/packages/create-nx-workspace/src/create-workspace-options.ts @@ -1,20 +1,15 @@ +import { NxCloud } from './utils/nx/nx-cloud'; import { PackageManager } from './utils/package-manager'; -import { CI } from './utils/ci/ci-list'; export interface CreateWorkspaceOptions { name: string; // Workspace name (e.g. org name) packageManager: PackageManager; // Package manager to use - nxCloud: boolean; // Enable Nx Cloud + nxCloud: NxCloud; // Enable Nx Cloud /** * @description Enable interactive mode with presets * @default true */ interactive?: boolean; // Enable interactive mode with presets - /** - * @description Generate a CI workflow file - * @default '' - */ - ci?: CI; /** * @description Default base to use for new projects. e.g. main, master * @default 'main' diff --git a/packages/create-nx-workspace/src/create-workspace.ts b/packages/create-nx-workspace/src/create-workspace.ts index 08a4a2aed6b98..12824ac4a00f9 100644 --- a/packages/create-nx-workspace/src/create-workspace.ts +++ b/packages/create-nx-workspace/src/create-workspace.ts @@ -17,7 +17,6 @@ export async function createWorkspace( packageManager, name, nxCloud, - ci = '', skipGit = false, defaultBase = 'main', commit, @@ -47,16 +46,17 @@ export async function createWorkspace( } let nxCloudInstallRes; - if (nxCloud) { + if (nxCloud !== 'skip') { nxCloudInstallRes = await setupNxCloud(directory, packageManager); - } - if (ci) { - await setupCI( - directory, - ci, - packageManager, - nxCloud && nxCloudInstallRes?.code === 0 - ); + + if (nxCloud !== 'yes') { + await setupCI( + directory, + nxCloud, + packageManager, + nxCloudInstallRes?.code === 0 + ); + } } if (!skipGit && commit) { try { diff --git a/packages/create-nx-workspace/src/internal-utils/prompts.ts b/packages/create-nx-workspace/src/internal-utils/prompts.ts index 4063ac6672095..ffb2060a46e8a 100644 --- a/packages/create-nx-workspace/src/internal-utils/prompts.ts +++ b/packages/create-nx-workspace/src/internal-utils/prompts.ts @@ -1,6 +1,5 @@ import * as yargs from 'yargs'; -import { messages } from '../utils/nx/ab-testing'; -import { CI } from '../utils/ci/ci-list'; +import { MessageKey, messages } from '../utils/nx/ab-testing'; import { output } from '../utils/output'; import { deduceDefaultBase } from '../utils/git/default-base'; import { @@ -10,81 +9,43 @@ import { } from '../utils/package-manager'; import { stringifyCollection } from '../utils/string-utils'; import enquirer = require('enquirer'); +import { NxCloud } from '../utils/nx/nx-cloud'; +import chalk = require('chalk'); export async function determineNxCloud( - parsedArgs: yargs.Arguments<{ nxCloud: boolean }> -): Promise { + parsedArgs: yargs.Arguments<{ nxCloud: NxCloud }> +): Promise { if (parsedArgs.nxCloud === undefined) { - return enquirer - .prompt<{ NxCloud: 'Yes' | 'No' }>([ - { - name: 'NxCloud', - message: messages.getPromptMessage('nxCloudCreation'), - type: 'autocomplete', - choices: [ - { - name: 'Yes', - hint: 'I want faster builds', - }, - - { - name: 'No', - }, - ], - initial: 'Yes' as any, - }, - ]) - .then((a) => a.NxCloud === 'Yes'); + return nxCloudPrompt('setupCI'); } else { return parsedArgs.nxCloud; } } -export async function determineCI( - parsedArgs: yargs.Arguments<{ ci?: CI; allPrompts?: boolean }>, - nxCloud: boolean -): Promise { - if (!nxCloud) { - if (parsedArgs.ci) { - output.warn({ - title: 'Invalid CI value', - bodyLines: [ - `CI option only works when Nx Cloud is enabled.`, - `The value provided will be ignored.`, - ], - }); - } - return ''; - } +async function nxCloudPrompt(key: MessageKey): Promise { + const { message, choices, initial, fallback, footer, hint } = + messages.getPrompt(key); - if (parsedArgs.ci) { - return parsedArgs.ci; + const promptConfig = { + name: 'NxCloud', + message, + type: 'autocomplete', + choices, + initial, + } as any; // meeroslav: types in enquirer are not up to date + if (footer) { + promptConfig.footer = () => chalk.dim(footer); } - - if (parsedArgs.allPrompts) { - return ( - enquirer - .prompt<{ CI: string }>([ - { - name: 'CI', - message: `CI workflow file to generate? `, - type: 'autocomplete', - initial: '' as any, - choices: [ - { message: 'none', name: '' }, - { message: 'GitHub Actions', name: 'github' }, - { message: 'Circle CI', name: 'circleci' }, - { message: 'Azure DevOps', name: 'azure' }, - ], - }, - ]) - // enquirer ignores name and value if they are falsy and takes - // first field that has a truthy value, so wee need to explicitly - // check for none - .then((a: { CI: string }) => (a.CI !== 'none' ? a.CI : '')) - ); + if (hint) { + promptConfig.hint = () => chalk.dim(hint); } - return ''; + + return enquirer.prompt<{ NxCloud: NxCloud }>([promptConfig]).then((a) => { + if (fallback && a.NxCloud === fallback.value) { + return nxCloudPrompt(fallback.key); + } + return a.NxCloud; + }); } export async function determineDefaultBase( diff --git a/packages/create-nx-workspace/src/internal-utils/yargs-options.ts b/packages/create-nx-workspace/src/internal-utils/yargs-options.ts index baf639cf516de..2404969725600 100644 --- a/packages/create-nx-workspace/src/internal-utils/yargs-options.ts +++ b/packages/create-nx-workspace/src/internal-utils/yargs-options.ts @@ -1,25 +1,18 @@ import chalk = require('chalk'); import yargs = require('yargs'); -import { CreateWorkspaceOptions } from '../create-workspace-options'; -import { ciList } from '../utils/ci/ci-list'; -import { messages } from '../utils/nx/ab-testing'; +import { NxCloudChoices, messages } from '../utils/nx/ab-testing'; import { packageManagerList } from '../utils/package-manager'; export function withNxCloud(argv: yargs.Argv) { - const result = argv.option('nxCloud', { - describe: chalk.dim(messages.getPromptMessage('nxCloudCreation')), - type: 'boolean', - }); - return result; -} + const { message } = messages.getPrompt('setupCI'); -export function withCI(argv: yargs.Argv) { - return argv.option('ci', { - describe: chalk.dim`Generate a CI workflow file`, - choices: ciList, - defaultDescription: '', + const result = argv.option('nxCloud', { + alias: 'ci', + describe: chalk.dim(message), + choices: NxCloudChoices, type: 'string', }); + return result; } export function withAllPrompts(argv: yargs.Argv) { diff --git a/packages/create-nx-workspace/src/utils/ci/ci-list.ts b/packages/create-nx-workspace/src/utils/ci/ci-list.ts deleted file mode 100644 index 36332d9703c9f..0000000000000 --- a/packages/create-nx-workspace/src/utils/ci/ci-list.ts +++ /dev/null @@ -1,9 +0,0 @@ -export const ciList = [ - 'github', - 'circleci', - 'azure', - 'bitbucket-pipelines', - 'gitlab', -]; - -export type CI = typeof ciList[number]; diff --git a/packages/create-nx-workspace/src/utils/ci/setup-ci.ts b/packages/create-nx-workspace/src/utils/ci/setup-ci.ts index 391842b2148f2..fd375d2ff5e69 100644 --- a/packages/create-nx-workspace/src/utils/ci/setup-ci.ts +++ b/packages/create-nx-workspace/src/utils/ci/setup-ci.ts @@ -1,5 +1,4 @@ import * as ora from 'ora'; -import { join } from 'path'; import { execAndWait } from '../child-process-utils'; import { mapErrorToBodyLines } from '../error-utils'; diff --git a/packages/create-nx-workspace/src/utils/nx/ab-testing.ts b/packages/create-nx-workspace/src/utils/nx/ab-testing.ts index 32791f217e594..8e2c17d9277d8 100644 --- a/packages/create-nx-workspace/src/utils/nx/ab-testing.ts +++ b/packages/create-nx-workspace/src/utils/nx/ab-testing.ts @@ -1,26 +1,63 @@ import { isCI } from '../ci/is-ci'; +export const NxCloudChoices = ['yes', 'github', 'circleci', 'skip']; + const messageOptions = { - nxCloudCreation: [ + setupCI: [ + { + code: 'enable-nx-cloud', + message: `Do you want Nx Cloud to make your CI fast?`, + initial: 'github', + choices: [ + { value: 'yes', name: 'Yes, enable Nx Cloud' }, + { value: 'github', name: 'Yes, configure Nx Cloud for GitHub Actions' }, + { value: 'circleci', name: 'Yes, configure Nx Cloud for Circle CI' }, + { value: 'skip', name: 'Skip for now' }, + ], + footer: + '\nRead more about remote cache at https://nx.dev/ci/features/remote-cache', + hint: `\n(it's free and can be disabled any time)`, + fallback: undefined, + }, { - code: 'set-up-distributed-caching-ci', - message: `Enable remote caching to make your CI faster`, + code: 'set-up-ci', + message: `Set up CI with caching, distribution and test deflaking`, + initial: 'github', + choices: [ + { value: 'github', name: 'Yes, for GitHub Actions with Nx Cloud' }, + { value: 'circleci', name: 'Yes, for CircleCI with Nx Cloud' }, + { value: 'skip', name: 'Skip for now' }, + ], + footer: + '\nRead more about CI benefits with Nx at https://nx.dev/ci/intro/ci-with-nx', + hint: `\n(it's free and can be disabled any time)`, + fallback: { value: 'skip', key: 'setupNxCloud' }, }, ], - nxCloudMigration: [ + setupNxCloud: [ { - code: 'make-ci-faster', - message: `Enable remote caching to make your CI faster?`, + code: 'enable-caching', + message: `Would you like remote caching to make your build faster?`, + initial: 'yes', + choices: [ + { value: 'yes', name: 'Yes' }, + { value: 'skip', name: 'Skip for now' }, + ], + footer: + '\nRead more about remote caching at https://nx.dev/ci/features/remote-cache', + hint: `\n(it's free and can be disabled any time)`, + fallback: undefined, }, ], } as const; -type MessageKey = keyof typeof messageOptions; +export type MessageKey = keyof typeof messageOptions; +type MessageData = typeof messageOptions[MessageKey][number]; export class PromptMessages { private selectedMessages: { [key in MessageKey]?: number } = {}; - getPromptMessage(key: MessageKey): string { + getPrompt(key: MessageKey): MessageData { if (this.selectedMessages[key] === undefined) { if (process.env.NX_GENERATE_DOCS_PROCESS === 'true') { this.selectedMessages[key] = 0; @@ -30,13 +67,13 @@ export class PromptMessages { ); } } - return messageOptions[key][this.selectedMessages[key]!].message; + return messageOptions[key][this.selectedMessages[key]!]; } codeOfSelectedPromptMessage(key: MessageKey): string { const selected = this.selectedMessages[key]; if (selected === undefined) { - return messageOptions[key][0].code; + return ''; } else { return messageOptions[key][selected].code; } @@ -53,7 +90,7 @@ export async function recordStat(opts: { command: string; nxVersion: string; useCloud: boolean; - meta: string; + meta: string[]; }) { try { const major = Number(opts.nxVersion.split('.')[0]); @@ -71,7 +108,7 @@ export async function recordStat(opts: { command: opts.command, isCI: isCI(), useCloud: opts.useCloud, - meta: opts.meta, + meta: opts.meta.filter((v) => !!v).join(','), }); } catch (e) { if (process.env.NX_VERBOSE_LOGGING === 'true') { diff --git a/packages/create-nx-workspace/src/utils/nx/nx-cloud.ts b/packages/create-nx-workspace/src/utils/nx/nx-cloud.ts index b8c07aa0155ce..0d7dc9419db9b 100644 --- a/packages/create-nx-workspace/src/utils/nx/nx-cloud.ts +++ b/packages/create-nx-workspace/src/utils/nx/nx-cloud.ts @@ -1,10 +1,11 @@ import * as ora from 'ora'; -import { join } from 'path'; import { execAndWait } from '../child-process-utils'; import { output } from '../output'; import { getPackageManagerCommand, PackageManager } from '../package-manager'; import { mapErrorToBodyLines } from '../error-utils'; +export type NxCloud = 'yes' | 'github' | 'circleci' | 'skip'; + export async function setupNxCloud( directory: string, packageManager: PackageManager diff --git a/packages/nx/src/command-line/connect/connect-to-nx-cloud.ts b/packages/nx/src/command-line/connect/connect-to-nx-cloud.ts index 838c55faed8d5..e998387f7fab3 100644 --- a/packages/nx/src/command-line/connect/connect-to-nx-cloud.ts +++ b/packages/nx/src/command-line/connect/connect-to-nx-cloud.ts @@ -8,6 +8,14 @@ import { import { runNxSync } from '../../utils/child-process'; import { NxJsonConfiguration } from '../../config/nx-json'; import { NxArgs } from '../../utils/command-line-utils'; +import { + MessageKey, + MessageOptionKey, + recordStat, + messages, +} from '../../utils/ab-testing'; +import { nxVersion } from '../../utils/versions'; +import chalk = require('chalk'); export function onlyDefaultRunnerIsUsed(nxJson: NxJsonConfiguration) { const defaultRunner = nxJson.tasksRunnerOptions?.default?.runner; @@ -66,26 +74,43 @@ export async function connectToNxCloudCommand(): Promise { return true; } -export async function connectToNxCloudPrompt(prompt?: string) { - return await ( - await import('enquirer') - ) - .prompt([ - { - name: 'NxCloud', - message: prompt ?? `Enable remote caching to make your CI faster`, - type: 'autocomplete', - choices: [ - { - name: 'Yes', - hint: 'I want faster builds', - }, - { - name: 'No', - }, - ], - initial: 'Yes' as any, - }, - ]) - .then((a: { NxCloud: 'Yes' | 'No' }) => a.NxCloud === 'Yes'); +export async function connectToNxCloudWithPrompt(command: string) { + const setNxCloud = await nxCloudPrompt('setupNxCloud'); + const useCloud = setNxCloud ? await connectToNxCloudCommand() : false; + await recordStat({ + command, + nxVersion, + useCloud, + meta: messages.codeOfSelectedPromptMessage('setupNxCloud'), + }); +} + +export async function connectExistingRepoToNxCloudPrompt( + key: MessageKey = 'setupNxCloud' +): Promise { + return nxCloudPrompt(key).then((value: MessageOptionKey) => value === 'yes'); +} + +async function nxCloudPrompt(key: MessageKey): Promise { + const { message, choices, initial, footer, hint } = messages.getPrompt(key); + + const promptConfig = { + name: 'NxCloud', + message, + type: 'autocomplete', + choices, + initial, + } as any; // meeroslav: types in enquirer are not up to date + if (footer) { + promptConfig.footer = () => chalk.dim(footer); + } + if (hint) { + promptConfig.hint = () => chalk.dim(hint); + } + + return await (await import('enquirer')) + .prompt<{ NxCloud: MessageOptionKey }>([promptConfig]) + .then((a) => { + return a.NxCloud; + }); } diff --git a/packages/nx/src/command-line/connect/view-logs.ts b/packages/nx/src/command-line/connect/view-logs.ts index e8cb61c8048c4..567b4ade7f51c 100644 --- a/packages/nx/src/command-line/connect/view-logs.ts +++ b/packages/nx/src/command-line/connect/view-logs.ts @@ -4,6 +4,7 @@ import { isNxCloudUsed } from '../../utils/nx-cloud-utils'; import { output } from '../../utils/output'; import { runNxSync } from '../../utils/child-process'; import { readNxJson } from '../../config/nx-json'; +import { connectExistingRepoToNxCloudPrompt } from './connect-to-nx-cloud'; export async function viewLogs(): Promise { const cloudUsed = isNxCloudUsed(readNxJson()); @@ -17,31 +18,12 @@ export async function viewLogs(): Promise { return 1; } - const installCloud = await ( - await import('enquirer') - ) - .prompt([ - { - name: 'NxCloud', - message: `To view the logs, Nx needs to connect your workspace to Nx Cloud and upload the most recent run details.`, - type: 'autocomplete', - choices: [ - { - name: 'Yes', - hint: 'Connect to Nx Cloud and upload the run details', - }, - { - name: 'No', - }, - ], - initial: 'Yes' as any, - }, - ]) - .then((a: { NxCloud: 'Yes' | 'No' }) => a.NxCloud === 'Yes'); - - if (!installCloud) return; - - const pmc = getPackageManagerCommand(); + const setupNxCloud = await connectExistingRepoToNxCloudPrompt( + 'setupViewLogs' + ); + if (!setupNxCloud) { + return; + } try { output.log({ @@ -61,6 +43,7 @@ export async function viewLogs(): Promise { return 1; } + const pmc = getPackageManagerCommand(); execSync(`${pmc.exec} nx-cloud upload-and-show-run-details`, { stdio: [0, 1, 2], }); diff --git a/packages/nx/src/command-line/init/implementation/add-nx-to-monorepo.ts b/packages/nx/src/command-line/init/implementation/add-nx-to-monorepo.ts index b4ebe04b4e47e..0f3a0f555fa11 100644 --- a/packages/nx/src/command-line/init/implementation/add-nx-to-monorepo.ts +++ b/packages/nx/src/command-line/init/implementation/add-nx-to-monorepo.ts @@ -8,13 +8,13 @@ import { output } from '../../../utils/output'; import { getPackageManagerCommand } from '../../../utils/package-manager'; import { addDepsToPackageJson, - askAboutNxCloud, createNxJsonFile, initCloud, printFinalMessage, runInstall, updateGitIgnore, } from './utils'; +import { connectExistingRepoToNxCloudPrompt } from '../../connect/connect-to-nx-cloud'; type Options = Pick; @@ -73,13 +73,16 @@ export async function addNxToMonorepo(options: Options) { )[scriptName]; } - useNxCloud = options.nxCloud ?? (await askAboutNxCloud()); + useNxCloud = + options.nxCloud ?? (await connectExistingRepoToNxCloudPrompt()); } else { targetDefaults = []; cacheableOperations = options.cacheable ?? []; useNxCloud = options.nxCloud ?? - (options.interactive ? await askAboutNxCloud() : false); + (options.interactive + ? await connectExistingRepoToNxCloudPrompt() + : false); } createNxJsonFile( diff --git a/packages/nx/src/command-line/init/implementation/add-nx-to-nest.ts b/packages/nx/src/command-line/init/implementation/add-nx-to-nest.ts index cca79d1071421..3e14afcd0f5a4 100644 --- a/packages/nx/src/command-line/init/implementation/add-nx-to-nest.ts +++ b/packages/nx/src/command-line/init/implementation/add-nx-to-nest.ts @@ -2,10 +2,7 @@ import * as enquirer from 'enquirer'; import { unlinkSync, writeFileSync } from 'fs-extra'; import { join } from 'path'; import { InitArgs } from '../init-v1'; -import { - NrwlJsPluginConfig, - NxJsonConfiguration, -} from '../../../config/nx-json'; +import { NxJsonConfiguration } from '../../../config/nx-json'; import { ProjectConfiguration } from '../../../config/workspace-json-project-json'; import { fileExists, @@ -17,7 +14,6 @@ import { PackageJson } from '../../../utils/package-json'; import { getPackageManagerCommand } from '../../../utils/package-manager'; import { addDepsToPackageJson, - askAboutNxCloud, createNxJsonFile, initCloud, markRootPackageJsonAsNxProject, @@ -26,6 +22,7 @@ import { updateGitIgnore, } from './utils'; import { nxVersion } from '../../../utils/versions'; +import { connectExistingRepoToNxCloudPrompt } from '../../connect/connect-to-nx-cloud'; type Options = Pick; type NestCLIConfiguration = any; @@ -101,12 +98,15 @@ export async function addNxToNest(options: Options, packageJson: PackageJson) { )[scriptName]; } - useNxCloud = options.nxCloud ?? (await askAboutNxCloud()); + useNxCloud = + options.nxCloud ?? (await connectExistingRepoToNxCloudPrompt()); } else { cacheableOperations = options.cacheable ?? []; useNxCloud = options.nxCloud ?? - (options.interactive ? await askAboutNxCloud() : false); + (options.interactive + ? await connectExistingRepoToNxCloudPrompt() + : false); } createNxJsonFile( diff --git a/packages/nx/src/command-line/init/implementation/add-nx-to-npm-repo.ts b/packages/nx/src/command-line/init/implementation/add-nx-to-npm-repo.ts index 782213f09d36b..1574337600add 100644 --- a/packages/nx/src/command-line/init/implementation/add-nx-to-npm-repo.ts +++ b/packages/nx/src/command-line/init/implementation/add-nx-to-npm-repo.ts @@ -5,7 +5,6 @@ import { output } from '../../../utils/output'; import { getPackageManagerCommand } from '../../../utils/package-manager'; import { addDepsToPackageJson, - askAboutNxCloud, createNxJsonFile, initCloud, markRootPackageJsonAsNxProject, @@ -13,6 +12,7 @@ import { runInstall, updateGitIgnore, } from './utils'; +import { connectExistingRepoToNxCloudPrompt } from '../../connect/connect-to-nx-cloud'; type Options = Pick; @@ -61,12 +61,15 @@ export async function addNxToNpmRepo(options: Options) { )[scriptName]; } - useNxCloud = options.nxCloud ?? (await askAboutNxCloud()); + useNxCloud = + options.nxCloud ?? (await connectExistingRepoToNxCloudPrompt()); } else { cacheableOperations = options.cacheable ?? []; useNxCloud = options.nxCloud ?? - (options.interactive ? await askAboutNxCloud() : false); + (options.interactive + ? await connectExistingRepoToNxCloudPrompt() + : false); } createNxJsonFile(repoRoot, [], cacheableOperations, {}); diff --git a/packages/nx/src/command-line/init/implementation/angular/index.ts b/packages/nx/src/command-line/init/implementation/angular/index.ts index 9be8d178c706d..59d73712f04f9 100644 --- a/packages/nx/src/command-line/init/implementation/angular/index.ts +++ b/packages/nx/src/command-line/init/implementation/angular/index.ts @@ -7,7 +7,6 @@ import { output } from '../../../../utils/output'; import type { PackageJson } from '../../../../utils/package-json'; import { addDepsToPackageJson, - askAboutNxCloud, initCloud, printFinalMessage, runInstall, @@ -17,6 +16,7 @@ import { setupIntegratedWorkspace } from './integrated-workspace'; import { getLegacyMigrationFunctionIfApplicable } from './legacy-angular-versions'; import { setupStandaloneWorkspace } from './standalone-workspace'; import type { AngularJsonConfig, Options } from './types'; +import { connectExistingRepoToNxCloudPrompt } from '../../../connect/connect-to-nx-cloud'; const defaultCacheableOperations: string[] = [ 'build', @@ -52,7 +52,8 @@ export async function addNxToAngularCliRepo(options: Options) { ? await collectCacheableOperations(options) : []; const useNxCloud = - options.nxCloud ?? (options.interactive ? await askAboutNxCloud() : false); + options.nxCloud ?? + (options.interactive ? await connectExistingRepoToNxCloudPrompt() : false); output.log({ title: '📦 Installing dependencies' }); installDependencies(); diff --git a/packages/nx/src/command-line/init/implementation/angular/legacy-angular-versions.ts b/packages/nx/src/command-line/init/implementation/angular/legacy-angular-versions.ts index 4d40d96478aac..d8e5678ee75c8 100644 --- a/packages/nx/src/command-line/init/implementation/angular/legacy-angular-versions.ts +++ b/packages/nx/src/command-line/init/implementation/angular/legacy-angular-versions.ts @@ -11,8 +11,9 @@ import { resolvePackageVersionUsingInstallation, resolvePackageVersionUsingRegistry, } from '../../../../utils/package-manager'; -import { askAboutNxCloud, initCloud, printFinalMessage } from '../utils'; +import { initCloud, printFinalMessage } from '../utils'; import type { Options } from './types'; +import { connectExistingRepoToNxCloudPrompt } from '../../../connect/connect-to-nx-cloud'; // map of Angular major versions to Nx versions to use for legacy `nx init` migrations, // key is major Angular version and value is Nx version to use @@ -86,7 +87,9 @@ export async function getLegacyMigrationFunctionIfApplicable( output.log({ title: '🐳 Nx initialization' }); const useNxCloud = options.nxCloud ?? - (options.interactive ? await askAboutNxCloud() : false); + (options.interactive + ? await connectExistingRepoToNxCloudPrompt() + : false); output.log({ title: '📦 Installing dependencies' }); const pmc = getPackageManagerCommand(); @@ -98,7 +101,6 @@ export async function getLegacyMigrationFunctionIfApplicable( pkgVersion, unscopedPkgName, }, - useNxCloud, pmc ); @@ -127,7 +129,6 @@ async function installDependencies( pkgVersion: string; unscopedPkgName: string; }, - useNxCloud: boolean, pmc: PackageManagerCommands ): Promise { const json = readJsonFile(join(repoRoot, 'package.json')); diff --git a/packages/nx/src/command-line/init/implementation/react/index.ts b/packages/nx/src/command-line/init/implementation/react/index.ts index 0fd98a1a552b6..3e7717209ac07 100644 --- a/packages/nx/src/command-line/init/implementation/react/index.ts +++ b/packages/nx/src/command-line/init/implementation/react/index.ts @@ -14,7 +14,7 @@ import { PackageManagerCommands, } from '../../../../utils/package-manager'; import { PackageJson } from '../../../../utils/package-json'; -import { askAboutNxCloud, printFinalMessage } from '../utils'; +import { printFinalMessage } from '../utils'; import { checkForCustomWebpackSetup } from './check-for-custom-webpack-setup'; import { checkForUncommittedChanges } from './check-for-uncommitted-changes'; import { cleanUpFiles } from './clean-up-files'; @@ -24,6 +24,7 @@ import { setupTsConfig } from './tsconfig-setup'; import { writeCracoConfig } from './write-craco-config'; import { writeViteConfig } from './write-vite-config'; import { writeViteIndexHtml } from './write-vite-index-html'; +import { connectExistingRepoToNxCloudPrompt } from '../../../connect/connect-to-nx-cloud'; type Options = InitArgs; @@ -93,7 +94,8 @@ async function normalizeOptions(options: Options): Promise { const isStandalone = !options.integrated; const nxCloud = - options.nxCloud ?? (options.interactive ? await askAboutNxCloud() : false); + options.nxCloud ?? + (options.interactive ? await connectExistingRepoToNxCloudPrompt() : false); return { ...options, @@ -183,7 +185,7 @@ function createTempWorkspace(options: NormalizedOptions) { } --preset=react-monorepo --style=css --bundler=${ options.isVite ? 'vite' : 'webpack' } --packageManager=${options.packageManager} ${ - options.nxCloud ? '--nxCloud' : '--nxCloud=false' + options.nxCloud ? '--nxCloud=yes' : '--nxCloud=skip' } ${options.addE2e ? '--e2eTestRunner=cypress' : '--e2eTestRunner=none'}`, { stdio: [0, 1, 2] } ); diff --git a/packages/nx/src/command-line/init/implementation/utils.ts b/packages/nx/src/command-line/init/implementation/utils.ts index ae399330bf416..b9c6491ec2b4a 100644 --- a/packages/nx/src/command-line/init/implementation/utils.ts +++ b/packages/nx/src/command-line/init/implementation/utils.ts @@ -1,5 +1,4 @@ import { execSync } from 'child_process'; -import * as enquirer from 'enquirer'; import { join } from 'path'; import { NxJsonConfiguration } from '../../../config/nx-json'; @@ -19,29 +18,6 @@ import { joinPathFragments } from '../../../utils/path'; import { nxVersion } from '../../../utils/versions'; import { readFileSync, writeFileSync } from 'fs'; -export async function askAboutNxCloud(): Promise { - return await enquirer - .prompt([ - { - name: 'NxCloud', - message: `Enable remote caching to make your CI faster`, - type: 'autocomplete', - choices: [ - { - name: 'Yes', - hint: 'I want faster builds', - }, - - { - name: 'No', - }, - ], - initial: 'Yes' as any, - }, - ]) - .then((a: { NxCloud: 'Yes' | 'No' }) => a.NxCloud === 'Yes'); -} - export function createNxJsonFile( repoRoot: string, topologicalTargets: string[], diff --git a/packages/nx/src/command-line/init/init-v2.ts b/packages/nx/src/command-line/init/init-v2.ts index 48ac6894d4b65..ef773a98abdfd 100644 --- a/packages/nx/src/command-line/init/init-v2.ts +++ b/packages/nx/src/command-line/init/init-v2.ts @@ -9,7 +9,6 @@ import { readJsonFile } from '../../utils/fileutils'; import { nxVersion } from '../../utils/versions'; import { addDepsToPackageJson, - askAboutNxCloud, createNxJsonFile, runInstall, updateGitIgnore, @@ -18,6 +17,7 @@ import { prompt } from 'enquirer'; import { execSync } from 'child_process'; import { addNxToAngularCliRepo } from './implementation/angular'; import { globWithWorkspaceContext } from '../../utils/workspace-context'; +import { connectExistingRepoToNxCloudPrompt } from '../connect/connect-to-nx-cloud'; export interface InitArgs { interactive: boolean; @@ -68,7 +68,8 @@ export async function initHandler(options: InitArgs): Promise { const detectPluginsResponse = await detectPlugins(); const useNxCloud = - options.nxCloud ?? (options.interactive ? await askAboutNxCloud() : false); + options.nxCloud ?? + (options.interactive ? await connectExistingRepoToNxCloudPrompt() : false); if (detectPluginsResponse) { addDepsToPackageJson(repoRoot, detectPluginsResponse.plugins); diff --git a/packages/nx/src/command-line/migrate/migrate.ts b/packages/nx/src/command-line/migrate/migrate.ts index 0b0bb59880887..b8e6473889483 100644 --- a/packages/nx/src/command-line/migrate/migrate.ts +++ b/packages/nx/src/command-line/migrate/migrate.ts @@ -48,13 +48,10 @@ import { } from '../../utils/package-manager'; import { handleErrors } from '../../utils/params'; import { - connectToNxCloudCommand, - connectToNxCloudPrompt, + connectToNxCloudWithPrompt, onlyDefaultRunnerIsUsed, } from '../connect/connect-to-nx-cloud'; import { output } from '../../utils/output'; -import { messages, recordStat } from '../../utils/ab-testing'; -import { nxVersion } from '../../utils/versions'; import { existsSync, readFileSync } from 'fs'; import { workspaceRoot } from '../../utils/workspace-root'; import { isCI } from '../../utils/is-ci'; @@ -1223,16 +1220,7 @@ async function generateMigrationsJsonAndUpdatePackageJson( !isCI() && !isNxCloudUsed(originalNxJson) ) { - const setNxCloud = await connectToNxCloudPrompt( - messages.getPromptMessage('nxCloudMigration') - ); - const useCloud = setNxCloud ? await connectToNxCloudCommand() : false; - await recordStat({ - command: 'migrate', - nxVersion, - useCloud, - meta: messages.codeOfSelectedPromptMessage('nxCloudMigration'), - }); + await connectToNxCloudWithPrompt('migrate'); originalPackageJson = readJsonFile( join(root, 'package.json') ); diff --git a/packages/nx/src/utils/ab-testing.ts b/packages/nx/src/utils/ab-testing.ts index fa7f98f74a9ee..d7a6f43b71547 100644 --- a/packages/nx/src/utils/ab-testing.ts +++ b/packages/nx/src/utils/ab-testing.ts @@ -1,39 +1,64 @@ import { isCI } from './is-ci'; -export class PromptMessages { - private messages = { - nxCloudCreation: [ - { - code: 'set-up-distributed-caching-ci', - message: `Enable remote caching to make your CI faster`, - }, - ], - nxCloudMigration: [ - { - code: 'make-ci-faster', - message: `Enable remote caching to make your CI faster?`, - }, - ], - }; +export type MessageOptionKey = 'yes' | 'skip'; + +const messageOptions = { + setupNxCloud: [ + { + code: 'enable-caching', + message: `Would you like remote caching to make your build faster?`, + initial: 'yes', + choices: [ + { value: 'yes', name: 'Yes' }, + { value: 'skip', name: 'Skip for now' }, + ], + footer: + '\nRead more about remote cache at https://nx.dev/ci/features/remote-cache', + hint: `\n(it's free and can be disabled any time)`, + }, + ], + setupViewLogs: [ + { + code: 'connect-to-view-logs', + message: `To view the logs, Nx needs to connect your workspace to Nx Cloud and upload the most recent run details`, + initial: 'yes', + choices: [ + { + value: 'yes', + name: 'Yes', + hint: 'Connect to Nx Cloud and upload the run details', + }, + { value: 'skip', name: 'No' }, + ], + footer: + '\nRead more about remote cache at https://nx.dev/ci/features/remote-cache', + hint: `\n(it's free and can be disabled any time)`, + }, + ], +} as const; +export type MessageKey = keyof typeof messageOptions; +export type MessageData = typeof messageOptions[MessageKey][number]; + +export class PromptMessages { private selectedMessages = {}; - getPromptMessage(key: string): string { + getPrompt(key: MessageKey): MessageData { if (this.selectedMessages[key] === undefined) { if (process.env.NX_GENERATE_DOCS_PROCESS === 'true') { this.selectedMessages[key] = 0; } else { this.selectedMessages[key] = Math.floor( - Math.random() * this.messages[key].length + Math.random() * messageOptions[key].length ); } } - return this.messages[key][this.selectedMessages[key]].message; + return messageOptions[key][this.selectedMessages[key]]; } codeOfSelectedPromptMessage(key: string): string { if (this.selectedMessages[key] === undefined) return null; - return this.messages[key][this.selectedMessages[key]].code; + return messageOptions[key][this.selectedMessages[key]].code; } } diff --git a/packages/plugin/src/generators/create-package/files/create-framework-package/bin/index.ts__tmpl__ b/packages/plugin/src/generators/create-package/files/create-framework-package/bin/index.ts__tmpl__ index 6df9c79f95352..79d0b8fbf4da9 100644 --- a/packages/plugin/src/generators/create-package/files/create-framework-package/bin/index.ts__tmpl__ +++ b/packages/plugin/src/generators/create-package/files/create-framework-package/bin/index.ts__tmpl__ @@ -17,7 +17,7 @@ async function main() { // TODO: update below to customize the workspace const { directory } = await createWorkspace(`<%= preset %>@${presetVersion}`, { name, - nxCloud: false, + nxCloud: 'skip', packageManager: 'npm', }); diff --git a/packages/plugin/src/generators/e2e-project/files/src/__pluginName__.spec.ts__tmpl__ b/packages/plugin/src/generators/e2e-project/files/src/__pluginName__.spec.ts__tmpl__ index 49df455b9e1fe..bf71aadb0f6d2 100644 --- a/packages/plugin/src/generators/e2e-project/files/src/__pluginName__.spec.ts__tmpl__ +++ b/packages/plugin/src/generators/e2e-project/files/src/__pluginName__.spec.ts__tmpl__ @@ -53,7 +53,7 @@ function createTestProject() { }); execSync( - `<%= packageManagerCommands.exec %> --yes create-nx-workspace@latest ${projectName} --preset apps --no-nxCloud --no-interactive`, + `<%= packageManagerCommands.exec %> --yes create-nx-workspace@latest ${projectName} --preset apps --nxCloud=skip --no-interactive`, { cwd: dirname(projectDirectory), stdio: 'inherit',