From 2ec25640b3febdb9cf0061a79dbd0075b496985b Mon Sep 17 00:00:00 2001 From: Jason Jean Date: Fri, 2 Aug 2024 15:47:10 -0500 Subject: [PATCH] fix(core): move token generation into new (#27266) ## Current Behavior The Nx Cloud token is generated after the workspace and preset have been generated. This makes it hard to use the Nx Cloud token for generating the workspace and preset. ## Expected Behavior The Nx Cloud token is generated in the workspace creation. This makes it easier to use the Nx Cloud token during the workspace creation process. ## Related Issue(s) Fixes # --- docs/generated/devkit/NxJsonConfiguration.md | 9 +++ docs/generated/devkit/Workspace.md | 13 +++++ .../src/create-workspace.ts | 4 +- .../src/utils/nx/nx-cloud.ts | 58 +++++-------------- packages/nx/src/adapter/compat.ts | 1 + packages/nx/src/config/nx-json.ts | 5 ++ .../connect-to-nx-cloud.ts | 43 ++++++++------ .../nx-cloud/utilities/get-cloud-options.ts | 7 ++- .../new/generate-workspace-files.ts | 22 ++++++- packages/workspace/src/generators/new/new.ts | 5 ++ 10 files changed, 97 insertions(+), 70 deletions(-) diff --git a/docs/generated/devkit/NxJsonConfiguration.md b/docs/generated/devkit/NxJsonConfiguration.md index 9db108e673..12e04783ca 100644 --- a/docs/generated/devkit/NxJsonConfiguration.md +++ b/docs/generated/devkit/NxJsonConfiguration.md @@ -28,6 +28,7 @@ Nx.json configuration - [implicitDependencies](../../devkit/documents/NxJsonConfiguration#implicitdependencies): ImplicitDependencyEntry - [installation](../../devkit/documents/NxJsonConfiguration#installation): NxInstallationConfiguration - [namedInputs](../../devkit/documents/NxJsonConfiguration#namedinputs): Object +- [neverConnectToCloud](../../devkit/documents/NxJsonConfiguration#neverconnecttocloud): boolean - [nxCloudAccessToken](../../devkit/documents/NxJsonConfiguration#nxcloudaccesstoken): string - [nxCloudEncryptionKey](../../devkit/documents/NxJsonConfiguration#nxcloudencryptionkey): string - [nxCloudUrl](../../devkit/documents/NxJsonConfiguration#nxcloudurl): string @@ -163,6 +164,14 @@ Named inputs targets can refer to reduce duplication --- +### neverConnectToCloud + +• `Optional` **neverConnectToCloud**: `boolean` + +Set this to false to disable connection to Nx Cloud + +--- + ### nxCloudAccessToken • `Optional` **nxCloudAccessToken**: `string` diff --git a/docs/generated/devkit/Workspace.md b/docs/generated/devkit/Workspace.md index 05c15bc3a9..a7dd997b95 100644 --- a/docs/generated/devkit/Workspace.md +++ b/docs/generated/devkit/Workspace.md @@ -26,6 +26,7 @@ use ProjectsConfigurations or NxJsonConfiguration - [implicitDependencies](../../devkit/documents/Workspace#implicitdependencies): ImplicitDependencyEntry - [installation](../../devkit/documents/Workspace#installation): NxInstallationConfiguration - [namedInputs](../../devkit/documents/Workspace#namedinputs): Object +- [neverConnectToCloud](../../devkit/documents/Workspace#neverconnecttocloud): boolean - [nxCloudAccessToken](../../devkit/documents/Workspace#nxcloudaccesstoken): string - [nxCloudEncryptionKey](../../devkit/documents/Workspace#nxcloudencryptionkey): string - [nxCloudUrl](../../devkit/documents/Workspace#nxcloudurl): string @@ -203,6 +204,18 @@ Named inputs targets can refer to reduce duplication --- +### neverConnectToCloud + +• `Optional` **neverConnectToCloud**: `boolean` + +Set this to false to disable connection to Nx Cloud + +#### Inherited from + +[NxJsonConfiguration](../../devkit/documents/NxJsonConfiguration).[neverConnectToCloud](../../devkit/documents/NxJsonConfiguration#neverconnecttocloud) + +--- + ### nxCloudAccessToken • `Optional` **nxCloudAccessToken**: `string` diff --git a/packages/create-nx-workspace/src/create-workspace.ts b/packages/create-nx-workspace/src/create-workspace.ts index c1bc203dad..3d6cbf1d48 100644 --- a/packages/create-nx-workspace/src/create-workspace.ts +++ b/packages/create-nx-workspace/src/create-workspace.ts @@ -1,6 +1,6 @@ import { CreateWorkspaceOptions } from './create-workspace-options'; import { output } from './utils/output'; -import { getOnboardingInfo, setupNxCloud } from './utils/nx/nx-cloud'; +import { getOnboardingInfo, readNxCloudToken } from './utils/nx/nx-cloud'; import { createSandbox } from './create-sandbox'; import { createEmptyWorkspace } from './create-empty-workspace'; import { createPreset } from './create-preset'; @@ -54,7 +54,7 @@ export async function createWorkspace( let connectUrl: string | undefined; let nxCloudInfo: string | undefined; if (nxCloud !== 'skip') { - const token = await setupNxCloud(directory, nxCloud, useGitHub); + const token = readNxCloudToken(directory) as string; if (nxCloud !== 'yes') { await setupCI(directory, nxCloud, packageManager); 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 0f6703c612..9018a35399 100644 --- a/packages/create-nx-workspace/src/utils/nx/nx-cloud.ts +++ b/packages/create-nx-workspace/src/utils/nx/nx-cloud.ts @@ -1,53 +1,23 @@ -import * as ora from 'ora'; -import { CLIOutput, output } from '../output'; -import { mapErrorToBodyLines } from '../error-utils'; +import { CLIOutput } from '../output'; import { getMessageFactory } from './messages'; +import * as ora from 'ora'; export type NxCloud = 'yes' | 'github' | 'circleci' | 'skip'; -export async function setupNxCloud( - directory: string, - nxCloud: NxCloud, - useGitHub?: boolean -) { - const nxCloudSpinner = ora(`Setting up Nx Cloud`).start(); - try { - // nx-ignore-next-line - const { connectWorkspaceToCloud } = require(require.resolve( - 'nx/src/command-line/connect/connect-to-nx-cloud', - { - paths: [directory], - } - // nx-ignore-next-line - )) as typeof import('nx/src/command-line/connect/connect-to-nx-cloud'); - - const accessToken = await connectWorkspaceToCloud( - { - installationSource: 'create-nx-workspace', - directory, - github: useGitHub, - }, - directory - ); - - nxCloudSpinner.succeed('Nx Cloud has been set up successfully'); - return accessToken; - } catch (e) { - nxCloudSpinner.fail(); - - if (e instanceof Error) { - output.error({ - title: `Failed to setup Nx Cloud`, - bodyLines: mapErrorToBodyLines(e), - }); - } else { - console.error(e); +export function readNxCloudToken(directory: string) { + const nxCloudSpinner = ora(`Checking Nx Cloud setup`).start(); + // nx-ignore-next-line + const { getCloudOptions } = require(require.resolve( + 'nx/src/nx-cloud/utilities/get-cloud-options', + { + paths: [directory], } + // nx-ignore-next-line + )) as typeof import('nx/src/nx-cloud/utilities/get-cloud-options'); - process.exit(1); - } finally { - nxCloudSpinner.stop(); - } + const { accessToken } = getCloudOptions(directory); + nxCloudSpinner.succeed('Nx Cloud has been set up successfully'); + return accessToken; } export async function getOnboardingInfo( diff --git a/packages/nx/src/adapter/compat.ts b/packages/nx/src/adapter/compat.ts index d13d3c02fd..279fc6e47a 100644 --- a/packages/nx/src/adapter/compat.ts +++ b/packages/nx/src/adapter/compat.ts @@ -77,6 +77,7 @@ export const allowedWorkspaceExtensions = [ 'cacheDirectory', 'useDaemonProcess', 'useInferencePlugins', + 'neverConnectToCloud', ] as const; if (!patched) { diff --git a/packages/nx/src/config/nx-json.ts b/packages/nx/src/config/nx-json.ts index e6980f9395..82f8b3ff2f 100644 --- a/packages/nx/src/config/nx-json.ts +++ b/packages/nx/src/config/nx-json.ts @@ -445,6 +445,11 @@ export interface NxJsonConfiguration { * Set this to false to disable adding inference plugins when generating new projects */ useInferencePlugins?: boolean; + + /** + * Set this to false to disable connection to Nx Cloud + */ + neverConnectToCloud?: boolean; } export type PluginConfiguration = string | ExpandedPluginConfiguration; diff --git a/packages/nx/src/nx-cloud/generators/connect-to-nx-cloud/connect-to-nx-cloud.ts b/packages/nx/src/nx-cloud/generators/connect-to-nx-cloud/connect-to-nx-cloud.ts index 92e79918f2..a7c9fd1876 100644 --- a/packages/nx/src/nx-cloud/generators/connect-to-nx-cloud/connect-to-nx-cloud.ts +++ b/packages/nx/src/nx-cloud/generators/connect-to-nx-cloud/connect-to-nx-cloud.ts @@ -1,15 +1,16 @@ import { execSync } from 'child_process'; import { output } from '../../../utils/output'; import { Tree } from '../../../generators/tree'; -import { readJson } from '../../../generators/utils/json'; +import { readJson, updateJson } from '../../../generators/utils/json'; import { NxJsonConfiguration } from '../../../config/nx-json'; -import { readNxJson, updateNxJson } from '../../../generators/utils/nx-json'; +import { readNxJson } from '../../../generators/utils/nx-json'; import { formatChangedFilesWithPrettierIfAvailable } from '../../../generators/internal-utils/format-changed-files-with-prettier-if-available'; import { repoUsesGithub, createNxCloudOnboardingURL, } from '../../utilities/url-shorten'; import { getCloudUrl } from '../../utilities/get-cloud-options'; +import { join } from 'path'; function printCloudConnectionDisabledMessage() { output.error({ @@ -103,30 +104,34 @@ export interface ConnectToNxCloudOptions { function addNxCloudOptionsToNxJson( tree: Tree, - nxJson: NxJsonConfiguration, - token: string + token: string, + directory: string = tree.root ) { - nxJson ??= { - extends: 'nx/presets/npm.json', - }; - nxJson.nxCloudAccessToken = token; - const overrideUrl = process.env.NX_CLOUD_API || process.env.NRWL_API; - if (overrideUrl) { - (nxJson as any).nxCloudUrl = overrideUrl; + const nxJsonPath = join(directory, 'nx.json'); + if (tree.exists(nxJsonPath)) { + updateJson( + tree, + join(directory, 'nx.json'), + (nxJson) => { + const overrideUrl = process.env.NX_CLOUD_API || process.env.NRWL_API; + if (overrideUrl) { + nxJson.nxCloudUrl = overrideUrl; + } + nxJson.nxCloudAccessToken = token; + + return nxJson; + } + ); } - updateNxJson(tree, nxJson); } export async function connectToNxCloud( tree: Tree, - schema: ConnectToNxCloudOptions + schema: ConnectToNxCloudOptions, + nxJson = readNxJson(tree) ): Promise { schema.installationSource ??= 'user'; - const nxJson = readNxJson(tree) as - | null - | (NxJsonConfiguration & { neverConnectToCloud: boolean }); - if (nxJson?.neverConnectToCloud) { printCloudConnectionDisabledMessage(); return null; @@ -150,8 +155,8 @@ export async function connectToNxCloud( addNxCloudOptionsToNxJson( tree, - nxJson, - responseFromCreateNxCloudWorkspace?.token + responseFromCreateNxCloudWorkspace?.token, + schema.directory ); await formatChangedFilesWithPrettierIfAvailable(tree, { diff --git a/packages/nx/src/nx-cloud/utilities/get-cloud-options.ts b/packages/nx/src/nx-cloud/utilities/get-cloud-options.ts index 6bca82e6af..3847c2a324 100644 --- a/packages/nx/src/nx-cloud/utilities/get-cloud-options.ts +++ b/packages/nx/src/nx-cloud/utilities/get-cloud-options.ts @@ -1,9 +1,12 @@ import { CloudTaskRunnerOptions } from '../nx-cloud-tasks-runner-shell'; import { readNxJson } from '../../config/nx-json'; import { getRunnerOptions } from '../../tasks-runner/run-command'; +import { workspaceRoot } from '../../utils/workspace-root'; -export function getCloudOptions(): CloudTaskRunnerOptions { - const nxJson = readNxJson(); +export function getCloudOptions( + directory = workspaceRoot +): CloudTaskRunnerOptions { + const nxJson = readNxJson(directory); // TODO: The default is not always cloud? But it's not handled at the moment return getRunnerOptions('default', nxJson, {}, true); diff --git a/packages/workspace/src/generators/new/generate-workspace-files.ts b/packages/workspace/src/generators/new/generate-workspace-files.ts index 566fa4a7d4..e3ef6de778 100644 --- a/packages/workspace/src/generators/new/generate-workspace-files.ts +++ b/packages/workspace/src/generators/new/generate-workspace-files.ts @@ -1,5 +1,4 @@ import { - formatFiles, generateFiles, getPackageManagerVersion, names, @@ -14,6 +13,7 @@ import { join } from 'path'; import { Preset } from '../utils/presets'; import { deduceDefaultBase } from '../../utilities/default-base'; import { NormalizedSchema } from './new'; +import { connectToNxCloud } from 'nx/src/nx-cloud/generators/connect-to-nx-cloud/connect-to-nx-cloud'; export async function generateWorkspaceFiles( tree: Tree, @@ -31,7 +31,20 @@ export async function generateWorkspaceFiles( options = normalizeOptions(options); createReadme(tree, options); createFiles(tree, options); - createNxJson(tree, options); + const nxJson = createNxJson(tree, options); + + const token = + options.nxCloud !== 'skip' + ? await connectToNxCloud( + tree, + { + installationSource: 'create-nx-workspace', + directory: options.directory, + github: options.useGitHub, + }, + nxJson + ) + : null; const [packageMajor] = packageManagerVersion.split('.'); if (options.packageManager === 'pnpm' && +packageMajor >= 7) { @@ -47,7 +60,7 @@ export async function generateWorkspaceFiles( addNpmScripts(tree, options); setUpWorkspacesInPackageJson(tree, options); - await formatFiles(tree); + return token; } function setPresetProperty(tree: Tree, options: NormalizedSchema) { @@ -96,6 +109,8 @@ function createNxJson( } writeJson(tree, join(directory, 'nx.json'), nxJson); + + return nxJson; } function createFiles(tree: Tree, options: NormalizedSchema) { @@ -217,6 +232,7 @@ function normalizeOptions(options: NormalizedSchema) { name, ...options, defaultBase, + nxCloud: options.nxCloud ?? 'skip', }; } diff --git a/packages/workspace/src/generators/new/new.ts b/packages/workspace/src/generators/new/new.ts index 9200d4a965..51b722625a 100644 --- a/packages/workspace/src/generators/new/new.ts +++ b/packages/workspace/src/generators/new/new.ts @@ -1,4 +1,5 @@ import { + formatFiles, getPackageManagerCommand, installPackagesTask, joinPathFragments, @@ -34,6 +35,8 @@ interface Schema { e2eTestRunner?: 'cypress' | 'playwright' | 'detox' | 'jest' | 'none'; ssr?: boolean; prefix?: string; + useGitHub?: boolean; + nxCloud?: 'yes' | 'skip' | 'circleci' | 'github'; } export interface NormalizedSchema extends Schema { @@ -49,6 +52,8 @@ export async function newGenerator(tree: Tree, opts: Schema) { addPresetDependencies(tree, options); + await formatFiles(tree); + return async () => { if (!options.skipInstall) { const pmc = getPackageManagerCommand(options.packageManager);