refactor(dev-infra): several code style and typo fixes (#39135)

This commit addresses comments from [my review][1] on PR #38656 (which
was merged without comments addressed). The changes are mostly related
to code style and typos.

[1]: https://github.com/angular/angular/pull/38656#pullrequestreview-482129333

PR Close #39135
This commit is contained in:
George Kalpakas 2020-10-10 22:02:47 +03:00 committed by atscott
parent 5471789664
commit 6947ceaf44
23 changed files with 73 additions and 82 deletions

View File

@ -26,7 +26,7 @@ export const release: ReleaseConfig = {
], ],
// TODO: Implement release package building here. // TODO: Implement release package building here.
buildPackages: async () => [], buildPackages: async () => [],
// TODO: This can be removed once there is a org-wide tool for changelog generation. // TODO: This can be removed once there is an org-wide tool for changelog generation.
generateReleaseNotesForHead: async () => { generateReleaseNotesForHead: async () => {
exec('yarn -s gulp changelog', {cwd: join(__dirname, '../')}); exec('yarn -s gulp changelog', {cwd: join(__dirname, '../')});
}, },

View File

@ -41,8 +41,8 @@ export async function getDefaultTargetLabelConfiguration(
// allow merging of PRs with `target: major`. // allow merging of PRs with `target: major`.
if (!next.isMajor) { if (!next.isMajor) {
throw new InvalidTargetLabelError( throw new InvalidTargetLabelError(
`Unable to merge pull request. The "${nextBranchName}" branch will be ` + `Unable to merge pull request. The "${nextBranchName}" branch will be released as ` +
`released as a minor version.`); 'a minor version.');
} }
return [nextBranchName]; return [nextBranchName];
}, },

View File

@ -30,8 +30,8 @@ export async function assertActiveLtsBranch(
const ltsNpmTag = getLtsNpmDistTagOfMajor(version.major); const ltsNpmTag = getLtsNpmDistTagOfMajor(version.major);
const ltsVersion = semver.parse(distTags[ltsNpmTag]); const ltsVersion = semver.parse(distTags[ltsNpmTag]);
// Ensure that there is a LTS version tagged for the given version-branch major. e.g. // Ensure that there is an LTS version tagged for the given version-branch major. e.g.
// if the version branch is `9.2.x` then we want to make sure that there is a LTS // if the version branch is `9.2.x` then we want to make sure that there is an LTS
// version tagged in NPM for `v9`, following the `v{major}-lts` tag convention. // version tagged in NPM for `v9`, following the `v{major}-lts` tag convention.
if (ltsVersion === null) { if (ltsVersion === null) {
throw new InvalidTargetBranchError(`No LTS version tagged for v${version.major} in NPM.`); throw new InvalidTargetBranchError(`No LTS version tagged for v${version.major} in NPM.`);

View File

@ -33,7 +33,7 @@ export async function mergePullRequest(
prNumber: number, githubToken: string, projectRoot: string = getRepoBaseDir(), prNumber: number, githubToken: string, projectRoot: string = getRepoBaseDir(),
config?: MergeConfigWithRemote) { config?: MergeConfigWithRemote) {
// Set the environment variable to skip all git commit hooks triggered by husky. We are unable to // Set the environment variable to skip all git commit hooks triggered by husky. We are unable to
// rely on `---no-verify` as some hooks still run, notably the `prepare-commit-msg` hook. // rely on `--no-verify` as some hooks still run, notably the `prepare-commit-msg` hook.
process.env['HUSKY_SKIP_HOOKS'] = '1'; process.env['HUSKY_SKIP_HOOKS'] = '1';
const api = await createPullRequestMergeTask(githubToken, projectRoot, config); const api = await createPullRequestMergeTask(githubToken, projectRoot, config);

View File

@ -60,7 +60,7 @@ describe('ng-dev release build', () => {
}); });
it('should error if package has not been built', async () => { it('should error if package has not been built', async () => {
// Set up a NPM package that is not built. // Set up an NPM package that is not built.
npmPackages.push('@angular/non-existent'); npmPackages.push('@angular/non-existent');
spyOn(console, 'error'); spyOn(console, 'error');

View File

@ -7,7 +7,7 @@
*/ */
import {promises as fs} from 'fs'; import {promises as fs} from 'fs';
import * as Ora from 'ora'; import * as ora from 'ora';
import {join} from 'path'; import {join} from 'path';
import * as semver from 'semver'; import * as semver from 'semver';
@ -44,7 +44,7 @@ export interface PullRequest {
forkBranch: string; forkBranch: string;
} }
/** Constructor type for a instantiating a release action */ /** Constructor type for instantiating a release action */
export interface ReleaseActionConstructor<T extends ReleaseAction = ReleaseAction> { export interface ReleaseActionConstructor<T extends ReleaseAction = ReleaseAction> {
/** Whether the release action is currently active. */ /** Whether the release action is currently active. */
isActive(active: ActiveReleaseTrains): Promise<boolean>; isActive(active: ActiveReleaseTrains): Promise<boolean>;
@ -107,25 +107,22 @@ export abstract class ReleaseAction {
if (state === 'failure') { if (state === 'failure') {
error( error(
red(` ✘ Cannot stage release. Commit "${commitSha}" does not pass all github ` + red(` ✘ Cannot stage release. Commit "${commitSha}" does not pass all github ` +
`status checks. Please make sure this commit passes all checks before re-running.`)); 'status checks. Please make sure this commit passes all checks before re-running.'));
error(` Please have a look at: ${branchCommitsUrl}`); error(` Please have a look at: ${branchCommitsUrl}`);
if (await promptConfirm('Do you want to ignore the Github status and proceed?')) { if (await promptConfirm('Do you want to ignore the Github status and proceed?')) {
info(yellow( info(yellow(
` ⚠ Upstream commit is failing CI checks, but status has been ` + ' ⚠ Upstream commit is failing CI checks, but status has been forcibly ignored.'));
`forcibly ignored.`));
return; return;
} }
throw new UserAbortedReleaseActionError(); throw new UserAbortedReleaseActionError();
} else if (state === 'pending') { } else if (state === 'pending') {
error( error(
red(` ✘ Commit "${commitSha}" still has pending github statuses that ` + red(` ✘ Commit "${commitSha}" still has pending github statuses that ` +
`need to succeed before staging a release.`)); 'need to succeed before staging a release.'));
error(red(` Please have a look at: ${branchCommitsUrl}`)); error(red(` Please have a look at: ${branchCommitsUrl}`));
if (await promptConfirm('Do you want to ignore the Github status and proceed?')) { if (await promptConfirm('Do you want to ignore the Github status and proceed?')) {
info(yellow( info(yellow(' ⚠ Upstream commit is pending CI, but status has been forcibly ignored.'));
` ⚠ Upstream commit is pending CI, but status has been ` +
`forcibly ignored.`));
return; return;
} }
throw new UserAbortedReleaseActionError(); throw new UserAbortedReleaseActionError();
@ -157,9 +154,9 @@ export abstract class ReleaseAction {
*/ */
protected async waitForEditsAndCreateReleaseCommit(newVersion: semver.SemVer) { protected async waitForEditsAndCreateReleaseCommit(newVersion: semver.SemVer) {
info(yellow( info(yellow(
` ⚠ Please review the changelog and ensure that the log contains only changes ` + ' ⚠ Please review the changelog and ensure that the log contains only changes ' +
`that apply to the public API surface. Manual changes can be made. When done, please ` + 'that apply to the public API surface. Manual changes can be made. When done, please ' +
`proceed with the prompt below.`)); 'proceed with the prompt below.'));
if (!await promptConfirm('Do you want to proceed and commit the changes?')) { if (!await promptConfirm('Do you want to proceed and commit the changes?')) {
throw new UserAbortedReleaseActionError(); throw new UserAbortedReleaseActionError();
@ -188,7 +185,7 @@ export abstract class ReleaseAction {
const forks = result.repository.forks.nodes; const forks = result.repository.forks.nodes;
if (forks.length === 0) { if (forks.length === 0) {
error(red(` ✘ Unable to find fork for currently authenticated user.`)); error(red(' ✘ Unable to find fork for currently authenticated user.'));
error(red(` Please ensure you created a fork of: ${owner}/${name}.`)); error(red(` Please ensure you created a fork of: ${owner}/${name}.`));
throw new FatalReleaseActionError(); throw new FatalReleaseActionError();
} }
@ -304,7 +301,7 @@ export abstract class ReleaseAction {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
debug(`Waiting for pull request #${id} to be merged.`); debug(`Waiting for pull request #${id} to be merged.`);
const spinner = Ora().start(`Waiting for pull request #${id} to be merged.`); const spinner = ora().start(`Waiting for pull request #${id} to be merged.`);
const intervalId = setInterval(async () => { const intervalId = setInterval(async () => {
const prState = await getPullRequestState(this.git, id); const prState = await getPullRequestState(this.git, id);
if (prState === 'merged') { if (prState === 'merged') {
@ -451,7 +448,7 @@ export abstract class ReleaseAction {
info(green( info(green(
` ✓ Pull request for cherry-picking the changelog into "${nextBranch}" ` + ` ✓ Pull request for cherry-picking the changelog into "${nextBranch}" ` +
`has been created.`)); 'has been created.'));
info(yellow(` Please ask team members to review: ${url}.`)); info(yellow(` Please ask team members to review: ${url}.`));
return true; return true;
} }
@ -490,7 +487,7 @@ export abstract class ReleaseAction {
if (!await this._isCommitForVersionStaging(newVersion, versionBumpCommitSha)) { if (!await this._isCommitForVersionStaging(newVersion, versionBumpCommitSha)) {
error(red(` ✘ Latest commit in "${publishBranch}" branch is not a staging commit.`)); error(red(` ✘ Latest commit in "${publishBranch}" branch is not a staging commit.`));
error(red(` Please make sure the staging pull request has been merged.`)); error(red(' Please make sure the staging pull request has been merged.'));
throw new FatalReleaseActionError(); throw new FatalReleaseActionError();
} }
@ -514,13 +511,13 @@ export abstract class ReleaseAction {
await this._publishBuiltPackageToNpm(builtPackage, npmDistTag); await this._publishBuiltPackageToNpm(builtPackage, npmDistTag);
} }
info(green(` ✓ Published all packages successfully`)); info(green(' ✓ Published all packages successfully'));
} }
/** Publishes the given built package to NPM with the specified NPM dist tag. */ /** Publishes the given built package to NPM with the specified NPM dist tag. */
private async _publishBuiltPackageToNpm(pkg: BuiltPackage, npmDistTag: string) { private async _publishBuiltPackageToNpm(pkg: BuiltPackage, npmDistTag: string) {
debug(`Starting publish of "${pkg.name}".`); debug(`Starting publish of "${pkg.name}".`);
const spinner = Ora().start(`Publishing "${pkg.name}"`); const spinner = ora().start(`Publishing "${pkg.name}"`);
try { try {
await runNpmPublish(pkg.outputPath, npmDistTag, this.config.publishRegistry); await runNpmPublish(pkg.outputPath, npmDistTag, this.config.publishRegistry);

View File

@ -65,14 +65,14 @@ export class CutLongTermSupportPatchAction extends ReleaseAction {
{ {
name: 'activeLtsBranch', name: 'activeLtsBranch',
type: 'list', type: 'list',
message: 'Please select a version for which you want to cut a LTS patch', message: 'Please select a version for which you want to cut an LTS patch',
choices: activeBranchChoices, choices: activeBranchChoices,
}, },
{ {
name: 'inactiveLtsBranch', name: 'inactiveLtsBranch',
type: 'list', type: 'list',
when: o => o.activeLtsBranch === null, when: o => o.activeLtsBranch === null,
message: 'Please select an inactive LTS version for which you want to cut a LTS patch', message: 'Please select an inactive LTS version for which you want to cut an LTS patch',
choices: inactive.map(branch => this._getChoiceForLtsBranch(branch)), choices: inactive.map(branch => this._getChoiceForLtsBranch(branch)),
} }
]); ]);

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import * as Ora from 'ora'; import * as ora from 'ora';
import * as semver from 'semver'; import * as semver from 'semver';
import {spawnWithDebugOutput} from '../../utils/child-process'; import {spawnWithDebugOutput} from '../../utils/child-process';
@ -19,11 +19,11 @@ import {FatalReleaseActionError} from './actions-error';
* ############################################################### * ###############################################################
* *
* This file contains helpers for invoking external `ng-dev` commands. A subset of actions, * This file contains helpers for invoking external `ng-dev` commands. A subset of actions,
* like building release output or setting a NPM dist tag for release packages, cannot be * like building release output or setting aν NPM dist tag for release packages, cannot be
* performed directly as part of the release tool and need to be delegated to external `ng-dev` * performed directly as part of the release tool and need to be delegated to external `ng-dev`
* commands that exist across arbitrary version branches. * commands that exist across arbitrary version branches.
* *
* In an concrete example: Consider a new patch version is released and that a new release * In a concrete example: Consider a new patch version is released and that a new release
* package has been added to the `next` branch. The patch branch will not contain the new * package has been added to the `next` branch. The patch branch will not contain the new
* release package, so we could not build the release output for it. To work around this, we * release package, so we could not build the release output for it. To work around this, we
* call the ng-dev build command for the patch version branch and expect it to return a list * call the ng-dev build command for the patch version branch and expect it to return a list
@ -44,7 +44,7 @@ export async function invokeSetNpmDistCommand(npmDistTag: string, version: semve
info(green(` ✓ Set "${npmDistTag}" NPM dist tag for all packages to v${version}.`)); info(green(` ✓ Set "${npmDistTag}" NPM dist tag for all packages to v${version}.`));
} catch (e) { } catch (e) {
error(e); error(e);
error(red(` ✘ An error occurred while setting the NPM dist tag for ${npmDistTag}.`)); error(red(' ✘ An error occurred while setting the NPM dist tag for "${npmDistTag}".'));
throw new FatalReleaseActionError(); throw new FatalReleaseActionError();
} }
} }
@ -54,21 +54,21 @@ export async function invokeSetNpmDistCommand(npmDistTag: string, version: semve
* packages for the currently checked out branch. * packages for the currently checked out branch.
*/ */
export async function invokeReleaseBuildCommand(): Promise<BuiltPackage[]> { export async function invokeReleaseBuildCommand(): Promise<BuiltPackage[]> {
const spinner = Ora().start('Building release output.'); const spinner = ora().start('Building release output.');
try { try {
// Since we expect JSON to be printed from the `ng-dev release build` command, // Since we expect JSON to be printed from the `ng-dev release build` command,
// we spawn the process in silent mode. We have set up an Ora progress spinner. // we spawn the process in silent mode. We have set up an Ora progress spinner.
const {stdout} = await spawnWithDebugOutput( const {stdout} = await spawnWithDebugOutput(
'yarn', ['--silent', 'ng-dev', 'release', 'build', '--json'], {mode: 'silent'}); 'yarn', ['--silent', 'ng-dev', 'release', 'build', '--json'], {mode: 'silent'});
spinner.stop(); spinner.stop();
info(green(` ✓ Built release output for all packages.`)); info(green(' ✓ Built release output for all packages.'));
// The `ng-dev release build` command prints a JSON array to stdout // The `ng-dev release build` command prints a JSON array to stdout
// that represents the built release packages and their output paths. // that represents the built release packages and their output paths.
return JSON.parse(stdout.trim()); return JSON.parse(stdout.trim());
} catch (e) { } catch (e) {
spinner.stop(); spinner.stop();
error(e); error(e);
error(red(` ✘ An error occurred while building the release packages.`)); error(red(' ✘ An error occurred while building the release packages.'));
throw new FatalReleaseActionError(); throw new FatalReleaseActionError();
} }
} }
@ -83,10 +83,10 @@ export async function invokeYarnInstallCommand(projectDir: string): Promise<void
// TODO: Consider using an Ora spinner instead to ensure minimal console output. // TODO: Consider using an Ora spinner instead to ensure minimal console output.
await spawnWithDebugOutput( await spawnWithDebugOutput(
'yarn', ['install', '--frozen-lockfile', '--non-interactive'], {cwd: projectDir}); 'yarn', ['install', '--frozen-lockfile', '--non-interactive'], {cwd: projectDir});
info(green(` ✓ Installed project dependencies.`)); info(green(' ✓ Installed project dependencies.'));
} catch (e) { } catch (e) {
error(e); error(e);
error(red(` ✘ An error occurred while installing dependencies.`)); error(red(' ✘ An error occurred while installing dependencies.'));
throw new FatalReleaseActionError(); throw new FatalReleaseActionError();
} }
} }

View File

@ -66,8 +66,7 @@ export class ReleaseTool {
// Only print the error message and stack if the error is not a known fatal release // Only print the error message and stack if the error is not a known fatal release
// action error (for which we print the error gracefully to the console with colors). // action error (for which we print the error gracefully to the console with colors).
if (!(e instanceof FatalReleaseActionError) && e instanceof Error) { if (!(e instanceof FatalReleaseActionError) && e instanceof Error) {
console.error(e.message); console.error(e);
console.error(e.stack);
} }
return CompletionState.FATAL_ERROR; return CompletionState.FATAL_ERROR;
} finally { } finally {
@ -90,7 +89,7 @@ export class ReleaseTool {
} }
} }
info(`Please select the type of release you want to perform.`); info('Please select the type of release you want to perform.');
const {releaseAction} = await prompt<{releaseAction: ReleaseAction}>({ const {releaseAction} = await prompt<{releaseAction: ReleaseAction}>({
name: 'releaseAction', name: 'releaseAction',
@ -108,9 +107,7 @@ export class ReleaseTool {
*/ */
private async _verifyNoUncommittedChanges(): Promise<boolean> { private async _verifyNoUncommittedChanges(): Promise<boolean> {
if (this._git.hasUncommittedChanges()) { if (this._git.hasUncommittedChanges()) {
error( error(red(' ✘ There are changes which are not committed and should be discarded.'));
red(` ✘ There are changes which are not committed and should be ` +
`discarded.`));
return false; return false;
} }
return true; return true;
@ -126,7 +123,7 @@ export class ReleaseTool {
await this._git.github.repos.getBranch({...this._git.remoteParams, branch: nextBranchName}); await this._git.github.repos.getBranch({...this._git.remoteParams, branch: nextBranchName});
if (headSha !== data.commit.sha) { if (headSha !== data.commit.sha) {
error(red(` ✘ Running release tool from an outdated local branch.`)); error(red(' ✘ Running release tool from an outdated local branch.'));
error(red(` Please make sure you are running from the "${nextBranchName}" branch.`)); error(red(` Please make sure you are running from the "${nextBranchName}" branch.`));
return false; return false;
} }

View File

@ -13,7 +13,7 @@ import {CutLongTermSupportPatchAction} from '../actions/cut-lts-patch';
import {expectStagingAndPublishWithCherryPick, fakeNpmPackageQueryRequest, getTestingMocksForReleaseAction, parse, setupReleaseActionForTesting, testTmpDir} from './test-utils'; import {expectStagingAndPublishWithCherryPick, fakeNpmPackageQueryRequest, getTestingMocksForReleaseAction, parse, setupReleaseActionForTesting, testTmpDir} from './test-utils';
describe('cut a LTS patch action', () => { describe('cut an LTS patch action', () => {
it('should be active', async () => { it('should be active', async () => {
expect(await CutLongTermSupportPatchAction.isActive({ expect(await CutLongTermSupportPatchAction.isActive({
releaseCandidate: null, releaseCandidate: null,

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import * as Ora from 'ora'; import * as ora from 'ora';
import * as semver from 'semver'; import * as semver from 'semver';
import {Arguments, Argv, CommandModule} from 'yargs'; import {Arguments, Argv, CommandModule} from 'yargs';
@ -15,7 +15,7 @@ import {getReleaseConfig} from '../config/index';
import {setNpmTagForPackage} from '../versioning/npm-publish'; import {setNpmTagForPackage} from '../versioning/npm-publish';
/** Command line options for setting a NPM dist tag. */ /** Command line options for setting an NPM dist tag. */
export interface ReleaseSetDistTagOptions { export interface ReleaseSetDistTagOptions {
tagName: string; tagName: string;
targetVersion: string; targetVersion: string;
@ -42,11 +42,11 @@ async function handler(args: Arguments<ReleaseSetDistTagOptions>) {
const version = semver.parse(rawVersion); const version = semver.parse(rawVersion);
if (version === null) { if (version === null) {
error(red(`Invalid version specified. Unable to set NPM dist tag.`)); error(red(`Invalid version specified (${rawVersion}). Unable to set NPM dist tag.`));
process.exit(1); process.exit(1);
} }
const spinner = Ora().start(); const spinner = ora().start();
debug(`Setting "${tagName}" NPM dist tag for release packages to v${version}.`); debug(`Setting "${tagName}" NPM dist tag for release packages to v${version}.`);
for (const pkgName of npmPackages) { for (const pkgName of npmPackages) {
@ -69,7 +69,7 @@ async function handler(args: Arguments<ReleaseSetDistTagOptions>) {
info(green(` ${bold(tagName)} will now point to ${bold(`v${version}`)}.`)); info(green(` ${bold(tagName)} will now point to ${bold(`v${version}`)}.`));
} }
/** CLI command module for setting a NPM dist tag. */ /** CLI command module for setting an NPM dist tag. */
export const ReleaseSetDistTagCommand: CommandModule<{}, ReleaseSetDistTagOptions> = { export const ReleaseSetDistTagCommand: CommandModule<{}, ReleaseSetDistTagOptions> = {
builder, builder,
handler, handler,

View File

@ -66,7 +66,7 @@ describe('ng-dev release set-dist-tag', () => {
await invokeCommand('latest', '10.0'); await invokeCommand('latest', '10.0');
expect(console.error) expect(console.error)
.toHaveBeenCalledWith('Invalid version specified. Unable to set NPM dist tag.'); .toHaveBeenCalledWith('Invalid version specified (10.0). Unable to set NPM dist tag.');
expect(process.exit).toHaveBeenCalledWith(1); expect(process.exit).toHaveBeenCalledWith(1);
expect(process.exit).toHaveBeenCalledTimes(1); expect(process.exit).toHaveBeenCalledTimes(1);
}); });

View File

@ -1,5 +1,5 @@
## Versioning for the Angular organization ## Versioning for the Angular organization
The folder contains common tooling needed for implementing the versioning as proposed The folder contains common tooling needed for implementing the versioning as proposed
by [this design document](https://docs.google.com/document/d/197kVillDwx-RZtSVOBtPb4BBIAw0E9RT3q3v6DZkykU/edit#heading=h.s3qlps8f4zq7). by [this design document](https://docs.google.com/document/d/197kVillDwx-RZtSVOBtPb4BBIAw0E9RT3q3v6DZkykU).
Primary tooling is the determination of _active_ release trains. Primary tooling is the determination of _active_ release trains.

View File

@ -17,7 +17,7 @@ export interface ActiveReleaseTrains {
releaseCandidate: ReleaseTrain|null; releaseCandidate: ReleaseTrain|null;
/** Release-train currently in the "latest" phase. */ /** Release-train currently in the "latest" phase. */
latest: ReleaseTrain; latest: ReleaseTrain;
/** Release-train in the `next` phase */ /** Release-train in the `next` phase. */
next: ReleaseTrain; next: ReleaseTrain;
} }

View File

@ -34,16 +34,16 @@ export interface LtsBranch {
* Number of months a major version in Angular is actively supported. See: * Number of months a major version in Angular is actively supported. See:
* https://angular.io/guide/releases#support-policy-and-schedule. * https://angular.io/guide/releases#support-policy-and-schedule.
*/ */
export const majorActiveSupportDuration = 6; const majorActiveSupportDuration = 6;
/** /**
* Number of months a major version has active long-term support. See: * Number of months a major version has active long-term support. See:
* https://angular.io/guide/releases#support-policy-and-schedule. * https://angular.io/guide/releases#support-policy-and-schedule.
*/ */
export const majorActiveTermSupportDuration = 12; const majorLongTermSupportDuration = 12;
/** Regular expression that matches LTS NPM dist tags. */ /** Regular expression that matches LTS NPM dist tags. */
export const ltsNpmDistTagRegex = /^v(\d+)-lts$/; const ltsNpmDistTagRegex = /^v(\d+)-lts$/;
/** Finds all long-term support release trains from the specified NPM package. */ /** Finds all long-term support release trains from the specified NPM package. */
export async function fetchLongTermSupportBranchesFromNpm(config: ReleaseConfig): export async function fetchLongTermSupportBranchesFromNpm(config: ReleaseConfig):
@ -54,7 +54,7 @@ export async function fetchLongTermSupportBranchesFromNpm(config: ReleaseConfig)
const inactive: LtsBranch[] = []; const inactive: LtsBranch[] = [];
// Iterate through the NPM package information and determine active/inactive LTS versions with // Iterate through the NPM package information and determine active/inactive LTS versions with
// their corresponding branches. We assume that a LTS tagged version in NPM belongs to the // their corresponding branches. We assume that an LTS tagged version in NPM belongs to the
// last-minor branch of a given major (i.e. we assume there are no outdated LTS NPM dist tags). // last-minor branch of a given major (i.e. we assume there are no outdated LTS NPM dist tags).
for (const npmDistTag in distTags) { for (const npmDistTag in distTags) {
if (ltsNpmDistTagRegex.test(npmDistTag)) { if (ltsNpmDistTagRegex.test(npmDistTag)) {
@ -64,7 +64,7 @@ export async function fetchLongTermSupportBranchesFromNpm(config: ReleaseConfig)
const ltsEndDate = computeLtsEndDateOfMajor(majorReleaseDate); const ltsEndDate = computeLtsEndDateOfMajor(majorReleaseDate);
const ltsBranch: LtsBranch = {name: branchName, version, npmDistTag}; const ltsBranch: LtsBranch = {name: branchName, version, npmDistTag};
// Depending on whether the LTS phase is still active, add the branch // Depending on whether the LTS phase is still active, add the branch
// the list of active or inactive LTS branches. // to the list of active or inactive LTS branches.
if (today <= ltsEndDate) { if (today <= ltsEndDate) {
active.push(ltsBranch); active.push(ltsBranch);
} else { } else {
@ -87,7 +87,7 @@ export async function fetchLongTermSupportBranchesFromNpm(config: ReleaseConfig)
export function computeLtsEndDateOfMajor(majorReleaseDate: Date): Date { export function computeLtsEndDateOfMajor(majorReleaseDate: Date): Date {
return new Date( return new Date(
majorReleaseDate.getFullYear(), majorReleaseDate.getFullYear(),
majorReleaseDate.getMonth() + majorActiveSupportDuration + majorActiveTermSupportDuration, majorReleaseDate.getMonth() + majorActiveSupportDuration + majorLongTermSupportDuration,
majorReleaseDate.getDate(), majorReleaseDate.getHours(), majorReleaseDate.getMinutes(), majorReleaseDate.getDate(), majorReleaseDate.getHours(), majorReleaseDate.getMinutes(),
majorReleaseDate.getSeconds(), majorReleaseDate.getMilliseconds()); majorReleaseDate.getSeconds(), majorReleaseDate.getMilliseconds());
} }

View File

@ -57,10 +57,9 @@ function getRepresentativeNpmPackage(config: ReleaseConfig) {
/** Fetches the specified NPM package from the NPM registry. */ /** Fetches the specified NPM package from the NPM registry. */
async function fetchPackageInfoFromNpmRegistry(pkgName: string): Promise<NpmPackageInfo> { async function fetchPackageInfoFromNpmRegistry(pkgName: string): Promise<NpmPackageInfo> {
if (_npmPackageInfoCache[pkgName] !== undefined) { if (_npmPackageInfoCache[pkgName] === undefined) {
return await _npmPackageInfoCache[pkgName]; _npmPackageInfoCache[pkgName] =
fetch(`https://registry.npmjs.org/${pkgName}`).then(r => r.json());
} }
const result = _npmPackageInfoCache[pkgName] = return await _npmPackageInfoCache[pkgName];
fetch(`https://registry.npmjs.org/${pkgName}`).then(r => r.json());
return await result;
} }

View File

@ -59,14 +59,14 @@ export async function printActiveReleaseTrains(
`published yet.`); `published yet.`);
} }
// If no release-train in release-candidate of feature-freeze phase is active, // If no release-train in release-candidate or feature-freeze phase is active,
// we print a message as last bullet point to make this clear. // we print a message as last bullet point to make this clear.
if (releaseCandidate === null) { if (releaseCandidate === null) {
info(` • No release-candidate or feature-freeze branch currently active.`); info(' • No release-candidate or feature-freeze branch currently active.');
} }
info(); info();
info(blue(`Current active LTS version branches:`)); info(blue('Current active LTS version branches:'));
// Print all active LTS branches (each branch as own bullet point). // Print all active LTS branches (each branch as own bullet point).
if (ltsBranches.active.length !== 0) { if (ltsBranches.active.length !== 0) {

View File

@ -28,7 +28,7 @@ export interface VersionBranch {
} }
/** Regular expression that matches version-branches. */ /** Regular expression that matches version-branches. */
const versionBranchNameRegex = /(\d+)\.(\d+)\.x/; const versionBranchNameRegex = /^(\d+)\.(\d+)\.x$/;
/** Gets the version of a given branch by reading the `package.json` upstream. */ /** Gets the version of a given branch by reading the `package.json` upstream. */
export async function getVersionOfBranch( export async function getVersionOfBranch(
@ -56,8 +56,6 @@ export function isVersionBranch(branchName: string): boolean {
* relevant but needed for parsing. SemVer does not allow `x` as patch digit. * relevant but needed for parsing. SemVer does not allow `x` as patch digit.
*/ */
export function getVersionForVersionBranch(branchName: string): semver.SemVer|null { export function getVersionForVersionBranch(branchName: string): semver.SemVer|null {
// Convert a given version-branch into a SemVer version that can be used
// with the SemVer utilities. i.e. to determine semantic order.
return semver.parse(branchName.replace(versionBranchNameRegex, '$1.$2.0')); return semver.parse(branchName.replace(versionBranchNameRegex, '$1.$2.0'));
} }

View File

@ -48,7 +48,7 @@ export function spawnWithDebugOutput(
childProcess.stderr.on('data', message => { childProcess.stderr.on('data', message => {
logOutput += message; logOutput += message;
// If console output is enabled, print the message directly to the stderr. Note that // If console output is enabled, print the message directly to the stderr. Note that
// we intentionally print all output to stderr as stderr should not be polluted. // we intentionally print all output to stderr as stdout should not be polluted.
if (outputMode === undefined || outputMode === 'enabled') { if (outputMode === undefined || outputMode === 'enabled') {
process.stderr.write(message); process.stderr.write(message);
} }
@ -57,7 +57,7 @@ export function spawnWithDebugOutput(
stdout += message; stdout += message;
logOutput += message; logOutput += message;
// If console output is enabled, print the message directly to the stderr. Note that // If console output is enabled, print the message directly to the stderr. Note that
// we intentionally print all output to stderr as stderr should not be polluted. // we intentionally print all output to stderr as stdout should not be polluted.
if (outputMode === undefined || outputMode === 'enabled') { if (outputMode === undefined || outputMode === 'enabled') {
process.stderr.write(message); process.stderr.write(message);
} }
@ -67,7 +67,7 @@ export function spawnWithDebugOutput(
const exitDescription = status !== null ? `exit code "${status}"` : `signal "${signal}"`; const exitDescription = status !== null ? `exit code "${status}"` : `signal "${signal}"`;
const printFn = outputMode === 'on-error' ? error : debug; const printFn = outputMode === 'on-error' ? error : debug;
printFn(`Command ${commandText} completed with ${exitDescription}.`); printFn(`Command "${commandText}" completed with ${exitDescription}.`);
printFn(`Process output: \n${logOutput}`); printFn(`Process output: \n${logOutput}`);
// On success, resolve the promise. Otherwise reject with the captured stderr // On success, resolve the promise. Otherwise reject with the captured stderr

View File

@ -12,16 +12,16 @@ import {GithubConfig} from '../config';
import {GitClient} from './index'; import {GitClient} from './index';
/** URL to the Github page where personal access tokens can be managed. */ /** URL to the Github page where personal access tokens can be managed. */
export const GITHUB_TOKEN_SETTINGS_URL = `https://github.com/settings/tokens`; export const GITHUB_TOKEN_SETTINGS_URL = 'https://github.com/settings/tokens';
/** URL to the Github page where personal access tokens can be generated. */ /** URL to the Github page where personal access tokens can be generated. */
export const GITHUB_TOKEN_GENERATE_URL = `https://github.com/settings/tokens/new`; export const GITHUB_TOKEN_GENERATE_URL = 'https://github.com/settings/tokens/new';
/** Adds the provided token to the given Github HTTPs remote url. */ /** Adds the provided token to the given Github HTTPs remote url. */
export function addTokenToGitHttpsUrl(githubHttpsUrl: string, token: string) { export function addTokenToGitHttpsUrl(githubHttpsUrl: string, token: string) {
const url = new URL(githubHttpsUrl); const url = new URL(githubHttpsUrl);
url.username = token; url.username = token;
return url.toString(); return url.href;
} }
/** Gets the repository Git URL for the given github config. */ /** Gets the repository Git URL for the given github config. */
@ -36,7 +36,7 @@ export function getRepositoryGitUrl(config: GithubConfig, githubToken?: string):
return baseHttpUrl; return baseHttpUrl;
} }
/** Gets a Github URL that refers to a lists of recent commits within a specified branch. */ /** Gets a Github URL that refers to a list of recent commits within a specified branch. */
export function getListCommitsInBranchUrl({remoteParams}: GitClient, branchName: string) { export function getListCommitsInBranchUrl({remoteParams}: GitClient, branchName: string) {
return `https://github.com/${remoteParams.owner}/${remoteParams.repo}/commits/${branchName}`; return `https://github.com/${remoteParams.owner}/${remoteParams.repo}/commits/${branchName}`;
} }

View File

@ -88,8 +88,8 @@ export class GitClient {
*/ */
runGraceful(args: string[], options: SpawnSyncOptions = {}): SpawnSyncReturns<string> { runGraceful(args: string[], options: SpawnSyncOptions = {}): SpawnSyncReturns<string> {
// To improve the debugging experience in case something fails, we print all executed Git // To improve the debugging experience in case something fails, we print all executed Git
// commands unless the `stdio` is explicitly to `ignore` (which is equivalent to silent). // commands unless the `stdio` is explicitly set to `ignore` (which is equivalent to silent).
// Note that we do not want to print the token if is contained in the command. It's common // Note that we do not want to print the token if it is contained in the command. It's common
// to share errors with others if the tool failed, and we do not want to leak tokens. // to share errors with others if the tool failed, and we do not want to leak tokens.
// TODO: Add support for configuring this on a per-client basis. Some tools do not want // TODO: Add support for configuring this on a per-client basis. Some tools do not want
// to print the Git command messages to the console at all (e.g. to maintain clean output). // to print the Git command messages to the console at all (e.g. to maintain clean output).
@ -202,7 +202,7 @@ export class GitClient {
/** /**
* Retrieve the OAuth scopes for the loaded Github token. * Retrieve the OAuth scopes for the loaded Github token.
**/ **/
private async getAuthScopesForToken() { private getAuthScopesForToken() {
// If the OAuth scopes have already been loaded, return the Promise containing them. // If the OAuth scopes have already been loaded, return the Promise containing them.
if (this._cachedOauthScopes !== null) { if (this._cachedOauthScopes !== null) {
return this._cachedOauthScopes; return this._cachedOauthScopes;

View File

@ -6,6 +6,6 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
export * from './semver-matchers';
export * from './virtual-git-client'; export * from './virtual-git-client';
export * from './virtual-git-matchers'; export * from './virtual-git-matchers';
export * from './semver-matchers';

View File

@ -123,7 +123,7 @@ export class VirtualGitClient extends GitClient {
this.branches[ref.destination] = { this.branches[ref.destination] = {
branch: ref.destination, branch: ref.destination,
ref: this.fetchHeadRef, ref: this.fetchHeadRef,
newCommits: [] newCommits: [],
}; };
} }
} }
@ -141,7 +141,7 @@ export class VirtualGitClient extends GitClient {
} }
this.head = {ref: this.fetchHeadRef, newCommits: []}; this.head = {ref: this.fetchHeadRef, newCommits: []};
} else if (this.branches[target]) { } else if (this.branches[target]) {
this.head = {...this._cloneHead(this.branches[target], detached)}; this.head = this._cloneHead(this.branches[target], detached);
} else if (createBranch) { } else if (createBranch) {
this.head = this.branches[target] = {branch: target, ...this._cloneHead(this.head, detached)}; this.head = this.branches[target] = {branch: target, ...this._cloneHead(this.head, detached)};
} else { } else {