diff --git a/docs/generated/packages/js/executors/release-publish.json b/docs/generated/packages/js/executors/release-publish.json index 49ec6e4cc38f6..2a46e54504778 100644 --- a/docs/generated/packages/js/executors/release-publish.json +++ b/docs/generated/packages/js/executors/release-publish.json @@ -19,6 +19,10 @@ "tag": { "type": "string", "description": "The distribution tag to apply to the published package." + }, + "dryRun": { + "type": "boolean", + "description": "Whether to run the command without actually publishing the package to the registry." } }, "required": [], diff --git a/e2e/release/src/release.test.ts b/e2e/release/src/release.test.ts index a170481b466a7..7b6e8c167cc0e 100644 --- a/e2e/release/src/release.test.ts +++ b/e2e/release/src/release.test.ts @@ -276,10 +276,9 @@ describe('nx release', () => { ).length ).toEqual(1); - // publish to custom registry (not e2e registry), and a custom dist tag of "next" - const publishOutput2 = runCLI( - `release publish --registry=${customRegistryUrl} --tag=next` - ); + // Perform an initial dry-run of the publish to the custom registry (not e2e registry), and a custom dist tag of "next" + const publishToNext = `release publish --registry=${customRegistryUrl} --tag=next`; + const publishOutput2 = runCLI(`${publishToNext} --dry-run`); expect(publishOutput2).toMatchInlineSnapshot(` > NX Running target nx-release-publish for 3 projects: @@ -291,6 +290,111 @@ describe('nx release', () => { With additional flags: --registry=${customRegistryUrl} --tag=next + --dryRun=true + + + + > nx run {project-name}:nx-release-publish + + + 📦 @proj/{project-name}@1000.0.0-next.0 + === Tarball Contents === + + XXB index.js + XXXB package.json + XXB project.json + === Tarball Details === + name: @proj/{project-name} + version: 1000.0.0-next.0 + filename: proj-{project-name}-1000.0.0-next.0.tgz + package size: XXXB + unpacked size: XXXB + shasum: {SHASUM} + integrity: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + total files: 3 + + Would publish to ${customRegistryUrl} with tag "next", but [dry-run] was set + + > nx run {project-name}:nx-release-publish + + + 📦 @proj/{project-name}@1000.0.0-next.0 + === Tarball Contents === + + XXB index.js + XXXB package.json + XXB project.json + === Tarball Details === + name: @proj/{project-name} + version: 1000.0.0-next.0 + filename: proj-{project-name}-1000.0.0-next.0.tgz + package size: XXXB + unpacked size: XXXB + shasum: {SHASUM} + integrity: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + total files: 3 + + Would publish to ${customRegistryUrl} with tag "next", but [dry-run] was set + + > nx run {project-name}:nx-release-publish + + + 📦 @proj/{project-name}@1000.0.0-next.0 + === Tarball Contents === + + XXB index.js + XXXB package.json + XXB project.json + === Tarball Details === + name: @proj/{project-name} + version: 1000.0.0-next.0 + filename: proj-{project-name}-1000.0.0-next.0.tgz + package size: XXXB + unpacked size: XXXB + shasum: {SHASUM} + integrity: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + total files: 3 + + Would publish to ${customRegistryUrl} with tag "next", but [dry-run] was set + + + + > NX Successfully ran target nx-release-publish for 3 projects + + + + `); + + // Versions are still unpublished on the next tag in the custom registry, because it was only a dry-run + expect(() => + execSync( + `npm view @proj/${pkg1}@next version --registry=${customRegistryUrl}` + ) + ).toThrowError(/npm ERR! code E404/); + expect(() => + execSync( + `npm view @proj/${pkg2}@next version --registry=${customRegistryUrl}` + ) + ).toThrowError(/npm ERR! code E404/); + expect(() => + execSync( + `npm view @proj/${pkg3}@next version --registry=${customRegistryUrl}` + ) + ).toThrowError(/npm ERR! code E404/); + + // Actually publish to the custom registry (not e2e registry), and a custom dist tag of "next" + const publishOutput3 = runCLI(publishToNext); + expect(publishOutput3).toMatchInlineSnapshot(` + + > NX Running target nx-release-publish for 3 projects: + + - {project-name} + - {project-name} + - {project-name} + + With additional flags: + --registry=${customRegistryUrl} + --tag=next @@ -365,6 +469,7 @@ describe('nx release', () => { `); + // The versions now exist on the next tag in the custom registry expect( execSync( `npm view @proj/${pkg1}@next version --registry=${customRegistryUrl}` diff --git a/packages/js/src/executors/release-publish/release-publish.impl.ts b/packages/js/src/executors/release-publish/release-publish.impl.ts index 2e0eb2c57a94c..8f18b957eeeb6 100644 --- a/packages/js/src/executors/release-publish/release-publish.impl.ts +++ b/packages/js/src/executors/release-publish/release-publish.impl.ts @@ -3,6 +3,7 @@ import { execSync } from 'child_process'; import { env as appendLocalEnv } from 'npm-run-path'; import { logTar } from './log-tar'; import { PublishExecutorSchema } from './schema'; +import chalk = require('chalk'); const LARGE_BUFFER = 1024 * 1000000; @@ -40,6 +41,10 @@ export default async function runExecutor( npmPublishCommandSegments.push(`--tag=${options.tag}`); } + if (options.dryRun) { + npmPublishCommandSegments.push(`--dry-run`); + } + // Resolve values using the `npm config` command so that things like environment variables and `publishConfig`s are accounted for const registry = options.registry ?? execSync(`npm config get registry`).toString().trim(); @@ -59,7 +64,15 @@ export default async function runExecutor( const normalizedStdoutData = stdoutData[context.projectName!] ?? stdoutData; logTar(normalizedStdoutData); - console.log(`Published to ${registry} with tag "${tag}"`); + if (options.dryRun) { + console.log( + `Would publish to ${registry} with tag "${tag}", but ${chalk.keyword( + 'orange' + )('[dry-run]')} was set` + ); + } else { + console.log(`Published to ${registry} with tag "${tag}"`); + } return { success: true, diff --git a/packages/js/src/executors/release-publish/schema.d.ts b/packages/js/src/executors/release-publish/schema.d.ts index 19de5b5518035..2980dc7910280 100644 --- a/packages/js/src/executors/release-publish/schema.d.ts +++ b/packages/js/src/executors/release-publish/schema.d.ts @@ -2,4 +2,5 @@ export interface PublishExecutorSchema { packageRoot?: string; registry?: string; tag?: string; + dryRun?: boolean; } diff --git a/packages/js/src/executors/release-publish/schema.json b/packages/js/src/executors/release-publish/schema.json index 149d7b0b6d38c..92bb52f7ad2b9 100644 --- a/packages/js/src/executors/release-publish/schema.json +++ b/packages/js/src/executors/release-publish/schema.json @@ -16,6 +16,10 @@ "tag": { "type": "string", "description": "The distribution tag to apply to the published package." + }, + "dryRun": { + "type": "boolean", + "description": "Whether to run the command without actually publishing the package to the registry." } }, "required": [] diff --git a/packages/nx/src/command-line/release/publish.ts b/packages/nx/src/command-line/release/publish.ts index 0646c38e888b2..aa7431debdf73 100644 --- a/packages/nx/src/command-line/release/publish.ts +++ b/packages/nx/src/command-line/release/publish.ts @@ -1,9 +1,8 @@ -import { readNxJson } from '../../config/nx-json'; +import { NxJsonConfiguration, readNxJson } from '../../config/nx-json'; import { ProjectGraph, ProjectGraphProjectNode, } from '../../config/project-graph'; -import { NxJsonConfiguration, output } from '../../devkit-exports'; import { createProjectGraphAsync } from '../../project-graph/project-graph'; import { runCommand } from '../../tasks-runner/run-command'; import { @@ -11,6 +10,9 @@ import { readGraphFileFromGraphArg, } from '../../utils/command-line-utils'; import { findMatchingProjects } from '../../utils/find-matching-projects'; +import { logger } from '../../utils/logger'; +import { output } from '../../utils/output'; +import { generateGraph } from '../graph/graph'; import { PublishOptions } from './command-object'; import { createNxReleaseConfig } from './config/config'; import { @@ -19,7 +21,6 @@ import { createReleaseGroups, handleCreateReleaseGroupsError, } from './config/create-release-groups'; -import { generateGraph } from '../graph/graph'; export async function publishHandler( args: PublishOptions & { __overrides_unparsed__: string[] } @@ -151,6 +152,12 @@ export async function publishHandler( ); } + if (args.dryRun) { + logger.warn( + `\nNOTE: The "dryRun" flag means no projects were actually published.` + ); + } + process.exit(0); } @@ -172,6 +179,9 @@ async function runPublishOnProjects( if (args.tag) { overrides.tag = args.tag; } + if (args.dryRun) { + overrides.dryRun = args.dryRun; + } if (args.verbose) { process.env.NX_VERBOSE_LOGGING = 'true';