88
99import semver from 'semver' ;
1010
11- import { ChildProcess } from '../../utils/child-process.js' ;
11+ import { ChildProcess , SpawnResult , SpawnOptions } from '../../utils/child-process.js' ;
1212import { Spinner } from '../../utils/spinner.js' ;
1313import { NpmDistTag } from '../versioning/index.js' ;
1414
@@ -20,6 +20,7 @@ import {ReleasePrecheckJsonStdin} from '../precheck/cli.js';
2020import { BuiltPackageWithInfo } from '../config/index.js' ;
2121import { green , Log } from '../../utils/logging.js' ;
2222import { getBazelBin } from '../../utils/bazel-bin.js' ;
23+ import { PnpmVersioning } from './pnpm-versioning.js' ;
2324
2425/*
2526 * ###############################################################
@@ -51,29 +52,24 @@ export abstract class ExternalCommands {
5152 projectDir : string ,
5253 npmDistTag : NpmDistTag ,
5354 version : semver . SemVer ,
55+ pnpmVersioning : PnpmVersioning ,
5456 options : { skipExperimentalPackages : boolean } = { skipExperimentalPackages : false } ,
5557 ) {
56- // Note: We cannot use `yarn` directly as command because we might operate in
57- // a different publish branch and the current `PATH` will point to the Yarn version
58- // that invoked the release tool. More details in the function description.
59- const yarnCommand = await resolveYarnScriptForProject ( projectDir ) ;
60-
6158 try {
6259 // Note: No progress indicator needed as that is the responsibility of the command.
63- // TODO: detect yarn berry and handle flag differences properly.
64- await ChildProcess . spawn (
65- yarnCommand . binary ,
60+ await this . _spawnNpmScript (
6661 [
67- ...yarnCommand . args ,
6862 'ng-dev' ,
6963 'release' ,
7064 'set-dist-tag' ,
7165 npmDistTag ,
7266 version . format ( ) ,
7367 `--skip-experimental-packages=${ options . skipExperimentalPackages } ` ,
7468 ] ,
75- { cwd : projectDir } ,
69+ projectDir ,
70+ pnpmVersioning ,
7671 ) ;
72+
7773 Log . info ( green ( ` ✓ Set "${ npmDistTag } " NPM dist tag for all packages to v${ version } .` ) ) ;
7874 } catch ( e ) {
7975 Log . error ( e ) ;
@@ -86,20 +82,19 @@ export abstract class ExternalCommands {
8682 * Invokes the `ng-dev release npm-dist-tag delete` command in order to delete the
8783 * NPM dist tag for all packages in the checked-out version branch.
8884 */
89- static async invokeDeleteNpmDistTag ( projectDir : string , npmDistTag : NpmDistTag ) {
90- // Note: We cannot use `yarn` directly as command because we might operate in
91- // a different publish branch and the current `PATH` will point to the Yarn version
92- // that invoked the release tool. More details in the function description.
93- const yarnCommand = await resolveYarnScriptForProject ( projectDir ) ;
94-
85+ static async invokeDeleteNpmDistTag (
86+ projectDir : string ,
87+ npmDistTag : NpmDistTag ,
88+ pnpmVersioning : PnpmVersioning ,
89+ ) {
9590 try {
9691 // Note: No progress indicator needed as that is the responsibility of the command.
97- // TODO: detect yarn berry and handle flag differences properly.
98- await ChildProcess . spawn (
99- yarnCommand . binary ,
100- [ ...yarnCommand . args , 'ng-dev' , 'release' , 'npm-dist-tag' , 'delete' , npmDistTag ] ,
101- { cwd : projectDir } ,
92+ await this . _spawnNpmScript (
93+ [ 'ng-dev' , 'release' , 'npm-dist-tag' , 'delete' , npmDistTag ] ,
94+ projectDir ,
95+ pnpmVersioning ,
10296 ) ;
97+
10398 Log . info ( green ( ` ✓ Deleted "${ npmDistTag } " NPM dist tag for all packages.` ) ) ;
10499 } catch ( e ) {
105100 Log . error ( e ) ;
@@ -112,27 +107,24 @@ export abstract class ExternalCommands {
112107 * Invokes the `ng-dev release build` command in order to build the release
113108 * packages for the currently checked out branch.
114109 */
115- static async invokeReleaseBuild ( projectDir : string ) : Promise < ReleaseBuildJsonStdout > {
116- // Note: We cannot use `yarn` directly as command because we might operate in
117- // a different publish branch and the current `PATH` will point to the Yarn version
118- // that invoked the release tool. More details in the function description.
119- const yarnCommand = await resolveYarnScriptForProject ( projectDir ) ;
110+ static async invokeReleaseBuild (
111+ projectDir : string ,
112+ pnpmVersioning : PnpmVersioning ,
113+ ) : Promise < ReleaseBuildJsonStdout > {
120114 // Note: We explicitly mention that this can take a few minutes, so that it's obvious
121115 // to caretakers that it can take longer than just a few seconds.
122116 const spinner = new Spinner ( 'Building release output. This can take a few minutes.' ) ;
123117
124118 try {
125- // Since we expect JSON to be printed from the `ng-dev release build` command,
126- // we spawn the process in silent mode. We have set up an Ora progress spinner.
127- // TODO: detect yarn berry and handle flag differences properly.
128- const { stdout} = await ChildProcess . spawn (
129- yarnCommand . binary ,
130- [ ...yarnCommand . args , 'ng-dev' , 'release' , 'build' , '--json' ] ,
119+ const { stdout} = await this . _spawnNpmScript (
120+ [ 'ng-dev' , 'release' , 'build' , '--json' ] ,
121+ projectDir ,
122+ pnpmVersioning ,
131123 {
132- cwd : projectDir ,
133124 mode : 'silent' ,
134125 } ,
135126 ) ;
127+
136128 spinner . complete ( ) ;
137129 Log . info ( green ( ' ✓ Built release output for all packages.' ) ) ;
138130 // The `ng-dev release build` command prints a JSON array to stdout
@@ -153,23 +145,18 @@ export abstract class ExternalCommands {
153145 * This is useful to e.g. determine whether a built package is currently
154146 * denoted as experimental or not.
155147 */
156- static async invokeReleaseInfo ( projectDir : string ) : Promise < ReleaseInfoJsonStdout > {
157- // Note: We cannot use `yarn` directly as command because we might operate in
158- // a different publish branch and the current `PATH` will point to the Yarn version
159- // that invoked the release tool. More details in the function description.
160- const yarnCommand = await resolveYarnScriptForProject ( projectDir ) ;
161-
148+ static async invokeReleaseInfo (
149+ projectDir : string ,
150+ pnpmVersioning : PnpmVersioning ,
151+ ) : Promise < ReleaseInfoJsonStdout > {
162152 try {
163- // Note: No progress indicator needed as that is expected to be a fast operation.
164- // TODO: detect yarn berry and handle flag differences properly.
165- const { stdout} = await ChildProcess . spawn (
166- yarnCommand . binary ,
167- [ ...yarnCommand . args , 'ng-dev' , 'release' , 'info' , '--json' ] ,
168- {
169- cwd : projectDir ,
170- mode : 'silent' ,
171- } ,
153+ const { stdout} = await this . _spawnNpmScript (
154+ [ 'ng-dev' , 'release' , 'info' , '--json' ] ,
155+ projectDir ,
156+ pnpmVersioning ,
157+ { mode : 'silent' } ,
172158 ) ;
159+
173160 // The `ng-dev release info` command prints a JSON object to stdout.
174161 return JSON . parse ( stdout . trim ( ) ) as ReleaseInfoJsonStdout ;
175162 } catch ( e ) {
@@ -194,30 +181,20 @@ export abstract class ExternalCommands {
194181 projectDir : string ,
195182 newVersion : semver . SemVer ,
196183 builtPackagesWithInfo : BuiltPackageWithInfo [ ] ,
184+ pnpmVersioning : PnpmVersioning ,
197185 ) : Promise < void > {
198- // Note: We cannot use `yarn` directly as command because we might operate in
199- // a different publish branch and the current `PATH` will point to the Yarn version
200- // that invoked the release tool. More details in the function description.
201- const yarnCommand = await resolveYarnScriptForProject ( projectDir ) ;
202186 const precheckStdin : ReleasePrecheckJsonStdin = {
203187 builtPackagesWithInfo,
204188 newVersion : newVersion . format ( ) ,
205189 } ;
206190
207191 try {
208- // Note: No progress indicator needed as that is expected to be a fast operation. Also
209- // we expect the command to handle console messaging and wouldn't want to clobber it.
210- // TODO: detect yarn berry and handle flag differences properly.
211- await ChildProcess . spawn (
212- yarnCommand . binary ,
213- [ ...yarnCommand . args , 'ng-dev' , 'release' , 'precheck' ] ,
214- {
215- cwd : projectDir ,
216- // Note: We pass the precheck information to the command through `stdin`
217- // because command line arguments are less reliable and have length limits.
218- input : JSON . stringify ( precheckStdin ) ,
219- } ,
220- ) ;
192+ await this . _spawnNpmScript ( [ 'ng-dev' , 'release' , 'precheck' ] , projectDir , pnpmVersioning , {
193+ // Note: We pass the precheck information to the command through `stdin`
194+ // because command line arguments are less reliable and have length limits.
195+ input : JSON . stringify ( precheckStdin ) ,
196+ } ) ;
197+
221198 Log . info ( green ( ` ✓ Executed release pre-checks for ${ newVersion } ` ) ) ;
222199 } catch ( e ) {
223200 // The `spawn` invocation already prints all stdout/stderr, so we don't need re-print.
@@ -258,6 +235,28 @@ export abstract class ExternalCommands {
258235 }
259236 }
260237
238+ /**
239+ * Invokes the `pnpm install` command in order to install dependencies for
240+ * the configured project with the currently checked out revision.
241+ */
242+ static async invokePnpmInstall (
243+ projectDir : string ,
244+ pnpmVersioning : PnpmVersioning ,
245+ ) : Promise < void > {
246+ try {
247+ const pnpmSpec = await pnpmVersioning . getPackageSpec ( projectDir ) ;
248+ await ChildProcess . spawn ( 'npx' , [ '--yes' , pnpmSpec , 'install' , '--frozen-lockfile' ] , {
249+ cwd : projectDir ,
250+ } ) ;
251+
252+ Log . info ( green ( ' ✓ Installed project dependencies.' ) ) ;
253+ } catch ( e ) {
254+ Log . error ( e ) ;
255+ Log . error ( ' ✘ An error occurred while installing dependencies.' ) ;
256+ throw new FatalReleaseActionError ( ) ;
257+ }
258+ }
259+
261260 /**
262261 * Invokes the `yarn bazel sync --only=repo` command in order
263262 * to refresh Aspect lock files.
@@ -276,4 +275,28 @@ export abstract class ExternalCommands {
276275 }
277276 spinner . success ( green ( ' Updated Aspect `rules_js` lock files.' ) ) ;
278277 }
278+
279+ private static async _spawnNpmScript (
280+ args : string [ ] ,
281+ projectDir : string ,
282+ pnpmVersioning : PnpmVersioning ,
283+ spawnOptions : SpawnOptions = { } ,
284+ ) : Promise < SpawnResult > {
285+ if ( await pnpmVersioning . isUsingPnpm ( projectDir ) ) {
286+ const pnpmSpec = await pnpmVersioning . getPackageSpec ( projectDir ) ;
287+ return ChildProcess . spawn ( 'npx' , [ '--yes' , pnpmSpec , 'run' , ...args ] , {
288+ ...spawnOptions ,
289+ cwd : projectDir ,
290+ } ) ;
291+ } else {
292+ // Note: We cannot use `yarn` directly as command because we might operate in
293+ // a different publish branch and the current `PATH` will point to the Yarn version
294+ // that invoked the release tool. More details in the function description.
295+ const yarnCommand = await resolveYarnScriptForProject ( projectDir ) ;
296+ return ChildProcess . spawn ( yarnCommand . binary , [ ...yarnCommand . args , ...args ] , {
297+ ...spawnOptions ,
298+ cwd : projectDir ,
299+ } ) ;
300+ }
301+ }
279302}
0 commit comments