From 99a488ae75ccac77956e7ec1f3fd1e8f5e2f6317 Mon Sep 17 00:00:00 2001 From: Raz Luvaton <16746759+rluvaton@users.noreply.github.com> Date: Wed, 24 Jul 2024 19:23:18 +0300 Subject: [PATCH 01/13] fix(core): use argument length that match the actual size of the argument length (#21074) --- packages/nx/src/utils/chunkify.ts | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/packages/nx/src/utils/chunkify.ts b/packages/nx/src/utils/chunkify.ts index fdb4cdda8b6e1..1becb0799e5bf 100644 --- a/packages/nx/src/utils/chunkify.ts +++ b/packages/nx/src/utils/chunkify.ts @@ -1,7 +1,4 @@ -import { execSync } from 'child_process'; - -const TERMINAL_SIZE = - process.platform === 'win32' ? 8192 : getUnixTerminalSize(); +const TERMINAL_SIZE = getMaxArgLength(); export function chunkify( target: string[], @@ -28,14 +25,22 @@ export function chunkify( return chunks; } -function getUnixTerminalSize() { - try { - const argMax = execSync('getconf ARG_MAX').toString().trim(); - return Number.parseInt(argMax); - } catch { - // This number varies by system, but 100k seems like a safe - // number from some research... - // https://stackoverflow.com/questions/19354870/bash-command-line-and-input-limit - return 100000; +/** + * Get the maximum length of a command-line argument string based on current platform + * + * https://serverfault.com/questions/69430/what-is-the-maximum-length-of-a-command-line-in-mac-os-x + * https://support.microsoft.com/en-us/help/830473/command-prompt-cmd-exe-command-line-string-limitation + * https://unix.stackexchange.com/a/120652 + * + * Taken from: https://github.com/lint-staged/lint-staged/blob/adf50b00669f6aac2eeca25dd28ff86a9a3c2a48/lib/index.js#L21-L37 + */ +export function getMaxArgLength() { + switch (process.platform) { + case 'darwin': + return 262144; + case 'win32': + return 8191; + default: + return 131072; } } From 467f343e3d6f292363341450c240c623e47199b9 Mon Sep 17 00:00:00 2001 From: Katerina Skroumpelou Date: Wed, 24 Jul 2024 22:05:34 +0300 Subject: [PATCH 02/13] feat(misc): only create one commit with cloud onboard URL on cnw (#27093) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Only create one commit during CNW * Commit includes cloud onboarding URL ![Screenshot 2024-07-24 at 7 15 54 PM](https://github.com/user-attachments/assets/39b50443-4fbd-4423-b25c-68c45099af66) --- .../src/create-workspace.spec.ts | 59 +++++++++++++++++++ .../src/create-workspace.ts | 47 ++++++++------- .../create-nx-workspace/src/utils/git/git.ts | 28 ++++----- .../connect-to-nx-cloud.ts | 16 +---- 4 files changed, 98 insertions(+), 52 deletions(-) create mode 100644 packages/create-nx-workspace/src/create-workspace.spec.ts diff --git a/packages/create-nx-workspace/src/create-workspace.spec.ts b/packages/create-nx-workspace/src/create-workspace.spec.ts new file mode 100644 index 0000000000000..97461c77e7bb3 --- /dev/null +++ b/packages/create-nx-workspace/src/create-workspace.spec.ts @@ -0,0 +1,59 @@ +import { extractConnectUrl } from './create-workspace'; + +describe('extractConnectUrl', () => { + test('should extract the correct URL from the given string', () => { + const inputString = ` + NX Your Nx Cloud workspace is ready. + + To claim it, connect it to your Nx Cloud account: + - Push your repository to your git hosting provider. + - Go to the following URL to connect your workspace to Nx Cloud: + + https://staging.nx.app/connect/O8dfB0jYgvd + `; + const expectedUrl = 'https://staging.nx.app/connect/O8dfB0jYgvd'; + expect(extractConnectUrl(inputString)).toBe(expectedUrl); + }); + + test('should return null if no URL is present', () => { + const inputString = ` + NX Your Nx Cloud workspace is ready. + + To claim it, connect it to your Nx Cloud account: + - Push your repository to your git hosting provider. + - Go to the following URL to connect your workspace to Nx Cloud: + + No URL here. + `; + expect(extractConnectUrl(inputString)).toBeNull(); + }); + + test('should handle URLs with different domains and paths', () => { + const inputString = ` + NX Your Nx Cloud workspace is ready. + + To claim it, connect it to your Nx Cloud account: + - Push your repository to your git hosting provider. + - Go to the following URL to connect your workspace to Nx Cloud: + + https://example.com/connect/abcd1234 + `; + const expectedUrl = 'https://example.com/connect/abcd1234'; + expect(extractConnectUrl(inputString)).toBe(expectedUrl); + }); + + test('should handle URLs with query parameters and fragments', () => { + const inputString = ` + NX Your Nx Cloud workspace is ready. + + To claim it, connect it to your Nx Cloud account: + - Push your repository to your git hosting provider. + - Go to the following URL to connect your workspace to Nx Cloud: + + https://example.com/connect/abcd1234?query=param#fragment + `; + const expectedUrl = + 'https://example.com/connect/abcd1234?query=param#fragment'; + expect(extractConnectUrl(inputString)).toBe(expectedUrl); + }); +}); diff --git a/packages/create-nx-workspace/src/create-workspace.ts b/packages/create-nx-workspace/src/create-workspace.ts index 92dbd58108565..d776aeb189df7 100644 --- a/packages/create-nx-workspace/src/create-workspace.ts +++ b/packages/create-nx-workspace/src/create-workspace.ts @@ -5,7 +5,7 @@ import { createSandbox } from './create-sandbox'; import { createEmptyWorkspace } from './create-empty-workspace'; import { createPreset } from './create-preset'; import { setupCI } from './utils/ci/setup-ci'; -import { commitChanges, initializeGitRepo } from './utils/git/git'; +import { initializeGitRepo } from './utils/git/git'; import { getPackageNameFromThirdPartyPreset } from './utils/preset/get-third-party-preset'; import { mapErrorToBodyLines } from './utils/error-utils'; @@ -51,23 +51,6 @@ export async function createWorkspace( ); } - let gitSuccess = false; - if (!skipGit && commit) { - try { - await initializeGitRepo(directory, { defaultBase, commit }); - gitSuccess = true; - } catch (e) { - if (e instanceof Error) { - output.error({ - title: 'Could not initialize git repository', - bodyLines: mapErrorToBodyLines(e), - }); - } else { - console.error(e); - } - } - } - let nxCloudInstallRes; if (nxCloud !== 'skip') { nxCloudInstallRes = await setupNxCloud( @@ -78,14 +61,30 @@ export async function createWorkspace( ); if (nxCloud !== 'yes') { - const nxCIsetupRes = await setupCI( + await setupCI( directory, nxCloud, packageManager, nxCloudInstallRes?.code === 0 ); - if (nxCIsetupRes?.code === 0) { - commitChanges(directory, `feat(nx): Generated CI workflow`); + } + } + + if (!skipGit) { + try { + let connectUrl; + if (nxCloudInstallRes?.code === 0) { + connectUrl = extractConnectUrl(nxCloudInstallRes?.stdout); + } + await initializeGitRepo(directory, { defaultBase, commit, connectUrl }); + } catch (e) { + if (e instanceof Error) { + output.error({ + title: 'Could not initialize git repository', + bodyLines: mapErrorToBodyLines(e), + }); + } else { + console.error(e); } } } @@ -95,3 +94,9 @@ export async function createWorkspace( directory, }; } + +export function extractConnectUrl(text: string): string | null { + const urlPattern = /(https:\/\/[^\s]+\/connect\/[^\s]+)/g; + const match = text.match(urlPattern); + return match ? match[0] : null; +} diff --git a/packages/create-nx-workspace/src/utils/git/git.ts b/packages/create-nx-workspace/src/utils/git/git.ts index 4747f65a1a68b..ad05496ac3eb9 100644 --- a/packages/create-nx-workspace/src/utils/git/git.ts +++ b/packages/create-nx-workspace/src/utils/git/git.ts @@ -16,6 +16,7 @@ export async function initializeGitRepo( options: { defaultBase: string; commit?: { message: string; name: string; email: string }; + connectUrl?: string | null; } ) { const execute = (args: ReadonlyArray, ignoreErrorStream = false) => { @@ -80,23 +81,16 @@ export async function initializeGitRepo( } await execute(['add', '.']); if (options.commit) { - const message = options.commit.message || 'initial commit'; - await execute(['commit', `-m "${message}"`]); - } -} + let message = `${options.commit.message}` || 'initial commit'; + if (options.connectUrl) { + message = `${message} -export function commitChanges(directory: string, message: string) { - try { - execSync('git add -A', { encoding: 'utf8', stdio: 'pipe', cwd: directory }); - execSync('git commit --no-verify -F -', { - encoding: 'utf8', - stdio: 'pipe', - input: message, - cwd: directory, - }); - } catch (e) { - console.error(`There was an error committing your Nx Cloud token.\n - Please commit the changes manually and push to your new repository.\n - \n${e}`); +To connect your workspace to Nx Cloud, push your repository +to your git hosting provider and go to the following URL: + +${options.connectUrl} +`; + } + await execute(['commit', `-m "${message}"`]); } } 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 15be5a0a75896..9ccb66511559a 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 @@ -7,7 +7,6 @@ import { readNxJson, updateNxJson } from '../../../generators/utils/nx-json'; import { formatChangedFilesWithPrettierIfAvailable } from '../../../generators/internal-utils/format-changed-files-with-prettier-if-available'; import { repoUsesGithub, shortenedCloudUrl } from '../../utilities/url-shorten'; import { getCloudUrl } from '../../utilities/get-cloud-options'; -import { commitChanges } from '../../../utils/git-utils'; import * as ora from 'ora'; import * as open from 'open'; @@ -72,8 +71,7 @@ async function createNxCloudWorkspace( async function printSuccessMessage( token: string | undefined, installationSource: string, - usesGithub: boolean, - directory?: string + usesGithub: boolean ) { const connectCloudUrl = await shortenedCloudUrl( installationSource, @@ -112,15 +110,6 @@ async function printSuccessMessage( `${connectCloudUrl}`, ], }); - commitChanges( - `feat(nx): Added Nx Cloud token to your nx.json - - To connect your workspace to Nx Cloud, push your repository - to your git hosting provider and go to the following URL: - - ${connectCloudUrl}`, - directory - ); } else { output.note({ title: `Your Nx Cloud workspace is ready.`, @@ -208,8 +197,7 @@ export async function connectToNxCloud( await printSuccessMessage( responseFromCreateNxCloudWorkspace?.token, schema.installationSource, - usesGithub, - schema.directory + usesGithub ); } } From b7472fdd4108f23dff22ce2bcb732477c17a9e85 Mon Sep 17 00:00:00 2001 From: Craigory Coppola Date: Wed, 24 Jul 2024 15:39:14 -0400 Subject: [PATCH 03/13] fix(core): reset should cleanup temporary nx-cloud files (#23316) ## Current Behavior `nx reset` doesn't remove marker files used by nx cloud. ## Expected Behavior `nx reset` removes marker files from nx cloud ## Related Issue(s) Fixes #https://github.com/nrwl/nx/issues/23308 --- docs/generated/cli/reset.md | 6 ++++ docs/generated/packages/nx/documents/reset.md | 6 ++++ packages/nx/bin/nx-cloud.ts | 32 +++++++---------- .../src/command-line/reset/command-object.ts | 6 ++++ packages/nx/src/command-line/reset/reset.ts | 14 ++++++++ packages/nx/src/nx-cloud/utilities/client.ts | 35 +++++++++++++++++++ 6 files changed, 80 insertions(+), 19 deletions(-) create mode 100644 packages/nx/src/nx-cloud/utilities/client.ts diff --git a/docs/generated/cli/reset.md b/docs/generated/cli/reset.md index ad1bf3e90755e..fef274c023160 100644 --- a/docs/generated/cli/reset.md +++ b/docs/generated/cli/reset.md @@ -55,6 +55,12 @@ Type: `boolean` Clears the Nx cache directory. This will remove all local cache entries for tasks, but will not affect the remote cache. +### onlyCloud + +Type: `boolean` + +Resets the Nx Cloud client. NOTE: Does not clear the remote cache. + ### onlyDaemon Type: `boolean` diff --git a/docs/generated/packages/nx/documents/reset.md b/docs/generated/packages/nx/documents/reset.md index ad1bf3e90755e..fef274c023160 100644 --- a/docs/generated/packages/nx/documents/reset.md +++ b/docs/generated/packages/nx/documents/reset.md @@ -55,6 +55,12 @@ Type: `boolean` Clears the Nx cache directory. This will remove all local cache entries for tasks, but will not affect the remote cache. +### onlyCloud + +Type: `boolean` + +Resets the Nx Cloud client. NOTE: Does not clear the remote cache. + ### onlyDaemon Type: `boolean` diff --git a/packages/nx/bin/nx-cloud.ts b/packages/nx/bin/nx-cloud.ts index 85b6e5154364b..08b92362a28fb 100644 --- a/packages/nx/bin/nx-cloud.ts +++ b/packages/nx/bin/nx-cloud.ts @@ -1,14 +1,16 @@ #!/usr/bin/env node -import { findAncestorNodeModules } from '../src/nx-cloud/resolution-helpers'; import { getCloudOptions } from '../src/nx-cloud/utilities/get-cloud-options'; import { NxCloudClientUnavailableError, NxCloudEnterpriseOutdatedError, - verifyOrUpdateNxCloudClient, } from '../src/nx-cloud/update-manager'; import type { CloudTaskRunnerOptions } from '../src/nx-cloud/nx-cloud-tasks-runner-shell'; import { output } from '../src/utils/output'; +import { + UnknownCommandError, + getCloudClient, +} from '../src/nx-cloud/utilities/client'; const command = process.argv[2]; @@ -16,31 +18,23 @@ const options = getCloudOptions(); Promise.resolve().then(async () => invokeCommandWithNxCloudClient(options)); -async function invokeCommandWithNxCloudClient(options: CloudTaskRunnerOptions) { +export async function invokeCommandWithNxCloudClient( + options: CloudTaskRunnerOptions +) { try { - const { nxCloudClient } = await verifyOrUpdateNxCloudClient(options); - - const paths = findAncestorNodeModules(__dirname, []); - nxCloudClient.configureLightClientRequire()(paths); - - if (command in nxCloudClient.commands) { - nxCloudClient.commands[command]() - .then(() => process.exit(0)) - .catch((e) => { - console.error(e); - process.exit(1); - }); - } else { + const client = await getCloudClient(options); + client.invoke(command); + } catch (e: any) { + if (e instanceof UnknownCommandError) { output.error({ - title: `Unknown Command "${command}"`, + title: `Unknown Command "${e.command}"`, }); output.log({ title: 'Available Commands:', - bodyLines: Object.keys(nxCloudClient.commands).map((c) => `- ${c}`), + bodyLines: e.availableCommands.map((c) => `- ${c}`), }); process.exit(1); } - } catch (e: any) { const body = ['Cannot run commands from the `nx-cloud` CLI.']; if (e instanceof NxCloudEnterpriseOutdatedError) { diff --git a/packages/nx/src/command-line/reset/command-object.ts b/packages/nx/src/command-line/reset/command-object.ts index 17decc00dd4d0..04e2871eb4ec1 100644 --- a/packages/nx/src/command-line/reset/command-object.ts +++ b/packages/nx/src/command-line/reset/command-object.ts @@ -4,6 +4,7 @@ export type ResetCommandOptions = { onlyCache?: boolean; onlyDaemon?: boolean; onlyWorkspaceData?: boolean; + onlyCloud?: boolean; }; export const yargsResetCommand: CommandModule< @@ -26,6 +27,11 @@ export const yargsResetCommand: CommandModule< 'Stops the Nx Daemon, it will be restarted fresh when the next Nx command is run.', type: 'boolean', }) + .option('onlyCloud', { + description: + 'Resets the Nx Cloud client. NOTE: Does not clear the remote cache.', + type: 'boolean', + }) .option('onlyWorkspaceData', { description: 'Clears the workspace data directory. Used by Nx to store cached data about the current workspace (e.g. partial results, incremental data, etc)', diff --git a/packages/nx/src/command-line/reset/reset.ts b/packages/nx/src/command-line/reset/reset.ts index 61dc978ad7651..3d79c31ec07a5 100644 --- a/packages/nx/src/command-line/reset/reset.ts +++ b/packages/nx/src/command-line/reset/reset.ts @@ -5,6 +5,9 @@ import { output } from '../../utils/output'; import { getNativeFileCacheLocation } from '../../native/native-file-cache-location'; import { ResetCommandOptions } from './command-object'; +import { getCloudClient } from '../../nx-cloud/utilities/client'; +import { getCloudOptions } from '../../nx-cloud/utilities/get-cloud-options'; + // Wait at max 5 seconds before giving up on a failing operation. const INCREMENTAL_BACKOFF_MAX_DURATION = 5000; @@ -62,6 +65,9 @@ export async function resetHandler(args: ResetCommandOptions) { errors.push('Failed to clean up the workspace data directory.'); } } + if (all || args.onlyCloud) { + await resetCloudClient(); + } if (errors.length > 0) { output.error({ title: 'Failed to reset the Nx workspace.', @@ -79,6 +85,14 @@ function killDaemon() { return daemonClient.stop(); } +async function resetCloudClient() { + // Remove nx cloud marker files. This helps if the use happens to run `nx-cloud start-ci-run` or + // similar commands on their local machine. + try { + (await getCloudClient(getCloudOptions())).invoke('cleanup'); + } catch {} +} + function cleanupCacheEntries() { return incrementalBackoff( INCREMENTAL_BACKOFF_FIRST_DELAY, diff --git a/packages/nx/src/nx-cloud/utilities/client.ts b/packages/nx/src/nx-cloud/utilities/client.ts new file mode 100644 index 0000000000000..09b9b3ff1164c --- /dev/null +++ b/packages/nx/src/nx-cloud/utilities/client.ts @@ -0,0 +1,35 @@ +import { findAncestorNodeModules } from '../resolution-helpers'; +import { verifyOrUpdateNxCloudClient } from '../update-manager'; +import { CloudTaskRunnerOptions } from '../nx-cloud-tasks-runner-shell'; + +export class UnknownCommandError extends Error { + constructor(public command: string, public availableCommands: string[]) { + super(`Unknown Command "${command}"`); + } +} + +export async function getCloudClient(options: CloudTaskRunnerOptions) { + const { nxCloudClient } = await verifyOrUpdateNxCloudClient(options); + + const paths = findAncestorNodeModules(__dirname, []); + nxCloudClient.configureLightClientRequire()(paths); + + return { + invoke: (command: string) => { + if (command in nxCloudClient.commands) { + nxCloudClient.commands[command]() + .then(() => process.exit(0)) + .catch((e) => { + console.error(e); + process.exit(1); + }); + } else { + throw new UnknownCommandError( + command, + Object.keys(nxCloudClient.commands) + ); + } + }, + availableCommands: Object.keys(nxCloudClient.commands), + }; +} From def20f29ca1da0fa327a845bc7e6c07bd58ccdcb Mon Sep 17 00:00:00 2001 From: Craigory Coppola Date: Wed, 24 Jul 2024 15:42:24 -0400 Subject: [PATCH 04/13] fix(core): shorten socket length for plugin workers (#27073) ## Current Behavior Plugin worker's socket path includes a hash digest of the workspace root. This is not needed, as the socket path already includes the process pid in it which is unique. The hash digest adds an additional 20 characters to the path and as the path is too long on some unix systems the uniqueness is dropped, causing the plugin transactions to get hit inappropriately. ## Expected Behavior The plugin socket path doesn't contain extra characters, so it should be unique. ## Related Issue(s) Possibly related #27040 --- packages/nx/src/daemon/socket-utils.ts | 2 +- packages/nx/src/daemon/tmp-dir.ts | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/nx/src/daemon/socket-utils.ts b/packages/nx/src/daemon/socket-utils.ts index 3cf26406131a4..c97f92f1e6170 100644 --- a/packages/nx/src/daemon/socket-utils.ts +++ b/packages/nx/src/daemon/socket-utils.ts @@ -23,7 +23,7 @@ export const getForkedProcessOsSocketPath = (id: string) => { }; export const getPluginOsSocketPath = (id: string) => { - let path = resolve(join(getSocketDir(), 'plugin' + id + '.sock')); + let path = resolve(join(getSocketDir(true), 'plugin' + id + '.sock')); return isWindows ? '\\\\.\\pipe\\nx\\' + resolve(path) : resolve(path); }; diff --git a/packages/nx/src/daemon/tmp-dir.ts b/packages/nx/src/daemon/tmp-dir.ts index a865439486193..0d163aef479f7 100644 --- a/packages/nx/src/daemon/tmp-dir.ts +++ b/packages/nx/src/daemon/tmp-dir.ts @@ -58,9 +58,11 @@ function socketDirName() { * We try to create a socket file in a tmp dir, but if it doesn't work because * for instance we don't have permissions, we create it in DAEMON_DIR_FOR_CURRENT_WORKSPACE */ -export function getSocketDir() { +export function getSocketDir(alreadyUnique = false) { try { - const dir = process.env.NX_DAEMON_SOCKET_DIR ?? socketDirName(); + const dir = + process.env.NX_DAEMON_SOCKET_DIR ?? + (alreadyUnique ? tmpdir : socketDirName()); ensureDirSync(dir); return dir; } catch (e) { From bb92857b2122497829229cb2c8132e2a618e2cb5 Mon Sep 17 00:00:00 2001 From: Jason Jean Date: Wed, 24 Jul 2024 14:46:09 -0500 Subject: [PATCH 05/13] chore(repo): make publishing depend on build targets (#27080) ## Current Behavior Populating the local registry for e2e tests is not dependent on the builds but runs them. It sometimes gets cache misses for some builds thus making that inconsistent. https://staging.nx.app/runs/FxIwbj6UQo/task/%40nx%2Fnx-source%3Apopulate-local-registry-storage ## Expected Behavior Populating the local registry for e2e tests is dependent on the builds and does not run them within itself. In the CI pipeline, this ensures that the build tasks are run first. https://staging.nx.app/runs/NUrFeUBbQk/task/%40nx%2Fnx-source%3Apopulate-local-registry-storage ## Related Issue(s) Fixes # --- nx-dev/util-ai/package.json | 1 + nx.json | 10 ++-------- package.json | 4 ++-- project.json | 22 ++++++++++++++++------ scripts/nx-release.ts | 7 ------- typedoc-theme/package.json | 1 + 6 files changed, 22 insertions(+), 23 deletions(-) diff --git a/nx-dev/util-ai/package.json b/nx-dev/util-ai/package.json index dd624378852c9..3ba6b2797af2e 100644 --- a/nx-dev/util-ai/package.json +++ b/nx-dev/util-ai/package.json @@ -1,5 +1,6 @@ { "name": "@nx/nx-dev/util-ai", + "private": true, "version": "0.0.1", "dependencies": { "@supabase/supabase-js": "^2.26.0", diff --git a/nx.json b/nx.json index cf7a475c3f0bc..31fe57c0d0f2c 100644 --- a/nx.json +++ b/nx.json @@ -139,18 +139,12 @@ "e2e-ci--**/*": { "inputs": ["e2eInputs", "^production"], "parallelism": false, - "dependsOn": [ - "nx:build-native", - "@nx/nx-source:populate-local-registry-storage" - ] + "dependsOn": ["@nx/nx-source:populate-local-registry-storage"] }, "e2e-macos-ci--**/*": { "inputs": ["e2eInputs", "^production"], "parallelism": false, - "dependsOn": [ - "nx:build-native", - "@nx/nx-source:populate-local-registry-storage" - ] + "dependsOn": ["@nx/nx-source:populate-local-registry-storage"] }, "e2e-base": { "inputs": ["default", "^production"] diff --git a/package.json b/package.json index 629e14dd301a9..65087545ed872 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "homepage": "https://nx.dev", "private": true, "scripts": { - "build": "nx run-many --target build --parallel 8 --exclude nx-dev,typedoc-theme,tools-documentation-create-embeddings", + "build": "nx run-many --target build --parallel 8 --exclude nx-dev,typedoc-theme,tools-documentation-create-embeddings,nx-dev-util-ai", "commit": "czg", "check-commit": "node ./scripts/commit-lint.js", "check-format": "nx format:check --all", @@ -12,7 +12,7 @@ "check-lock-files": "node ./scripts/check-lock-files.js", "check-documentation-map": "ts-node -P ./scripts/tsconfig.scripts.json ./scripts/documentation/map-link-checker.ts", "check-codeowners": "ts-node -P ./scripts/tsconfig.scripts.json ./scripts/check-codeowners.ts", - "nx-release": "ts-node -P ./scripts/tsconfig.release.json ./scripts/nx-release", + "nx-release": "nx nx-release @nx/nx-source --parallel 8", "prepublishOnly": "node ./scripts/update-package-group.js", "local-registry": "nx local-registry @nx/nx-source", "documentation": "ts-node -P scripts/tsconfig.scripts.json ./scripts/documentation/generators/main.ts && pnpm check-documentation-map", diff --git a/project.json b/project.json index a191d8df2961f..b358d7a670dc4 100644 --- a/project.json +++ b/project.json @@ -15,16 +15,26 @@ "inputs": [ { "input": "production", - "projects": [ - "*", - "!nx-dev", - "!typedoc-theme", - "!tools-documentation-create-embeddings" - ] + "projects": ["tag:npm:public"] + } + ], + "dependsOn": [ + { + "target": "build", + "projects": ["tag:npm:public"] } ], "command": "node ./scripts/local-registry/run-populate-storage.mjs", "outputs": ["{workspaceRoot}/build/local-registry/storage"] + }, + "nx-release": { + "dependsOn": [ + { + "target": "build", + "projects": ["tag:npm:public"] + } + ], + "command": "ts-node -P ./scripts/tsconfig.release.json ./scripts/nx-release" } } } diff --git a/scripts/nx-release.ts b/scripts/nx-release.ts index 0f4a0027ce4bb..71728df240055 100755 --- a/scripts/nx-release.ts +++ b/scripts/nx-release.ts @@ -31,13 +31,6 @@ const VALID_AUTHORS_FOR_LATEST = [ }); } - const buildCommand = 'pnpm build'; - console.log(`> ${buildCommand}`); - execSync(buildCommand, { - stdio: [0, 1, 2], - maxBuffer: LARGE_BUFFER, - }); - // Ensure all the native-packages directories are available at the top level of the build directory, enabling consistent packageRoot structure execSync(`pnpm nx copy-native-package-directories nx`, { stdio: isVerboseLogging ? [0, 1, 2] : 'ignore', diff --git a/typedoc-theme/package.json b/typedoc-theme/package.json index f9e039c51e017..c7fd9ee8dbb78 100644 --- a/typedoc-theme/package.json +++ b/typedoc-theme/package.json @@ -1,5 +1,6 @@ { "name": "@nx/typedoc-theme", + "private": true, "version": "0.0.1", "keywords": [ "typedocplugin" From c7e1a1681e62abb00ea1a4f6aa5d41595efb81b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leosvel=20P=C3=A9rez=20Espinosa?= Date: Wed, 24 Jul 2024 21:48:59 +0200 Subject: [PATCH 06/13] fix(testing): use taskkill to kill web server process when running cypress on windows (#27068) ## Current Behavior ## Expected Behavior ## Related Issue(s) Fixes #26599 --- packages/cypress/plugins/cypress-preset.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/cypress/plugins/cypress-preset.ts b/packages/cypress/plugins/cypress-preset.ts index 31ee3da34f453..95879c1730571 100644 --- a/packages/cypress/plugins/cypress-preset.ts +++ b/packages/cypress/plugins/cypress-preset.ts @@ -1,11 +1,11 @@ import { workspaceRoot } from '@nx/devkit'; import { dirname, join, relative } from 'path'; -import { existsSync, lstatSync } from 'fs'; +import { lstatSync } from 'fs'; import vitePreprocessor from '../src/plugins/preprocessor-vite'; import { NX_PLUGIN_OPTIONS } from '../src/utils/constants'; -import { spawn } from 'child_process'; +import { execSync, spawn } from 'child_process'; import { request as httpRequest } from 'http'; import { request as httpsRequest } from 'https'; import type { InlineConfig } from 'vite'; @@ -82,7 +82,13 @@ function startWebServer(webServerCommand: string) { return () => { if (process.platform === 'win32') { - serverProcess.kill(); + try { + execSync('taskkill /pid ' + serverProcess.pid + ' /T /F'); + } catch (e) { + if (process.env.NX_VERBOSE_LOGGING === 'true') { + console.error(e); + } + } } else { // child.kill() does not work on linux // process.kill will kill the whole process group on unix From e474b597c5d84d2d01f3dbdb781db04b4f63538d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leosvel=20P=C3=A9rez=20Espinosa?= Date: Wed, 24 Jul 2024 22:02:36 +0200 Subject: [PATCH 07/13] fix(core): remove outdated workaround artificially keeping process alive (#27062) Remove an old workaround added in https://github.com/nrwl/nx/pull/5807. The issue it was addressing was fixed in the Angular CLI shortly after, but the workaround was never removed. ## Current Behavior ## Expected Behavior ## Related Issue(s) Fixes # --- packages/nx/src/command-line/run/run.ts | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/packages/nx/src/command-line/run/run.ts b/packages/nx/src/command-line/run/run.ts index 22db2d3fe70bd..f33b863b53135 100644 --- a/packages/nx/src/command-line/run/run.ts +++ b/packages/nx/src/command-line/run/run.ts @@ -70,19 +70,8 @@ async function* promiseToIterator( async function iteratorToProcessStatusCode( i: AsyncIterableIterator<{ success: boolean }> ): Promise { - // This is a workaround to fix an issue that only happens with - // the @angular-devkit/build-angular:browser builder. Starting - // on version 12.0.1, a SASS compilation implementation was - // introduced making use of workers and it's unref()-ing the worker - // too early, causing the process to exit early in environments - // like CI or when running Docker builds. - const keepProcessAliveInterval = setInterval(() => {}, 1000); - try { - const { success } = await getLastValueFromAsyncIterableIterator(i); - return success ? 0 : 1; - } finally { - clearInterval(keepProcessAliveInterval); - } + const { success } = await getLastValueFromAsyncIterableIterator(i); + return success ? 0 : 1; } async function parseExecutorAndTarget( From 8afd3ea15d21a82c43e7f722136d87d437147dcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leosvel=20P=C3=A9rez=20Espinosa?= Date: Wed, 24 Jul 2024 22:03:49 +0200 Subject: [PATCH 08/13] fix(core): update migration generators missing calls to format files (#27082) ## Current Behavior ## Expected Behavior ## Related Issue(s) Fixes #25122 --- .../src/migrations/update-16-0-0/update-nx-cloud-runner.ts | 3 +++ ...ec.ts => disable-crystal-for-existing-workspaces.spec.ts} | 4 ++-- .../update-18-0-0/disable-crystal-for-existing-workspaces.ts | 5 ++++- 3 files changed, 9 insertions(+), 3 deletions(-) rename packages/nx/src/migrations/update-18-0-0/{disable-crysal-for-existing-workspaces.spec.ts => disable-crystal-for-existing-workspaces.spec.ts} (89%) diff --git a/packages/nx/src/migrations/update-16-0-0/update-nx-cloud-runner.ts b/packages/nx/src/migrations/update-16-0-0/update-nx-cloud-runner.ts index e037b65c04d79..23b663ee4086a 100644 --- a/packages/nx/src/migrations/update-16-0-0/update-nx-cloud-runner.ts +++ b/packages/nx/src/migrations/update-16-0-0/update-nx-cloud-runner.ts @@ -4,6 +4,7 @@ import { } from '../../generators/utils/project-configuration'; import { Tree } from '../../generators/tree'; import { updateJson } from '../../generators/utils/json'; +import { formatChangedFilesWithPrettierIfAvailable } from '../../generators/internal-utils/format-changed-files-with-prettier-if-available'; export default async function (tree: Tree) { updateJson(tree, 'package.json', (json) => { @@ -28,4 +29,6 @@ export default async function (tree: Tree) { } } updateNxJson(tree, nxJson); + + await formatChangedFilesWithPrettierIfAvailable(tree); } diff --git a/packages/nx/src/migrations/update-18-0-0/disable-crysal-for-existing-workspaces.spec.ts b/packages/nx/src/migrations/update-18-0-0/disable-crystal-for-existing-workspaces.spec.ts similarity index 89% rename from packages/nx/src/migrations/update-18-0-0/disable-crysal-for-existing-workspaces.spec.ts rename to packages/nx/src/migrations/update-18-0-0/disable-crystal-for-existing-workspaces.spec.ts index 6ae16df790763..5d108ca614077 100644 --- a/packages/nx/src/migrations/update-18-0-0/disable-crysal-for-existing-workspaces.spec.ts +++ b/packages/nx/src/migrations/update-18-0-0/disable-crystal-for-existing-workspaces.spec.ts @@ -2,9 +2,9 @@ import { createTreeWithEmptyWorkspace } from '../../generators/testing-utils/cre import migrate from './disable-crystal-for-existing-workspaces'; describe('disable crystal for existing workspaces', () => { - it('should add flag to nx.json', () => { + it('should add flag to nx.json', async () => { const tree = createTreeWithEmptyWorkspace(); - migrate(tree); + await migrate(tree); expect(tree.read('nx.json', 'utf-8')).toMatchInlineSnapshot(` "{ "affected": { diff --git a/packages/nx/src/migrations/update-18-0-0/disable-crystal-for-existing-workspaces.ts b/packages/nx/src/migrations/update-18-0-0/disable-crystal-for-existing-workspaces.ts index 951ea0ebc0a05..b532024b10056 100644 --- a/packages/nx/src/migrations/update-18-0-0/disable-crystal-for-existing-workspaces.ts +++ b/packages/nx/src/migrations/update-18-0-0/disable-crystal-for-existing-workspaces.ts @@ -1,8 +1,11 @@ import { readNxJson, updateNxJson } from '../../generators/utils/nx-json'; import { Tree } from '../../generators/tree'; +import { formatChangedFilesWithPrettierIfAvailable } from '../../generators/internal-utils/format-changed-files-with-prettier-if-available'; -export default function migrate(tree: Tree) { +export default async function migrate(tree: Tree) { const nxJson = readNxJson(tree); nxJson.useInferencePlugins = false; updateNxJson(tree, nxJson); + + await formatChangedFilesWithPrettierIfAvailable(tree); } From 28cd0c4eef99bc9da11ccd1772bf494c236672f5 Mon Sep 17 00:00:00 2001 From: Emily Xiong Date: Wed, 24 Jul 2024 13:18:13 -0700 Subject: [PATCH 09/13] feat(js): add scopes option for verdaccio (#26918) --- .../packages/js/executors/verdaccio.json | 5 + .../js/src/executors/verdaccio/schema.d.ts | 1 + .../js/src/executors/verdaccio/schema.json | 7 + .../src/executors/verdaccio/verdaccio.impl.ts | 182 +++++++++++------- 4 files changed, 127 insertions(+), 68 deletions(-) diff --git a/docs/generated/packages/js/executors/verdaccio.json b/docs/generated/packages/js/executors/verdaccio.json index ad707dfaf3a68..2591b416bc69f 100644 --- a/docs/generated/packages/js/executors/verdaccio.json +++ b/docs/generated/packages/js/executors/verdaccio.json @@ -31,6 +31,11 @@ "type": "boolean", "description": "Clear local registry storage before starting Verdaccio", "default": true + }, + "scopes": { + "type": "array", + "description": "Scopes to be added to the Verdaccio config", + "items": { "type": "string" } } }, "required": ["port"], diff --git a/packages/js/src/executors/verdaccio/schema.d.ts b/packages/js/src/executors/verdaccio/schema.d.ts index e90513aad1b8e..3af9bfc76a952 100644 --- a/packages/js/src/executors/verdaccio/schema.d.ts +++ b/packages/js/src/executors/verdaccio/schema.d.ts @@ -4,4 +4,5 @@ export interface VerdaccioExecutorSchema { port?: number; config?: string; clear?: boolean; + scopes?: string[]; } diff --git a/packages/js/src/executors/verdaccio/schema.json b/packages/js/src/executors/verdaccio/schema.json index ce099fc017423..bb619f8b0f4e2 100644 --- a/packages/js/src/executors/verdaccio/schema.json +++ b/packages/js/src/executors/verdaccio/schema.json @@ -28,6 +28,13 @@ "type": "boolean", "description": "Clear local registry storage before starting Verdaccio", "default": true + }, + "scopes": { + "type": "array", + "description": "Scopes to be added to the Verdaccio config", + "items": { + "type": "string" + } } }, "required": ["port"] diff --git a/packages/js/src/executors/verdaccio/verdaccio.impl.ts b/packages/js/src/executors/verdaccio/verdaccio.impl.ts index 5496ce747aced..7667c95bc7f10 100644 --- a/packages/js/src/executors/verdaccio/verdaccio.impl.ts +++ b/packages/js/src/executors/verdaccio/verdaccio.impl.ts @@ -138,59 +138,88 @@ function setupNpm(options: VerdaccioExecutorSchema) { return () => {}; } - let npmRegistryPath: string; + let npmRegistryPaths: string[] = []; + const scopes: string[] = ['', ...(options.scopes || [])]; + try { - npmRegistryPath = execSync( - `npm config get registry --location ${options.location}`, - { env } - ) - ?.toString() - ?.trim() - ?.replace('\u001b[2K\u001b[1G', ''); // strip out ansi codes - execSync( - `npm config set registry http://localhost:${options.port}/ --location ${options.location}`, - { env } - ); - execSync( - `npm config set //localhost:${options.port}/:_authToken="secretVerdaccioToken" --location ${options.location}`, - { env } - ); - logger.info(`Set npm registry to http://localhost:${options.port}/`); - } catch (e) { - throw new Error( - `Failed to set npm registry to http://localhost:${options.port}/: ${e.message}` - ); - } + scopes.forEach((scope) => { + const scopeName = scope ? `${scope}:` : ''; + try { + npmRegistryPaths.push( + execSync( + `npm config get '${scopeName}registry' --location ${options.location}`, + { env } + ) + ?.toString() + ?.trim() + ?.replace('\u001b[2K\u001b[1G', '') // strip out ansi codes + ); + execSync( + `npm config set '${scopeName}registry' http://localhost:${options.port}/ --location ${options.location}`, + { env } + ); - return () => { - try { - const currentNpmRegistryPath = execSync( - `npm config get registry --location ${options.location}`, - { env } - ) - ?.toString() - ?.trim() - ?.replace('\u001b[2K\u001b[1G', ''); // strip out ansi codes - if (npmRegistryPath && currentNpmRegistryPath.includes('localhost')) { execSync( - `npm config set registry ${npmRegistryPath} --location ${options.location}`, + `npm config set //localhost:${options.port}/:_authToken="secretVerdaccioToken" --location ${options.location}`, { env } ); - logger.info(`Reset npm registry to ${npmRegistryPath}`); - } else { - execSync(`npm config delete registry --location ${options.location}`, { - env, - }); - logger.info('Cleared custom npm registry'); + + logger.info( + `Set npm ${scopeName}registry to http://localhost:${options.port}/` + ); + } catch (e) { + throw new Error( + `Failed to set npm ${scopeName}registry to http://localhost:${options.port}/: ${e.message}` + ); } - execSync( - `npm config delete //localhost:${options.port}/:_authToken --location ${options.location}`, - { env } - ); - } catch (e) { - throw new Error(`Failed to reset npm registry: ${e.message}`); - } - }; + }); + + return () => { + try { + const currentNpmRegistryPath = execSync( + `npm config get registry --location ${options.location}`, + { env } + ) + ?.toString() + ?.trim() + ?.replace('\u001b[2K\u001b[1G', ''); // strip out ansi codes + if ( + npmRegistryPaths.length > 0 && + currentNpmRegistryPath.includes('localhost') + ) { + scopes.forEach((scope, index) => { + const scopeName = scope ? `${scope}:` : ''; + + execSync( + `npm config set '${scopeName}registry' ${npmRegistryPaths[index]} --location ${options.location}`, + { env } + ); + logger.info( + `Reset npm ${scopeName}registry to ${npmRegistryPaths[index]}` + ); + }); + } else { + execSync( + `npm config delete registry --location ${options.location}`, + { + env, + } + ); + logger.info('Cleared custom npm registry'); + } + execSync( + `npm config delete //localhost:${options.port}/:_authToken --location ${options.location}`, + { env } + ); + } catch (e) { + throw new Error(`Failed to reset npm registry: ${e.message}`); + } + }; + } catch (e) { + throw new Error( + `Failed to set npm registry to http://localhost:${options.port}/: ${e.message}` + ); + } } function getYarnUnsafeHttpWhitelist(isYarnV1: boolean) { @@ -227,6 +256,8 @@ function setYarnUnsafeHttpWhitelist( function setupYarn(options: VerdaccioExecutorSchema) { let isYarnV1; + let yarnRegistryPaths: string[] = []; + const scopes: string[] = ['', ...(options.scopes || [])]; try { isYarnV1 = @@ -238,20 +269,28 @@ function setupYarn(options: VerdaccioExecutorSchema) { try { const registryConfigName = isYarnV1 ? 'registry' : 'npmRegistryServer'; - const yarnRegistryPath = execSync(`yarn config get ${registryConfigName}`, { - env, - }) - ?.toString() - ?.trim() - ?.replace('\u001b[2K\u001b[1G', ''); // strip out ansi codes + scopes.forEach((scope) => { + const scopeName = scope ? `${scope}:` : ''; - execSync( - `yarn config set ${registryConfigName} http://localhost:${options.port}/` + - (options.location === 'user' ? ' --home' : ''), - { env } - ); + yarnRegistryPaths.push( + execSync(`yarn config get ${scopeName}${registryConfigName}`, { + env, + }) + ?.toString() + ?.trim() + ?.replace('\u001b[2K\u001b[1G', '') // strip out ansi codes + ); - logger.info(`Set yarn registry to http://localhost:${options.port}/`); + execSync( + `yarn config set ${scopeName}${registryConfigName} http://localhost:${options.port}/` + + (options.location === 'user' ? ' --home' : ''), + { env } + ); + + logger.info( + `Set yarn ${scopeName}registry to http://localhost:${options.port}/` + ); + }); const currentWhitelist: Set | null = getYarnUnsafeHttpWhitelist(isYarnV1); @@ -277,15 +316,22 @@ function setupYarn(options: VerdaccioExecutorSchema) { ?.toString() ?.trim() ?.replace('\u001b[2K\u001b[1G', ''); // strip out ansi codes - if (yarnRegistryPath && currentYarnRegistryPath.includes('localhost')) { - execSync( - `yarn config set ${registryConfigName} ${yarnRegistryPath}` + - (options.location === 'user' ? ' --home' : ''), - { env } - ); - logger.info( - `Reset yarn ${registryConfigName} to ${yarnRegistryPath}` - ); + if ( + yarnRegistryPaths.length > 0 && + currentYarnRegistryPath.includes('localhost') + ) { + scopes.forEach((scope, index) => { + const scopeName = scope ? `${scope}:` : ''; + + execSync( + `yarn config set ${scopeName}${registryConfigName} ${yarnRegistryPaths[index]}` + + (options.location === 'user' ? ' --home' : ''), + { env } + ); + logger.info( + `Reset yarn ${scopeName}${registryConfigName} to ${yarnRegistryPaths[index]}` + ); + }); } else { execSync( `yarn config ${ From fc1ad39fa29ef99720ac898cb7623edde82eb14d Mon Sep 17 00:00:00 2001 From: Jason Jean Date: Wed, 24 Jul 2024 15:31:19 -0500 Subject: [PATCH 10/13] =?UTF-8?q?fix(core):=20improve=20error=20for=20sett?= =?UTF-8?q?ing=20an=20internal=20node=20as=20an=20external=20=E2=80=A6=20(?= =?UTF-8?q?#27102)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …dependency ## Current Behavior A unhelpful error is thrown when someone specifies a local project as an external dependency input. ## Expected Behavior A helpful error is thrown when someone specifies a local projecft as an external dependency input. ## Related Issue(s) Fixes https://github.com/nrwl/nx/issues/27088 --- packages/nx/src/native/tasks/hash_planner.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/nx/src/native/tasks/hash_planner.rs b/packages/nx/src/native/tasks/hash_planner.rs index 66e553be2b262..8a458a4b2c0fb 100644 --- a/packages/nx/src/native/tasks/hash_planner.rs +++ b/packages/nx/src/native/tasks/hash_planner.rs @@ -167,7 +167,16 @@ impl HashPlanner { let external_node_name = find_external_dependency_node_name(dep, &self.project_graph); let Some(external_node_name) = external_node_name else { - anyhow::bail!("The externalDependency '{dep}' for '{project_name}:{target_name}' could not be found") + if self.project_graph.nodes.contains_key(dep) { + let deps = self.project_graph.dependencies.get(project_name); + if deps.is_some_and(|deps| deps.contains(dep)) { + anyhow::bail!("The externalDependency '{dep}' for '{project_name}:{target_name}' is not an external node and is already a dependency. Please remove it from the externalDependency inputs.") + } else { + anyhow::bail!("The externalDependency '{dep}' for '{project_name}:{target_name}' is not an external node. If you believe this is a dependency, add an implicitDependency to '{project_name}'") + } + } else { + anyhow::bail!("The externalDependency '{dep}' for '{project_name}:{target_name}' could not be found") + } }; trace!( "Add External Instruction for External Input {external_node_name}: {}", From 0104ea4fc3c45c25b32e90050e2b27888c872b05 Mon Sep 17 00:00:00 2001 From: Craigory Coppola Date: Wed, 24 Jul 2024 17:41:24 -0400 Subject: [PATCH 11/13] fix(core): allow overriding NX_PARALLEL with --parallel (#27016) --- .../command-line/release/command-object.ts | 26 +++---------- .../target-project-locator.spec.ts | 11 +++--- .../nx/src/utils/command-line-utils.spec.ts | 39 +++++++++++++++++++ packages/nx/src/utils/command-line-utils.ts | 21 ++++++---- 4 files changed, 63 insertions(+), 34 deletions(-) diff --git a/packages/nx/src/command-line/release/command-object.ts b/packages/nx/src/command-line/release/command-object.ts index 9ccce78640359..7a66e50bfc151 100644 --- a/packages/nx/src/command-line/release/command-object.ts +++ b/packages/nx/src/command-line/release/command-object.ts @@ -10,6 +10,7 @@ import { withRunManyOptions, } from '../yargs-utils/shared-options'; import { VersionData } from './utils/shared'; +import { readParallelFromArgsAndEnv } from '../../utils/command-line-utils'; export interface NxReleaseArgs { groups?: string[]; @@ -355,27 +356,10 @@ const planCommand: CommandModule = { }; function coerceParallelOption(args: any) { - if (args['parallel'] === 'false' || args['parallel'] === false) { - return { - ...args, - parallel: 1, - }; - } else if ( - args['parallel'] === 'true' || - args['parallel'] === true || - args['parallel'] === '' - ) { - return { - ...args, - parallel: Number(args['maxParallel'] || args['max-parallel'] || 3), - }; - } else if (args['parallel'] !== undefined) { - return { - ...args, - parallel: Number(args['parallel']), - }; - } - return args; + return { + ...args, + parallel: readParallelFromArgsAndEnv(args), + }; } function withGitCommitAndGitTagOptions( diff --git a/packages/nx/src/plugins/js/project-graph/build-dependencies/target-project-locator.spec.ts b/packages/nx/src/plugins/js/project-graph/build-dependencies/target-project-locator.spec.ts index cded6c227b971..16199daf5b8d2 100644 --- a/packages/nx/src/plugins/js/project-graph/build-dependencies/target-project-locator.spec.ts +++ b/packages/nx/src/plugins/js/project-graph/build-dependencies/target-project-locator.spec.ts @@ -864,10 +864,11 @@ describe('TargetProjectLocator', () => { }); describe('isBuiltinModuleImport()', () => { - it('should return true for all node builtin modules', () => { - const allBuiltinModules = require('node:module').builtinModules; - allBuiltinModules.forEach((builtinModule) => { + const allBuiltinModules = require('node:module').builtinModules; + it.each(allBuiltinModules)( + `should return true for %s builtin module`, + (builtinModule) => { expect(isBuiltinModuleImport(builtinModule)).toBe(true); - }); - }); + } + ); }); diff --git a/packages/nx/src/utils/command-line-utils.spec.ts b/packages/nx/src/utils/command-line-utils.spec.ts index d5b66eefbb4b6..50436fd0ef87d 100644 --- a/packages/nx/src/utils/command-line-utils.spec.ts +++ b/packages/nx/src/utils/command-line-utils.spec.ts @@ -473,5 +473,44 @@ describe('splitArgs', () => { expect(parallel).toEqual(5); }); + + it('should be able to be specified in the environment', () => { + const { nxArgs } = withEnvironment( + { + NX_PARALLEL: '5', + }, + () => + splitArgsIntoNxArgsAndOverrides( + { + $0: '', + __overrides_unparsed__: [], + }, + 'affected', + {} as any, + {} as any + ) + ); + expect(nxArgs.parallel).toEqual(5); + }); + + it('should be able to override NX_PARALLEL with the parallel flag', () => { + const { nxArgs } = withEnvironment( + { + NX_PARALLEL: '5', + }, + () => + splitArgsIntoNxArgsAndOverrides( + { + $0: '', + __overrides_unparsed__: [], + parallel: '3', + }, + 'affected', + {} as any, + {} as any + ) + ); + expect(nxArgs.parallel).toEqual(3); + }); }); }); diff --git a/packages/nx/src/utils/command-line-utils.ts b/packages/nx/src/utils/command-line-utils.ts index c6241de5f809b..11928b3129792 100644 --- a/packages/nx/src/utils/command-line-utils.ts +++ b/packages/nx/src/utils/command-line-utils.ts @@ -177,25 +177,30 @@ export function splitArgsIntoNxArgsAndOverrides( normalizeNxArgsRunner(nxArgs, nxJson, options); + nxArgs['parallel'] = readParallelFromArgsAndEnv(args); + + return { nxArgs, overrides } as any; +} + +export function readParallelFromArgsAndEnv(args: { [k: string]: any }) { if (args['parallel'] === 'false' || args['parallel'] === false) { - nxArgs['parallel'] = 1; + return 1; } else if ( args['parallel'] === 'true' || args['parallel'] === true || args['parallel'] === '' || - process.env.NX_PARALLEL // dont require passing --parallel if NX_PARALLEL is set + // dont require passing --parallel if NX_PARALLEL is set, but allow overriding it + (process.env.NX_PARALLEL && args['parallel'] === undefined) ) { - nxArgs['parallel'] = Number( - nxArgs['maxParallel'] || - nxArgs['max-parallel'] || + return Number( + args['maxParallel'] || + args['max-parallel'] || process.env.NX_PARALLEL || 3 ); } else if (args['parallel'] !== undefined) { - nxArgs['parallel'] = Number(args['parallel']); + return Number(args['parallel']); } - - return { nxArgs, overrides } as any; } function normalizeNxArgsRunner( From fc60f6b187f61ccb187d9f316c50923d9921df33 Mon Sep 17 00:00:00 2001 From: Nicholas Cunningham Date: Wed, 24 Jul 2024 18:33:06 -0600 Subject: [PATCH 12/13] feat(nx-dev): Update nx-cloud page ai section (#27103) To be inline with ocean: https://github.com/nrwl/ocean/pull/5379 --- nx-dev/ui-cloud/src/lib/enhance-with-ai.tsx | 39 +++++++++++++++++---- 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/nx-dev/ui-cloud/src/lib/enhance-with-ai.tsx b/nx-dev/ui-cloud/src/lib/enhance-with-ai.tsx index 4287ae8df760d..802f040683459 100644 --- a/nx-dev/ui-cloud/src/lib/enhance-with-ai.tsx +++ b/nx-dev/ui-cloud/src/lib/enhance-with-ai.tsx @@ -5,30 +5,39 @@ import { ServerStackIcon, } from '@heroicons/react/24/outline'; import { SectionHeading } from '@nx/nx-dev/ui-common'; +import Link from 'next/link'; const features = [ { - name: 'Task error insights', + name: 'AI explainer', description: "Debug task errors on your CI pipeline directly in your pipeline's UI.", icon: CodeBracketIcon, + isAvailable: true, + link: '/ci/troubleshooting/explain-with-ai#explain-with-ai-betautm_source=nx.app&utm_campaign=ai-section', }, { name: 'Dynamic Nx Agent sizing', description: - "Automatically adjust Nx Agents' resource classes and numbers depending on your workspace usage and needs.", + 'Automatically adjust Nx Agents numbers depending on your workspace usage and needs.', icon: ServerStackIcon, + isAvailable: false, + link: '', }, { name: 'Task cache miss diagnosis', description: 'Understand why a task has a cache miss and how to fix it.', icon: CloudArrowDownIcon, + isAvailable: false, + link: '', }, { name: 'Organization insights', description: "Understand your teams' workspaces: shared code usage, ownership, bottlenecks.", icon: RectangleGroupIcon, + isAvailable: false, + link: '', }, ]; @@ -37,9 +46,6 @@ export function EnhancedWithAi(): JSX.Element {
- - Coming soon - AI for your CI @@ -52,16 +58,37 @@ export function EnhancedWithAi(): JSX.Element {
{features.map((feature) => (
-
+
{feature.description}
+ {feature.link ? ( + + See documentation{' '} + + + ) : null}
))}
From 3890edc560726d899e5b56c11a0cbb24a9a7cdad Mon Sep 17 00:00:00 2001 From: Jack Hsu Date: Wed, 24 Jul 2024 21:32:01 -0400 Subject: [PATCH 13/13] docs(core): feature Nx Cloud sections more prominently in CLI tutorials (#27077) Updated these tutorials: - https://nx-dev-git-docs-cli-tutorials-promote-cloud-nrwl.vercel.app/getting-started/tutorials/npm-workspaces-tutorial - https://nx-dev-git-docs-cli-tutorials-promote-cloud-nrwl.vercel.app/getting-started/tutorials/react-standalone-tutorial - https://nx-dev-git-docs-cli-tutorials-promote-cloud-nrwl.vercel.app/getting-started/tutorials/react-monorepo-tutorial - https://nx-dev-git-docs-cli-tutorials-promote-cloud-nrwl.vercel.app/getting-started/tutorials/angular-standalone-tutorial - https://nx-dev-git-docs-cli-tutorials-promote-cloud-nrwl.vercel.app/getting-started/tutorials/angular-monorepo-tutorial - https://nx-dev-git-docs-cli-tutorials-promote-cloud-nrwl.vercel.app/getting-started/tutorials/vue-standalone-tutorial - https://nx-dev-git-docs-cli-tutorials-promote-cloud-nrwl.vercel.app/getting-started/tutorials/gradle-tutorial - https://nx-dev-git-docs-cli-tutorials-promote-cloud-nrwl.vercel.app/recipes/adopting-nx/adding-to-monorepo - https://nx-dev-git-docs-cli-tutorials-promote-cloud-nrwl.vercel.app/recipes/adopting-nx/adding-to-existing-project --- .../migration/adding-to-existing-project.md | 14 ++-- docs/shared/migration/adding-to-monorepo.md | 16 ++-- docs/shared/tutorials/angular-monorepo.md | 15 ++-- docs/shared/tutorials/angular-standalone.md | 15 ++-- docs/shared/tutorials/gradle.md | 74 +++++++++++++------ docs/shared/tutorials/npm-workspaces.md | 72 +++++++++++------- docs/shared/tutorials/react-monorepo.md | 15 ++-- docs/shared/tutorials/react-standalone.md | 15 ++-- docs/shared/tutorials/vue-standalone.md | 15 ++-- .../src/lib/table-of-contents.tsx | 28 ++++++- .../src/lib/nodes/heading.component.tsx | 14 +++- .../src/lib/nodes/heading.schema.ts | 1 + 12 files changed, 202 insertions(+), 92 deletions(-) diff --git a/docs/shared/migration/adding-to-existing-project.md b/docs/shared/migration/adding-to-existing-project.md index 68f371c06abf2..130d4d3af5d1f 100644 --- a/docs/shared/migration/adding-to-existing-project.md +++ b/docs/shared/migration/adding-to-existing-project.md @@ -1,6 +1,6 @@ # Adding Nx to your Existing Project -Nx can be added to any type of project, not just monorepos. The main benefit is to get caching abilities for the package +Nx can be added to any type of project, not just monorepos. A large benefit of Nx is its caching feature for package scripts. Each project usually has a set of scripts in the `package.json`: ```json {% fileName="package.json" %} @@ -14,12 +14,14 @@ scripts. Each project usually has a set of scripts in the `package.json`: } ``` -You can make these scripts faster by leveraging Nx's caching capabilities. For example: +You can make these scripts faster by leveraging Nx's [caching capabilities](/features/cache-task-results). For example: - You change some spec files: in that case the `build` task can be cached and doesn't have to re-run. - You update your docs, changing a couple of markdown files: then there's no need to re-run builds, tests, linting on your CI. All you might want to do is trigger the Docusaurus build. +Additionally, Nx also [speeds up your CI ⚡](#fast-ci) with [remote caching](/ci/features/remote-cache) and [distributed task execution](/ci/features/distribute-task-execution). + ## Install Nx on a Non-Monorepo Project Run the following command: @@ -284,7 +286,7 @@ Now if you run `npm run test` or `nx test` twice, the results will be retrieved this example are as cautious as possible, so you can significantly improve the value of the cache by [customizing Nx Inputs](/recipes/running-tasks/configure-inputs) for each task. -## Set Up CI for Your Workspace +## Fast CI ⚡ {% highlightColor="green" %} This tutorial walked you through how Nx can improve the local development experience, but the biggest difference Nx makes is in CI. As repositories get bigger, making sure that the CI is fast, reliable and maintainable can get very challenging. Nx provides a solution. @@ -293,7 +295,7 @@ This tutorial walked you through how Nx can improve the local development experi - Nx Agents [efficiently distribute tasks across machines](/ci/concepts/parallelization-distribution) ensuring constant CI time regardless of the repository size. The right number of machines is allocated for each PR to ensure good performance without wasting compute. - Nx Atomizer [automatically splits](/ci/features/split-e2e-tasks) large e2e tests to distribute them across machines. Nx can also automatically [identify and rerun flaky e2e tests](/ci/features/flaky-tasks). -### Connect to Nx Cloud +### Connect to Nx Cloud {% highlightColor="green" %} Nx Cloud is a companion app for your CI system that provides remote caching, task distribution, e2e tests deflaking, better DX and more. @@ -325,7 +327,7 @@ git pull You should now have an `nxCloudAccessToken` property specified in the `nx.json` file. -### Create a CI Workflow +### Create a CI Workflow {% highlightColor="green" %} Use the following command to generate a CI workflow file. @@ -362,7 +364,7 @@ jobs: - run: npx nx affected -t lint test build ``` -### Open a Pull Request +### Open a Pull Request {% highlightColor="green" %} Commit the changes and open a new PR on GitHub. diff --git a/docs/shared/migration/adding-to-monorepo.md b/docs/shared/migration/adding-to-monorepo.md index 5644f7c5bf312..fb224dd8bc163 100644 --- a/docs/shared/migration/adding-to-monorepo.md +++ b/docs/shared/migration/adding-to-monorepo.md @@ -6,14 +6,12 @@ powering Nx underneath. As a result, Lerna gets all the modern features such as on [https://lerna.js.org/upgrade](https://lerna.js.org/upgrade). {% /callout %} -Nx has first-class support for [monorepos](/getting-started/tutorials/npm-workspaces-tutorial). As a result, if you have +Nx has first-class support for [monorepos](/getting-started/tutorials/npm-workspaces-tutorial). If you have an existing NPM/Yarn or PNPM-based monorepo setup, you can easily add Nx to get - fast [task scheduling](/features/run-tasks) -- support for [task pipelines](/concepts/task-pipeline-configuration) -- [caching](/features/cache-task-results) -- [remote caching with Nx Cloud](/ci/features/remote-cache) -- [distributed task execution with Nx Cloud](/ci/features/distribute-task-execution) +- high-performance task [caching](/features/cache-task-results) +- [fast CI ⚡](#fast-ci) with [remote caching](/ci/features/remote-cache) and [distributed task execution](/ci/features/distribute-task-execution) This is a low-impact operation because all that needs to be done is to install the `nx` package at the root level and add an `nx.json` for configuring caching and task pipelines. @@ -307,7 +305,7 @@ pnpm run -r test This allows for incrementally adopting Nx in your existing workspace. -## Set Up CI for Your Workspace +## Fast CI ⚡ {% highlightColor="green" %} This tutorial walked you through how Nx can improve the local development experience, but the biggest difference Nx makes is in CI. As repositories get bigger, making sure that the CI is fast, reliable and maintainable can get very challenging. Nx provides a solution. @@ -316,7 +314,7 @@ This tutorial walked you through how Nx can improve the local development experi - Nx Agents [efficiently distribute tasks across machines](/ci/concepts/parallelization-distribution) ensuring constant CI time regardless of the repository size. The right number of machines is allocated for each PR to ensure good performance without wasting compute. - Nx Atomizer [automatically splits](/ci/features/split-e2e-tasks) large e2e tests to distribute them across machines. Nx can also automatically [identify and rerun flaky e2e tests](/ci/features/flaky-tasks). -### Connect to Nx Cloud +### Connect to Nx Cloud {% highlightColor="green" %} Nx Cloud is a companion app for your CI system that provides remote caching, task distribution, e2e tests deflaking, better DX and more. @@ -348,7 +346,7 @@ git pull You should now have an `nxCloudAccessToken` property specified in the `nx.json` file. -### Create a CI Workflow +### Create a CI Workflow {% highlightColor="green" %} Use the following command to generate a CI workflow file. @@ -385,7 +383,7 @@ jobs: - run: npx nx affected -t lint test build ``` -### Open a Pull Request +### Open a Pull Request {% highlightColor="green" %} Commit the changes and open a new PR on GitHub. diff --git a/docs/shared/tutorials/angular-monorepo.md b/docs/shared/tutorials/angular-monorepo.md index 3d31f77da4335..893233bf8d4fa 100644 --- a/docs/shared/tutorials/angular-monorepo.md +++ b/docs/shared/tutorials/angular-monorepo.md @@ -7,12 +7,13 @@ description: In this tutorial you'll create a frontend-focused workspace with Nx In this tutorial you'll learn how to use Angular with Nx in a [monorepo (integrated) setup](/concepts/integrated-vs-package-based#integrated-repos). -What are you going to learn? +What will you learn? - how to create a new Angular application - how to run a single task (i.e. serve your app) or run multiple tasks in parallel - how to leverage code generators to scaffold components - how to modularize your codebase and impose architectural constraints for better maintainability +- [how to speed up CI with Nx Cloud ⚡](#fast-ci) {% callout type="info" title="Looking for an Angular standalone app?" %} Note, this tutorial sets up a repo with applications and libraries in their own subfolders. If you are looking for an Angular standalone app setup then check out our [Angular standalone app tutorial](/getting-started/tutorials/angular-standalone-tutorial). @@ -1238,7 +1239,11 @@ If you have the ESLint plugin installed in your IDE you should immediately see a Learn more about how to [enforce module boundaries](/features/enforce-module-boundaries). -## Set Up CI for Your Angular Monorepo +## Fast CI ⚡ {% highlightColor="green" %} + +{% callout type="check" title="Repository with Nx" %} +Make sure you have completed the previous sections of this tutorial before starting this one. If you want a clean starting point, you can check out the [reference code](https://github.com/nrwl/nx-recipes/tree/main/angular-monorepo) as a starting point. +{% /callout %} {% video-link link="https://youtu.be/ZzTP4bVJEnI?t=791" /%} @@ -1249,7 +1254,7 @@ This tutorial walked you through how Nx can improve the local development experi - Nx Agents [efficiently distribute tasks across machines](/ci/concepts/parallelization-distribution) ensuring constant CI time regardless of the repository size. The right number of machines is allocated for each PR to ensure good performance without wasting compute. - Nx Atomizer [automatically splits](/ci/features/split-e2e-tasks) large e2e tests to distribute them across machines. Nx can also automatically [identify and rerun flaky e2e tests](/ci/features/flaky-tasks). -### Connect to Nx Cloud +### Connect to Nx Cloud {% highlightColor="green" %} Nx Cloud is a companion app for your CI system that provides remote caching, task distribution, e2e tests deflaking, better DX and more. @@ -1267,7 +1272,7 @@ npx nx connect Once you click the link, follow the steps provided and make sure Nx Cloud is enabled on the main branch of your repository. -### Configure Your CI Workflow +### Configure Your CI Workflow {% highlightColor="green" %} When you chose GitHub Actions as your CI provider at the beginning of the tutorial, `create-nx-workspace` created a `.github/workflows/ci.yml` file that contains a CI pipeline that will run the `lint`, `test`, `build` and `e2e` tasks for projects that are affected by any given PR. Since we are using Nx Cloud, the pipeline will also distribute tasks across multiple machines to ensure fast and reliable CI runs. @@ -1304,7 +1309,7 @@ jobs: - run: npx nx affected -t lint test build ``` -### Open a Pull Request +### Open a Pull Request {% highlightColor="green" %} Commit the changes and open a new PR on GitHub. diff --git a/docs/shared/tutorials/angular-standalone.md b/docs/shared/tutorials/angular-standalone.md index 47e058d1c63a9..256a1e2096435 100644 --- a/docs/shared/tutorials/angular-standalone.md +++ b/docs/shared/tutorials/angular-standalone.md @@ -2,12 +2,13 @@ In this tutorial you'll learn how to use Angular with Nx in a ["standalone" (non-monorepo) setup](/concepts/integrated-vs-package-based#standalone-applications). Not to be confused with the "Angular Standalone API", a standalone project in Nx is a non-monorepo setup where you have a single application at the root level. This setup is very similar to what the Angular CLI gives you. -What are you going to learn? +What will you learn? - how to create a new standalone (single-project) Nx workspace setup for Angular - how to run a single task (i.e. serve your app) or run multiple tasks in parallel - how to leverage code generators to scaffold components - how to modularize your codebase and impose architectural constraints for better maintainability +- [how to speed up CI with Nx Cloud ⚡](#fast-ci) {% callout type="info" title="Looking for Angular monorepos?" %} Note, this tutorial sets up a repo with a single application at the root level that breaks out its code into libraries to add structure. If you are looking for an Angular monorepo setup then check out our [Angular monorepo tutorial](/getting-started/tutorials/angular-monorepo-tutorial). @@ -1041,7 +1042,11 @@ When you are ready to add another application to the repo, you'll probably want You can also go through the full [Angular monorepo tutorial](/getting-started/tutorials/angular-monorepo-tutorial) -## Set Up CI for the Angular App +## Fast CI ⚡ {% highlightColor="green" %} + +{% callout type="check" title="Repository with Nx" %} +Make sure you have completed the previous sections of this tutorial before starting this one. If you want a clean starting point, you can check out the [reference code](https://github.com/nrwl/nx-recipes/tree/main/angular-standalone) as a starting point. +{% /callout %} This tutorial walked you through how Nx can improve the local development experience, but the biggest difference Nx makes is in CI. As repositories get bigger, making sure that the CI is fast, reliable and maintainable can get very challenging. Nx provides a solution. @@ -1050,7 +1055,7 @@ This tutorial walked you through how Nx can improve the local development experi - Nx Agents [efficiently distribute tasks across machines](/ci/concepts/parallelization-distribution) ensuring constant CI time regardless of the repository size. The right number of machines is allocated for each PR to ensure good performance without wasting compute. - Nx Atomizer [automatically splits](/ci/features/split-e2e-tasks) large e2e tests to distribute them across machines. Nx can also automatically [identify and rerun flaky e2e tests](/ci/features/flaky-tasks). -### Connect to Nx Cloud +### Connect to Nx Cloud {% highlightColor="green" %} Nx Cloud is a companion app for your CI system that provides remote caching, task distribution, e2e tests deflaking, better DX and more. @@ -1068,7 +1073,7 @@ npx nx connect Once you click the link, follow the steps provided and make sure Nx Cloud is enabled on the main branch of your repository. -### Configure Your CI Workflow +### Configure Your CI Workflow {% highlightColor="green" %} When you chose GitHub Actions as your CI provider at the beginning of the tutorial, `create-nx-workspace` created a `.github/workflows/ci.yml` file that contains a CI pipeline that will run the `lint`, `test`, `build` and `e2e` tasks for projects that are affected by any given PR. Since we are using Nx Cloud, the pipeline will also distribute tasks across multiple machines to ensure fast and reliable CI runs. @@ -1105,7 +1110,7 @@ jobs: - run: npx nx affected -t lint test build ``` -### Open a Pull Request +### Open a Pull Request {% highlightColor="green" %} Commit the changes and open a new PR on GitHub. diff --git a/docs/shared/tutorials/gradle.md b/docs/shared/tutorials/gradle.md index aaf3e945d3f41..dd7d0d2428c31 100644 --- a/docs/shared/tutorials/gradle.md +++ b/docs/shared/tutorials/gradle.md @@ -5,8 +5,15 @@ description: In this tutorial you'll add Nx to an existing Gradle repo # Gradle Tutorial -In this tutorial, you'll learn how to add Nx to a repository with an existing Gradle setup. You'll see how Nx can -provide immediate value. +In this tutorial, you'll learn how to add Nx to a repository with an existing Gradle setup. + +What will you learn? + +- how to add Nx to a Gradle project +- how to run a single task (i.e. serve your app) or run multiple tasks in parallel +- how to leverage code generators to scaffold components +- how to modularize your codebase and impose architectural constraints for better maintainability +- [how to speed up CI with Nx Cloud ⚡](#fast-ci) ## Prerequisites @@ -31,11 +38,7 @@ node -v This tutorial picks up where [Spring framework](https://spring.io/)'s guide for [Multi-Module Projects](https://spring.io/guides/gs/multi-module) leaves off. -Fork [the sample repository](https://github.com/nrwl/gradle-tutorial): - -[https://github.com/nrwl/gradle-tutorial](https://github.com/nrwl/gradle-tutorial) - -And then clone it on your local machine: +Fork [the sample repository](https://github.com/nrwl/gradle-tutorial/fork), and then clone it on your local machine: ```shell git clone https://github.com//gradle-tutorial.git @@ -66,7 +69,11 @@ Root project 'gradle-tutorial' Nx is a build system with built in tooling and advanced CI capabilities. It helps you maintain and scale monorepos, both locally and on CI. We will explore the features of Nx in this tutorial by adding it to the Gradle workspace above. -To add Nx, run `npx nx@latest init`. +To add Nx, run + +```shell {% path="~/gradle-tutorial" %} +npx nx@latest init +``` This command will download the latest version of Nx and help set up your repository to take advantage of it. Nx will also detect Gradle is used in the repo so it will propose adding the `@nx/gradle` plugin to integrate Gradle with Nx. @@ -260,7 +267,11 @@ To run the `test` tasks for projects affected by this change, run: Notice that this command does not run the `test` task for the `library` project, since it could not have been affected by the code change. -## Set Up CI for Your Gradle Workspace +## Fast CI ⚡ {% highlightColor="green" %} + +{% callout type="check" title="Repository with Nx" %} +Make sure you have completed the previous sections of this tutorial before starting this one. If you want a clean starting point, you can check out the [reference code](https://github.com/nrwl/nx-recipes/tree/main/gradle) as a starting point. +{% /callout %} This tutorial walked you through how Nx can improve the local development experience, but the biggest difference Nx makes is in CI. As repositories get bigger, making sure that the CI is fast, reliable and maintainable can get very challenging. Nx provides a solution. @@ -269,7 +280,7 @@ This tutorial walked you through how Nx can improve the local development experi - Nx Agents [efficiently distribute tasks across machines](/ci/concepts/parallelization-distribution) ensuring constant CI time regardless of the repository size. The right number of machines is allocated for each PR to ensure good performance without wasting compute. - Nx Atomizer [automatically splits](/ci/features/split-e2e-tasks) large e2e tests to distribute them across machines. Nx can also automatically [identify and rerun flaky e2e tests](/ci/features/flaky-tasks). -### Connect to Nx Cloud +### Connect to Nx Cloud {% highlightColor="green" %} Nx Cloud is a companion app for your CI system that provides remote caching, task distribution, e2e tests deflaking, better DX and more. @@ -282,7 +293,7 @@ Now that we're working on the CI pipeline, it is important for your changes to b Now connect your repository to Nx Cloud with the following command: ```shell -npx nx connect +./nx connect ``` A browser window will open to register your repository in your [Nx Cloud](https://cloud.nx.app) account. The link is also printed to the terminal if the windows does not open, or you closed it before finishing the steps. The app will guide you to create a PR to enable Nx Cloud on your repository. @@ -301,7 +312,7 @@ git pull You should now have an `nxCloudAccessToken` property specified in the `nx.json` file. -### Create a CI Workflow +### Create a CI Workflow {% highlightColor="green" %} Let's create a branch to add a CI workflow. @@ -312,16 +323,26 @@ git checkout -b add-workflow And use the following command to generate a CI workflow file. ```shell -npx nx generate ci-workflow --ci=github +./nx generate ci-workflow --ci=github ``` This generator creates a `.github/workflows/ci.yml` file that contains a CI pipeline that will run the `lint`, `test`, `build` and `e2e` tasks for projects that are affected by any given PR. Since we are using Nx Cloud, the pipeline will also distribute tasks across multiple machines to ensure fast and reliable CI runs. The key lines in the CI pipeline are: -```yml {% fileName=".github/workflows/ci.yml" highlightLines=["10-14", "21-22"] %} +```yml {% fileName=".github/workflows/ci.yml" highlightLines=["21-24", "38-39"] %} name: CI -# ... + +on: + push: + branches: + - main + pull_request: + +permissions: + actions: read + contents: read + jobs: main: runs-on: ubuntu-latest @@ -329,22 +350,29 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 + # This enables task distribution via Nx Cloud # Run this command as early as possible, before dependencies are installed # Learn more at https://nx.dev/ci/reference/nx-cloud-cli#npx-nxcloud-startcirun - # Connect your workspace by running "nx connect" and uncomment this - - run: npx nx-cloud start-ci-run --distribute-on="3 linux-medium-js" --stop-agents-after="build" - - uses: actions/setup-node@v3 + - run: npx nx-cloud start-ci-run --distribute-on="3 linux-medium-jvm" --stop-agents-after="build" + + - name: Set up JDK 17 for x64 + uses: actions/setup-java@v4 with: - node-version: 20 - cache: 'npm' - - run: npm ci --legacy-peer-deps + java-version: '17' + distribution: 'temurin' + architecture: x64 + + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 + - uses: nrwl/nx-set-shas@v4 + # Nx Affected runs only tasks affected by the changes in this PR/commit. Learn more: https://nx.dev/ci/features/affected - - run: npx nx affected -t lint test build + - run: ./nx affected -t test build ``` -### Open a Pull Request +### Open a Pull Request {% highlightColor="green" %} Commit the changes and open a new PR on GitHub. diff --git a/docs/shared/tutorials/npm-workspaces.md b/docs/shared/tutorials/npm-workspaces.md index 5663831a84ab1..5d2c6b7202233 100644 --- a/docs/shared/tutorials/npm-workspaces.md +++ b/docs/shared/tutorials/npm-workspaces.md @@ -5,12 +5,16 @@ description: In this tutorial you'll add Nx to an existing NPM workspaces repo # NPM Workspaces Tutorial -In this tutorial, you'll learn how to add Nx to a repository with an existing NPM workspaces setup. You'll see how Nx can provide immediate value with very little configuration and then you can gradually enable more features. +In this tutorial, you'll learn how to add Nx to a repository with an existing [NPM workspaces](https://docs.npmjs.com/cli/using-npm/workspaces) setup. -- Add Nx to the repository with a single command -- Configure caching for your existing tasks -- Configure a task pipeline -- Use Nx Plugins to automatically configure caching +What will you learn? + +- how to add Nx to the repository with a single command +- how to configure caching for your tasks +- how to configure a task pipeline +- how to configure projects automatically with Nx Plugins +- how to manage your releases with `nx release` +- [how to speed up CI with Nx Cloud ⚡](#fast-ci)