From 35750280552350477bc347bc2ce962f5463719d8 Mon Sep 17 00:00:00 2001 From: Craigory Coppola Date: Thu, 30 Mar 2023 10:18:47 -0400 Subject: [PATCH] feat(core): version option should display global and local installation separately (#15911) --- e2e/nx-misc/src/misc.test.ts | 40 +++++++++++++++ packages/nx/bin/nx.ts | 56 +++++++++++++++------ packages/nx/src/command-line/nx-commands.ts | 6 ++- packages/nx/src/command-line/report.ts | 13 ++++- 4 files changed, 97 insertions(+), 18 deletions(-) diff --git a/e2e/nx-misc/src/misc.test.ts b/e2e/nx-misc/src/misc.test.ts index 016f34fc0b23b..c85bdb6bfd595 100644 --- a/e2e/nx-misc/src/misc.test.ts +++ b/e2e/nx-misc/src/misc.test.ts @@ -623,6 +623,7 @@ describe('global installation', () => { }); it('should warn if local Nx has higher major version', () => { + const packageJsonContents = readFile('node_modules/nx/package.json'); updateJson('node_modules/nx/package.json', (json) => { json.version = `${major(getPublishedVersion()) + 2}.0.0`; return json; @@ -632,5 +633,44 @@ describe('global installation', () => { output = runCommand(`nx show projects`); }).not.toThrow(); expect(output).toContain('Its time to update Nx'); + updateFile('node_modules/nx/package.json', packageJsonContents); + }); + + it('--version should display global installs version', () => { + const packageJsonContents = readFile('node_modules/nx/package.json'); + const localVersion = `${major(getPublishedVersion()) + 2}.0.0`; + updateJson('node_modules/nx/package.json', (json) => { + json.version = localVersion; + return json; + }); + let output: string; + expect(() => { + output = runCommand(`nx --version`); + }).not.toThrow(); + expect(output).toContain(`- Local: v${localVersion}`); + expect(output).toContain(`- Global: v${getPublishedVersion()}`); + updateFile('node_modules/nx/package.json', packageJsonContents); + }); + + it('report should display global installs version', () => { + const packageJsonContents = readFile('node_modules/nx/package.json'); + const localVersion = `${major(getPublishedVersion()) + 2}.0.0`; + updateJson('node_modules/nx/package.json', (json) => { + json.version = localVersion; + return json; + }); + let output: string; + expect(() => { + output = runCommand(`nx report`); + }).not.toThrow(); + expect(output).toEqual( + expect.stringMatching(new RegExp(`nx.*:.*${localVersion}`)) + ); + expect(output).toEqual( + expect.stringMatching( + new RegExp(`nx \\(global\\).*:.*${getPublishedVersion()}`) + ) + ); + updateFile('node_modules/nx/package.json', packageJsonContents); }); }); diff --git a/packages/nx/bin/nx.ts b/packages/nx/bin/nx.ts index c4f7ffec46b02..ea96c3ad8a872 100644 --- a/packages/nx/bin/nx.ts +++ b/packages/nx/bin/nx.ts @@ -12,6 +12,7 @@ import { } from '../src/utils/installation-directory'; import { major } from 'semver'; import { readJsonFile } from '../src/utils/fileutils'; +import { stripIndents } from '../src/utils/strip-indents'; import { execSync } from 'child_process'; function main() { @@ -60,21 +61,36 @@ function main() { try { localNx = resolveNx(workspace); } catch { - // If we can't resolve a local copy of Nx, we must be global. - warnIfUsingOutdatedGlobalInstall(); - output.error({ - title: `Could not find Nx modules in this workspace.`, - bodyLines: [`Have you run ${chalk.bold.white(`npm/yarn install`)}?`], - }); - process.exit(1); + localNx = null; + } + + const isLocalInstall = localNx === resolveNx(null); + const LOCAL_NX_VERSION: string | null = localNx + ? getLocalNxVersion(workspace) + : null; + const GLOBAL_NX_VERSION: string | null = isLocalInstall + ? null + : require('../package.json').version; + + globalThis.GLOBAL_NX_VERSION ??= GLOBAL_NX_VERSION; + + if (process.argv[2] === '--version') { + console.log(stripIndents`Nx Version: + - Local: v${LOCAL_NX_VERSION ?? 'Not found'} + - Global: v${GLOBAL_NX_VERSION ?? 'Not found'}`); + process.exit(0); + } + + if (!localNx) { + handleMissingLocalInstallation(); } // this file is already in the local workspace - if (localNx === resolveNx(null)) { + if (isLocalInstall) { initLocal(workspace); } else { // Nx is being run from globally installed CLI - hand off to the local - warnIfUsingOutdatedGlobalInstall(getLocalNxVersion(workspace)); + warnIfUsingOutdatedGlobalInstall(GLOBAL_NX_VERSION, LOCAL_NX_VERSION); if (localNx.includes('.nx')) { const nxWrapperPath = localNx.replace(/\.nx.*/, '.nx/') + 'nxw.js'; require(nxWrapperPath); @@ -106,24 +122,34 @@ function resolveNx(workspace: WorkspaceTypeAndRoot | null) { } } +function handleMissingLocalInstallation() { + output.error({ + title: `Could not find Nx modules in this workspace.`, + bodyLines: [`Have you run ${chalk.bold.white(`npm/yarn install`)}?`], + }); + process.exit(1); +} + /** * Assumes currently running Nx is global install. * Warns if out of date by 1 major version or more. */ -function warnIfUsingOutdatedGlobalInstall(localNxVersion?: string) { - const globalVersion = require('../package.json').version; +function warnIfUsingOutdatedGlobalInstall( + globalNxVersion: string, + localNxVersion?: string +) { const isOutdatedGlobalInstall = - globalVersion && - ((localNxVersion && major(globalVersion) < major(localNxVersion)) || + globalNxVersion && + ((localNxVersion && major(globalNxVersion) < major(localNxVersion)) || (!localNxVersion && getLatestVersionOfNx() && - major(globalVersion) < major(getLatestVersionOfNx()))); + major(globalNxVersion) < major(getLatestVersionOfNx()))); // Using a global Nx Install if (isOutdatedGlobalInstall) { const bodyLines = localNxVersion ? [ - `Your repository uses a higher version of Nx (${localNxVersion}) than your global CLI version (${globalVersion})`, + `Your repository uses a higher version of Nx (${localNxVersion}) than your global CLI version (${globalNxVersion})`, ] : []; diff --git a/packages/nx/src/command-line/nx-commands.ts b/packages/nx/src/command-line/nx-commands.ts index 521a9c000c5b6..b996b1e18b4dc 100644 --- a/packages/nx/src/command-line/nx-commands.ts +++ b/packages/nx/src/command-line/nx-commands.ts @@ -9,6 +9,7 @@ import { getPackageManagerCommand } from '../utils/package-manager'; import { writeJsonFile } from '../utils/fileutils'; import { WatchArguments } from './watch'; import { runNxSync } from '../utils/child-process'; +import { stripIndents } from '../utils/strip-indents'; // Ensure that the output takes up the available width of the terminal. yargs.wrap(yargs.terminalWidth()); @@ -411,7 +412,10 @@ export const commandsObject = yargs }) .scriptName('nx') .help() - .version(nxVersion); + // NOTE: we handle --version in nx.ts, this just tells yargs that the option exists + // so that it shows up in help. The default yargs implementation of --version is not + // hit, as the implementation in nx.ts is hit first and calls process.exit(0). + .version(); function withShowOptions(yargs: yargs.Argv): yargs.Argv { return yargs.positional('object', { diff --git a/packages/nx/src/command-line/report.ts b/packages/nx/src/command-line/report.ts index 5897efdb1a1b3..2eee47eab333a 100644 --- a/packages/nx/src/command-line/report.ts +++ b/packages/nx/src/command-line/report.ts @@ -1,5 +1,4 @@ import * as chalk from 'chalk'; -import { workspaceRoot } from '../utils/workspace-root'; import { output } from '../utils/output'; import { join } from 'path'; import { @@ -27,7 +26,6 @@ const nxPackageJson = readJsonFile( ); export const packagesWeCareAbout = [ - 'nx', 'lerna', ...nxPackageJson['nx-migrations'].packageGroup.map((x) => typeof x === 'string' ? x : x.package @@ -161,6 +159,16 @@ export async function getReportData(): Promise { } const packageVersionsWeCareAbout = findInstalledPackagesWeCareAbout(); + packageVersionsWeCareAbout.unshift({ + package: 'nx', + version: nxPackageJson.version, + }); + if (globalThis.GLOBAL_NX_VERSION) { + packageVersionsWeCareAbout.unshift({ + package: 'nx (global)', + version: globalThis.GLOBAL_NX_VERSION, + }); + } const outOfSyncPackageGroup = findMisalignedPackagesForPackage(nxPackageJson); @@ -248,6 +256,7 @@ export function findInstalledCommunityPlugins(): PackageJson[] { const installedPlugins = findInstalledPlugins(); return installedPlugins.filter( (dep) => + dep.name !== 'nx' && !patternsWeIgnoreInCommunityReport.some((pattern) => typeof pattern === 'string' ? pattern === dep.name