Skip to content
2 changes: 2 additions & 0 deletions ng-dev/release/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {ReleaseInfoCommandModule} from './info/cli.js';
import {ReleaseNotesCommandModule} from './notes/cli.js';
import {ReleasePrecheckCommandModule} from './precheck/cli.js';
import {ReleasePublishCommandModule} from './publish/cli.js';
import {ReleasePublishCiCommandModule} from './publish/cli-ci.js';
import {ReleasePublishSnapshotsCommandModule} from './snapshot-publish/cli.js';
import {BuildEnvStampCommand} from './stamping/cli.js';
import {ReleaseNpmDistTagCommand} from './npm-dist-tag/cli.js';
Expand All @@ -23,6 +24,7 @@ export function buildReleaseParser(localYargs: Argv) {
.strict()
.demandCommand()
.command(ReleasePublishCommandModule)
.command(ReleasePublishCiCommandModule)
.command(ReleaseBuildCommandModule)
.command(ReleaseInfoCommandModule)
.command(ReleaseNpmDistTagCommand)
Expand Down
9 changes: 9 additions & 0 deletions ng-dev/release/publish/actions-error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,17 @@
* found in the LICENSE file at https://angular.io/license
*/

import type {PullRequest} from './actions.js';

/** Error that will be thrown if the user manually aborted a release action. */
export class UserAbortedReleaseActionError extends Error {}

/** Error that will be thrown if the action has been aborted due to a fatal error. */
export class FatalReleaseActionError extends Error {}

/** Error that will be thrown if the stage-only phase is completed successfully. */
export class StageOnlySuccessError extends Error {
constructor(public pullRequest: PullRequest) {
super('Stage-only phase completed successfully.');
}
}
29 changes: 27 additions & 2 deletions ng-dev/release/publish/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@ import {ActiveReleaseTrains} from '../versioning/active-release-trains.js';
import {createExperimentalSemver} from '../versioning/experimental-versions.js';
import {NpmCommand} from '../versioning/npm-command.js';
import {getReleaseTagForVersion} from '../versioning/version-tags.js';
import {FatalReleaseActionError, UserAbortedReleaseActionError} from './actions-error.js';
import {
FatalReleaseActionError,
StageOnlySuccessError,
UserAbortedReleaseActionError,
} from './actions-error.js';
import {
analyzeAndExtendBuiltPackagesWithInfo,
assertIntegrityOfBuiltPackages,
Expand Down Expand Up @@ -78,7 +82,15 @@ export interface ReleaseActionConstructor<T extends ReleaseAction = ReleaseActio
/** Whether the release action is currently active. */
isActive(active: ActiveReleaseTrains, config: ReleaseConfig): Promise<boolean>;
/** Constructs a release action. */
new (...args: [ActiveReleaseTrains, AuthenticatedGitClient, ReleaseConfig, string]): T;
new (
...args: [
active: ActiveReleaseTrains,
git: AuthenticatedGitClient,
config: ReleaseConfig,
projectDir: string,
stageOnly?: boolean,
]
): T;
}

/**
Expand Down Expand Up @@ -106,6 +118,7 @@ export abstract class ReleaseAction {
protected git: AuthenticatedGitClient,
protected config: ReleaseConfig,
protected projectDir: string,
protected stageOnly = false,
) {}

/**
Expand Down Expand Up @@ -519,6 +532,18 @@ export abstract class ReleaseAction {

Log.info(green(' ✓ Release staging pull request has been created.'));

if (this.stageOnly) {
await Promise.all(
builtPackagesWithInfo.map(async (pkg) => {
if (existsSync(pkg.outputPath)) {
await fs.rm(pkg.outputPath, {recursive: true, force: true});
Log.info(`Cleaned up built package directory: ${pkg.outputPath}`);
}
}),
);
throw new StageOnlySuccessError(pullRequest);
}

return {releaseNotes, pullRequest, builtPackagesWithInfo};
}

Expand Down
73 changes: 73 additions & 0 deletions ng-dev/release/publish/cli-ci.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/**
* @license
* Copyright Google LLC
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/

import {Argv, Arguments, CommandModule} from 'yargs';

import {assertValidGithubConfig, getConfig} from '../../utils/config.js';
import {addGithubTokenOption} from '../../utils/git/github-yargs.js';
import {assertValidReleaseConfig} from '../config/index.js';
import {AuthenticatedGitClient} from '../../utils/git/authenticated-git-client.js';
import {PublishCiTool} from './index-ci.js';
import {green, Log} from '../../utils/logging.js';

export interface ReleasePublishCiOptions {
builtPackagesDir: string;
expectedSha: string;
dryRun?: boolean;
}

function builder(argv: Argv): Argv<ReleasePublishCiOptions> {
return addGithubTokenOption(argv)
.option('built-packages-dir' as 'builtPackagesDir', {
type: 'string',
demandOption: true,
description: 'Path to the directory containing pre-built packages.',
})
.option('expected-sha' as 'expectedSha', {
type: 'string',
demandOption: true,
description: 'The expected Git SHA of the release commit.',
})
.option('dry-run' as 'dryRun', {
type: 'boolean',
default: false,
description:
'Run the publish command in dry-run mode, skipping tag/release creation and NPM publishing.',
});
}

async function handler(flags: Arguments<ReleasePublishCiOptions>) {
const git = await AuthenticatedGitClient.get();
const config = await getConfig();
assertValidReleaseConfig(config);
assertValidGithubConfig(config);

const tool = new PublishCiTool(config, git, git.baseDir, flags);

try {
await tool.run();
Log.info(green('Release CI publish completed successfully.'));
} catch (e) {
if (e instanceof Error) {
Log.error(`Release CI publish failed: ${e.message}`);
if (e.stack) {
Log.debug(e.stack);
}
} else {
Log.error(`Release CI publish failed with unknown error: ${e}`);
}
process.exitCode = 1;
}
}

export const ReleasePublishCiCommandModule: CommandModule<{}, ReleasePublishCiOptions> = {
builder,
handler,
command: 'publish-ci',
describe: 'Publish a release from CI.',
};
13 changes: 10 additions & 3 deletions ng-dev/release/publish/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,16 @@ export interface ReleasePublishOptions extends ReleaseToolFlags {}

/** Yargs command builder for configuring the `ng-dev release publish` command. */
function builder(argv: Argv): Argv<ReleasePublishOptions> {
return addGithubTokenOption(argv).option('publishRegistry', {
type: 'string',
});
return addGithubTokenOption(argv)
.option('publishRegistry', {
type: 'string',
})
.option('stage-only', {
type: 'boolean',
default: false,
description:
'Only stage the release (bump version, generate changelog, build, precheck, create PR) and exit.',
});
}

/** Yargs command handler for staging a release. */
Expand Down
Loading
Loading