2020-12-07 13:52:40 -05:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
var tslib = require('tslib');
|
|
|
|
var fs = require('fs');
|
|
|
|
var path = require('path');
|
2021-06-03 09:59:20 -04:00
|
|
|
require('chalk');
|
2020-12-07 13:52:40 -05:00
|
|
|
require('inquirer');
|
2021-04-12 17:50:50 -04:00
|
|
|
var child_process = require('child_process');
|
|
|
|
var semver = require('semver');
|
|
|
|
var graphql = require('@octokit/graphql');
|
2021-05-27 15:14:36 -04:00
|
|
|
var rest = require('@octokit/rest');
|
2021-04-12 17:50:50 -04:00
|
|
|
var typedGraphqlify = require('typed-graphqlify');
|
|
|
|
var url = require('url');
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @license
|
|
|
|
* Copyright Google LLC All Rights Reserved.
|
|
|
|
*
|
|
|
|
* 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
|
|
|
|
*/
|
|
|
|
/** Whether the current environment is in dry run mode. */
|
|
|
|
function isDryRun() {
|
|
|
|
return process.env['DRY_RUN'] !== undefined;
|
|
|
|
}
|
|
|
|
/** Error to be thrown when a function or method is called in dryRun mode and shouldn't be. */
|
|
|
|
var DryRunError = /** @class */ (function (_super) {
|
|
|
|
tslib.__extends(DryRunError, _super);
|
|
|
|
function DryRunError() {
|
|
|
|
var _this = _super.call(this, 'Cannot call this function in dryRun mode.') || this;
|
|
|
|
// Set the prototype explicitly because in ES5, the prototype is accidentally lost due to
|
|
|
|
// a limitation in down-leveling.
|
|
|
|
// https://github.com/Microsoft/TypeScript/wiki/FAQ#why-doesnt-extending-built-ins-like-error-array-and-map-work.
|
|
|
|
Object.setPrototypeOf(_this, DryRunError.prototype);
|
|
|
|
return _this;
|
|
|
|
}
|
|
|
|
return DryRunError;
|
|
|
|
}(Error));
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @license
|
|
|
|
* Copyright Google LLC All Rights Reserved.
|
|
|
|
*
|
|
|
|
* 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
|
|
|
|
*/
|
|
|
|
/** Error for failed Github API requests. */
|
|
|
|
var GithubApiRequestError = /** @class */ (function (_super) {
|
|
|
|
tslib.__extends(GithubApiRequestError, _super);
|
|
|
|
function GithubApiRequestError(status, message) {
|
|
|
|
var _this = _super.call(this, message) || this;
|
|
|
|
_this.status = status;
|
|
|
|
return _this;
|
|
|
|
}
|
|
|
|
return GithubApiRequestError;
|
|
|
|
}(Error));
|
2021-06-03 09:59:20 -04:00
|
|
|
/** A Github client for interacting with the Github APIs. */
|
2021-05-27 15:14:36 -04:00
|
|
|
var GithubClient = /** @class */ (function () {
|
2021-06-03 09:59:20 -04:00
|
|
|
function GithubClient(_octokitOptions) {
|
|
|
|
this._octokitOptions = _octokitOptions;
|
|
|
|
/** The octokit instance actually performing API requests. */
|
|
|
|
this._octokit = new rest.Octokit(this._octokitOptions);
|
2021-05-27 15:14:36 -04:00
|
|
|
this.pulls = this._octokit.pulls;
|
|
|
|
this.repos = this._octokit.repos;
|
|
|
|
this.issues = this._octokit.issues;
|
|
|
|
this.git = this._octokit.git;
|
|
|
|
this.paginate = this._octokit.paginate;
|
|
|
|
this.rateLimit = this._octokit.rateLimit;
|
2021-06-03 09:59:20 -04:00
|
|
|
}
|
|
|
|
return GithubClient;
|
|
|
|
}());
|
|
|
|
/**
|
|
|
|
* Extension of the `GithubClient` that provides utilities which are specific
|
|
|
|
* to authenticated instances.
|
|
|
|
*/
|
|
|
|
var AuthenticatedGithubClient = /** @class */ (function (_super) {
|
|
|
|
tslib.__extends(AuthenticatedGithubClient, _super);
|
|
|
|
function AuthenticatedGithubClient(_token) {
|
|
|
|
var _this =
|
|
|
|
// Set the token for the octokit instance.
|
|
|
|
_super.call(this, { auth: _token }) || this;
|
|
|
|
_this._token = _token;
|
|
|
|
/** The graphql instance with authentication set during construction. */
|
|
|
|
_this._graphql = graphql.graphql.defaults({ headers: { authorization: "token " + _this._token } });
|
|
|
|
return _this;
|
2021-04-12 17:50:50 -04:00
|
|
|
}
|
|
|
|
/** Perform a query using Github's Graphql API. */
|
2021-06-03 09:59:20 -04:00
|
|
|
AuthenticatedGithubClient.prototype.graphql = function (queryObject, params) {
|
2021-04-12 17:50:50 -04:00
|
|
|
if (params === void 0) { params = {}; }
|
|
|
|
return tslib.__awaiter(this, void 0, void 0, function () {
|
|
|
|
return tslib.__generator(this, function (_a) {
|
|
|
|
switch (_a.label) {
|
2021-06-03 09:59:20 -04:00
|
|
|
case 0: return [4 /*yield*/, this._graphql(typedGraphqlify.query(queryObject).toString(), params)];
|
2021-04-12 17:50:50 -04:00
|
|
|
case 1: return [2 /*return*/, (_a.sent())];
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
};
|
2021-06-03 09:59:20 -04:00
|
|
|
return AuthenticatedGithubClient;
|
|
|
|
}(GithubClient));
|
2021-04-12 17:50:50 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @license
|
|
|
|
* Copyright Google LLC All Rights Reserved.
|
|
|
|
*
|
|
|
|
* 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
|
|
|
|
*/
|
|
|
|
/** Adds the provided token to the given Github HTTPs remote url. */
|
|
|
|
function addTokenToGitHttpsUrl(githubHttpsUrl, token) {
|
|
|
|
var url$1 = new url.URL(githubHttpsUrl);
|
|
|
|
url$1.username = token;
|
|
|
|
return url$1.href;
|
|
|
|
}
|
|
|
|
/** Gets the repository Git URL for the given github config. */
|
|
|
|
function getRepositoryGitUrl(config, githubToken) {
|
|
|
|
if (config.useSsh) {
|
|
|
|
return "git@github.com:" + config.owner + "/" + config.name + ".git";
|
|
|
|
}
|
|
|
|
var baseHttpUrl = "https://github.com/" + config.owner + "/" + config.name + ".git";
|
|
|
|
if (githubToken !== undefined) {
|
|
|
|
return addTokenToGitHttpsUrl(baseHttpUrl, githubToken);
|
|
|
|
}
|
|
|
|
return baseHttpUrl;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @license
|
|
|
|
* Copyright Google LLC All Rights Reserved.
|
|
|
|
*
|
|
|
|
* 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
|
|
|
|
*/
|
|
|
|
/** Error for failed Git commands. */
|
|
|
|
var GitCommandError = /** @class */ (function (_super) {
|
|
|
|
tslib.__extends(GitCommandError, _super);
|
|
|
|
function GitCommandError(client, args) {
|
|
|
|
var _this =
|
|
|
|
// Errors are not guaranteed to be caught. To ensure that we don't
|
|
|
|
// accidentally leak the Github token that might be used in a command,
|
|
|
|
// we sanitize the command that will be part of the error message.
|
2021-06-03 09:59:20 -04:00
|
|
|
_super.call(this, "Command failed: git " + client.sanitizeConsoleOutput(args.join(' '))) || this;
|
2021-04-12 17:50:50 -04:00
|
|
|
_this.args = args;
|
|
|
|
return _this;
|
|
|
|
}
|
|
|
|
return GitCommandError;
|
|
|
|
}(Error));
|
2021-06-03 09:59:20 -04:00
|
|
|
/** Class that can be used to perform Git interactions with a given remote. **/
|
2021-04-12 17:50:50 -04:00
|
|
|
var GitClient = /** @class */ (function () {
|
2021-06-03 09:59:20 -04:00
|
|
|
function GitClient(
|
|
|
|
/** The full path to the root of the repository base. */
|
|
|
|
baseDir,
|
|
|
|
/** The configuration, containing the github specific configuration. */
|
|
|
|
config) {
|
|
|
|
if (baseDir === void 0) { baseDir = determineRepoBaseDirFromCwd(); }
|
|
|
|
if (config === void 0) { config = getConfig(baseDir); }
|
|
|
|
this.baseDir = baseDir;
|
|
|
|
this.config = config;
|
|
|
|
/** Short-hand for accessing the default remote configuration. */
|
2021-04-12 17:50:50 -04:00
|
|
|
this.remoteConfig = this.config.github;
|
2021-06-03 09:59:20 -04:00
|
|
|
/** Octokit request parameters object for targeting the configured remote. */
|
2021-04-12 17:50:50 -04:00
|
|
|
this.remoteParams = { owner: this.remoteConfig.owner, repo: this.remoteConfig.name };
|
2021-06-03 09:59:20 -04:00
|
|
|
/** Instance of the Github client. */
|
|
|
|
this.github = new GithubClient();
|
2021-04-12 17:50:50 -04:00
|
|
|
}
|
|
|
|
/** Executes the given git command. Throws if the command fails. */
|
|
|
|
GitClient.prototype.run = function (args, options) {
|
|
|
|
var result = this.runGraceful(args, options);
|
|
|
|
if (result.status !== 0) {
|
|
|
|
throw new GitCommandError(this, args);
|
|
|
|
}
|
|
|
|
// Omit `status` from the type so that it's obvious that the status is never
|
|
|
|
// non-zero as explained in the method description.
|
|
|
|
return result;
|
|
|
|
};
|
|
|
|
/**
|
|
|
|
* Spawns a given Git command process. Does not throw if the command fails. Additionally,
|
|
|
|
* if there is any stderr output, the output will be printed. This makes it easier to
|
|
|
|
* info failed commands.
|
|
|
|
*/
|
|
|
|
GitClient.prototype.runGraceful = function (args, options) {
|
|
|
|
if (options === void 0) { options = {}; }
|
|
|
|
/** The git command to be run. */
|
|
|
|
var gitCommand = args[0];
|
|
|
|
if (isDryRun() && gitCommand === 'push') {
|
|
|
|
debug("\"git push\" is not able to be run in dryRun mode.");
|
|
|
|
throw new DryRunError();
|
|
|
|
}
|
|
|
|
// To improve the debugging experience in case something fails, we print all executed Git
|
2021-06-03 09:59:20 -04:00
|
|
|
// commands at the DEBUG level to better understand the git actions occurring. Verbose logging,
|
2021-04-30 12:33:40 -04:00
|
|
|
// always logging at the INFO level, can be enabled either by setting the verboseLogging
|
|
|
|
// property on the GitClient class or the options object provided to the method.
|
|
|
|
var printFn = (GitClient.verboseLogging || options.verboseLogging) ? info : debug;
|
2021-06-03 09:59:20 -04:00
|
|
|
// Note that we sanitize the command before printing it to the console. We do not want to
|
|
|
|
// print an access 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.
|
|
|
|
printFn('Executing: git', this.sanitizeConsoleOutput(args.join(' ')));
|
2021-04-12 17:50:50 -04:00
|
|
|
var result = child_process.spawnSync('git', args, tslib.__assign(tslib.__assign({ cwd: this.baseDir, stdio: 'pipe' }, options), {
|
|
|
|
// Encoding is always `utf8` and not overridable. This ensures that this method
|
|
|
|
// always returns `string` as output instead of buffers.
|
|
|
|
encoding: 'utf8' }));
|
|
|
|
if (result.stderr !== null) {
|
|
|
|
// Git sometimes prints the command if it failed. This means that it could
|
|
|
|
// potentially leak the Github token used for accessing the remote. To avoid
|
|
|
|
// printing a token, we sanitize the string before printing the stderr output.
|
2021-06-03 09:59:20 -04:00
|
|
|
process.stderr.write(this.sanitizeConsoleOutput(result.stderr));
|
2021-04-12 17:50:50 -04:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
};
|
|
|
|
/** Git URL that resolves to the configured repository. */
|
|
|
|
GitClient.prototype.getRepoGitUrl = function () {
|
2021-06-03 09:59:20 -04:00
|
|
|
return getRepositoryGitUrl(this.remoteConfig);
|
2021-04-12 17:50:50 -04:00
|
|
|
};
|
|
|
|
/** Whether the given branch contains the specified SHA. */
|
|
|
|
GitClient.prototype.hasCommit = function (branchName, sha) {
|
|
|
|
return this.run(['branch', branchName, '--contains', sha]).stdout !== '';
|
|
|
|
};
|
|
|
|
/** Gets the currently checked out branch or revision. */
|
|
|
|
GitClient.prototype.getCurrentBranchOrRevision = function () {
|
|
|
|
var branchName = this.run(['rev-parse', '--abbrev-ref', 'HEAD']).stdout.trim();
|
|
|
|
// If no branch name could be resolved. i.e. `HEAD` has been returned, then Git
|
|
|
|
// is currently in a detached state. In those cases, we just want to return the
|
|
|
|
// currently checked out revision/SHA.
|
|
|
|
if (branchName === 'HEAD') {
|
|
|
|
return this.run(['rev-parse', 'HEAD']).stdout.trim();
|
|
|
|
}
|
|
|
|
return branchName;
|
|
|
|
};
|
|
|
|
/** Gets whether the current Git repository has uncommitted changes. */
|
|
|
|
GitClient.prototype.hasUncommittedChanges = function () {
|
|
|
|
return this.runGraceful(['diff-index', '--quiet', 'HEAD']).status !== 0;
|
|
|
|
};
|
|
|
|
/**
|
|
|
|
* Checks out a requested branch or revision, optionally cleaning the state of the repository
|
|
|
|
* before attempting the checking. Returns a boolean indicating whether the branch or revision
|
|
|
|
* was cleanly checked out.
|
|
|
|
*/
|
|
|
|
GitClient.prototype.checkout = function (branchOrRevision, cleanState) {
|
|
|
|
if (cleanState) {
|
|
|
|
// Abort any outstanding ams.
|
|
|
|
this.runGraceful(['am', '--abort'], { stdio: 'ignore' });
|
|
|
|
// Abort any outstanding cherry-picks.
|
|
|
|
this.runGraceful(['cherry-pick', '--abort'], { stdio: 'ignore' });
|
|
|
|
// Abort any outstanding rebases.
|
|
|
|
this.runGraceful(['rebase', '--abort'], { stdio: 'ignore' });
|
|
|
|
// Clear any changes in the current repo.
|
|
|
|
this.runGraceful(['reset', '--hard'], { stdio: 'ignore' });
|
|
|
|
}
|
|
|
|
return this.runGraceful(['checkout', branchOrRevision], { stdio: 'ignore' }).status === 0;
|
|
|
|
};
|
|
|
|
/** Gets the latest git tag on the current branch that matches SemVer. */
|
|
|
|
GitClient.prototype.getLatestSemverTag = function () {
|
|
|
|
var semVerOptions = { loose: true };
|
|
|
|
var tags = this.runGraceful(['tag', '--sort=-committerdate', '--merged']).stdout.split('\n');
|
|
|
|
var latestTag = tags.find(function (tag) { return semver.parse(tag, semVerOptions); });
|
|
|
|
if (latestTag === undefined) {
|
|
|
|
throw new Error("Unable to find a SemVer matching tag on \"" + this.getCurrentBranchOrRevision() + "\"");
|
|
|
|
}
|
|
|
|
return new semver.SemVer(latestTag, semVerOptions);
|
|
|
|
};
|
2021-06-03 09:59:20 -04:00
|
|
|
/** Retrieve a list of all files in the repository changed since the provided shaOrRef. */
|
2021-04-12 17:50:50 -04:00
|
|
|
GitClient.prototype.allChangesFilesSince = function (shaOrRef) {
|
|
|
|
if (shaOrRef === void 0) { shaOrRef = 'HEAD'; }
|
|
|
|
return Array.from(new Set(tslib.__spreadArray(tslib.__spreadArray([], tslib.__read(gitOutputAsArray(this.runGraceful(['diff', '--name-only', '--diff-filter=d', shaOrRef])))), tslib.__read(gitOutputAsArray(this.runGraceful(['ls-files', '--others', '--exclude-standard']))))));
|
|
|
|
};
|
|
|
|
/** Retrieve a list of all files currently staged in the repostitory. */
|
|
|
|
GitClient.prototype.allStagedFiles = function () {
|
|
|
|
return gitOutputAsArray(this.runGraceful(['diff', '--name-only', '--diff-filter=ACM', '--staged']));
|
|
|
|
};
|
2021-06-03 09:59:20 -04:00
|
|
|
/** Retrieve a list of all files tracked in the repository. */
|
2021-04-12 17:50:50 -04:00
|
|
|
GitClient.prototype.allFiles = function () {
|
|
|
|
return gitOutputAsArray(this.runGraceful(['ls-files']));
|
|
|
|
};
|
|
|
|
/**
|
2021-06-03 09:59:20 -04:00
|
|
|
* Sanitizes the given console message. This method can be overridden by
|
|
|
|
* derived classes. e.g. to sanitize access tokens from Git commands.
|
2021-04-12 17:50:50 -04:00
|
|
|
*/
|
2021-06-03 09:59:20 -04:00
|
|
|
GitClient.prototype.sanitizeConsoleOutput = function (value) {
|
|
|
|
return value;
|
2021-04-12 17:50:50 -04:00
|
|
|
};
|
2021-06-03 09:59:20 -04:00
|
|
|
/** Set the verbose logging state of all git client instances. */
|
|
|
|
GitClient.setVerboseLoggingState = function (verbose) {
|
|
|
|
GitClient.verboseLogging = verbose;
|
2021-04-12 17:50:50 -04:00
|
|
|
};
|
2021-06-03 09:59:20 -04:00
|
|
|
/**
|
|
|
|
* Static method to get the singleton instance of the `GitClient`, creating it
|
|
|
|
* if it has not yet been created.
|
|
|
|
*/
|
|
|
|
GitClient.get = function () {
|
|
|
|
if (!this._unauthenticatedInstance) {
|
|
|
|
GitClient._unauthenticatedInstance = new GitClient();
|
2021-04-12 17:50:50 -04:00
|
|
|
}
|
2021-06-03 09:59:20 -04:00
|
|
|
return GitClient._unauthenticatedInstance;
|
2021-04-12 17:50:50 -04:00
|
|
|
};
|
2021-04-30 12:33:40 -04:00
|
|
|
/** Whether verbose logging of Git actions should be used. */
|
|
|
|
GitClient.verboseLogging = false;
|
2021-04-12 17:50:50 -04:00
|
|
|
return GitClient;
|
|
|
|
}());
|
|
|
|
/**
|
2021-06-03 09:59:20 -04:00
|
|
|
* Takes the output from `run` and `runGraceful` and returns an array of strings for each
|
|
|
|
* new line. Git commands typically return multiple output values for a command a set of
|
2021-04-12 17:50:50 -04:00
|
|
|
* strings separated by new lines.
|
|
|
|
*
|
2021-04-18 05:43:49 -04:00
|
|
|
* Note: This is specifically created as a locally available function for usage as convenience
|
|
|
|
* utility within `GitClient`'s methods to create outputs as array.
|
2021-04-12 17:50:50 -04:00
|
|
|
*/
|
|
|
|
function gitOutputAsArray(gitCommandResult) {
|
|
|
|
return gitCommandResult.stdout.split('\n').map(function (x) { return x.trim(); }).filter(function (x) { return !!x; });
|
|
|
|
}
|
2021-06-03 09:59:20 -04:00
|
|
|
/** Determines the repository base directory from the current working directory. */
|
|
|
|
function determineRepoBaseDirFromCwd() {
|
|
|
|
// TODO(devversion): Replace with common spawn sync utility once available.
|
|
|
|
var _a = child_process.spawnSync('git', ['rev-parse --show-toplevel'], { shell: true, stdio: 'pipe', encoding: 'utf8' }), stdout = _a.stdout, stderr = _a.stderr, status = _a.status;
|
|
|
|
if (status !== 0) {
|
|
|
|
throw Error("Unable to find the path to the base directory of the repository.\n" +
|
|
|
|
"Was the command run from inside of the repo?\n\n" +
|
|
|
|
("" + stderr));
|
|
|
|
}
|
|
|
|
return stdout.trim();
|
|
|
|
}
|
2020-12-07 13:52:40 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @license
|
|
|
|
* Copyright Google LLC All Rights Reserved.
|
|
|
|
*
|
|
|
|
* 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
|
|
|
|
*/
|
|
|
|
/**
|
|
|
|
* Supported levels for logging functions.
|
|
|
|
*
|
|
|
|
* Levels are mapped to numbers to represent a hierarchy of logging levels.
|
|
|
|
*/
|
|
|
|
var LOG_LEVELS;
|
|
|
|
(function (LOG_LEVELS) {
|
|
|
|
LOG_LEVELS[LOG_LEVELS["SILENT"] = 0] = "SILENT";
|
|
|
|
LOG_LEVELS[LOG_LEVELS["ERROR"] = 1] = "ERROR";
|
|
|
|
LOG_LEVELS[LOG_LEVELS["WARN"] = 2] = "WARN";
|
|
|
|
LOG_LEVELS[LOG_LEVELS["LOG"] = 3] = "LOG";
|
|
|
|
LOG_LEVELS[LOG_LEVELS["INFO"] = 4] = "INFO";
|
|
|
|
LOG_LEVELS[LOG_LEVELS["DEBUG"] = 5] = "DEBUG";
|
|
|
|
})(LOG_LEVELS || (LOG_LEVELS = {}));
|
|
|
|
/** Default log level for the tool. */
|
|
|
|
var DEFAULT_LOG_LEVEL = LOG_LEVELS.INFO;
|
|
|
|
/** Write to the console for at INFO logging level */
|
|
|
|
var info = buildLogLevelFunction(function () { return console.info; }, LOG_LEVELS.INFO);
|
|
|
|
/** Write to the console for at ERROR logging level */
|
|
|
|
var error = buildLogLevelFunction(function () { return console.error; }, LOG_LEVELS.ERROR);
|
|
|
|
/** Write to the console for at DEBUG logging level */
|
|
|
|
var debug = buildLogLevelFunction(function () { return console.debug; }, LOG_LEVELS.DEBUG);
|
|
|
|
/** Write to the console for at LOG logging level */
|
|
|
|
// tslint:disable-next-line: no-console
|
|
|
|
var log = buildLogLevelFunction(function () { return console.log; }, LOG_LEVELS.LOG);
|
|
|
|
/** Write to the console for at WARN logging level */
|
|
|
|
var warn = buildLogLevelFunction(function () { return console.warn; }, LOG_LEVELS.WARN);
|
|
|
|
/** Build an instance of a logging function for the provided level. */
|
|
|
|
function buildLogLevelFunction(loadCommand, level) {
|
|
|
|
/** Write to stdout for the LOG_LEVEL. */
|
|
|
|
var loggingFunction = function () {
|
|
|
|
var text = [];
|
|
|
|
for (var _i = 0; _i < arguments.length; _i++) {
|
|
|
|
text[_i] = arguments[_i];
|
|
|
|
}
|
2021-03-16 15:10:32 -04:00
|
|
|
runConsoleCommand.apply(void 0, tslib.__spreadArray([loadCommand, level], tslib.__read(text)));
|
2020-12-07 13:52:40 -05:00
|
|
|
};
|
|
|
|
/** Start a group at the LOG_LEVEL, optionally starting it as collapsed. */
|
|
|
|
loggingFunction.group = function (text, collapsed) {
|
|
|
|
if (collapsed === void 0) { collapsed = false; }
|
|
|
|
var command = collapsed ? console.groupCollapsed : console.group;
|
|
|
|
runConsoleCommand(function () { return command; }, level, text);
|
|
|
|
};
|
|
|
|
/** End the group at the LOG_LEVEL. */
|
|
|
|
loggingFunction.groupEnd = function () {
|
|
|
|
runConsoleCommand(function () { return console.groupEnd; }, level);
|
|
|
|
};
|
|
|
|
return loggingFunction;
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Run the console command provided, if the environments logging level greater than the
|
|
|
|
* provided logging level.
|
|
|
|
*
|
|
|
|
* The loadCommand takes in a function which is called to retrieve the console.* function
|
|
|
|
* to allow for jasmine spies to still work in testing. Without this method of retrieval
|
|
|
|
* the console.* function, the function is saved into the closure of the created logging
|
|
|
|
* function before jasmine can spy.
|
|
|
|
*/
|
|
|
|
function runConsoleCommand(loadCommand, logLevel) {
|
|
|
|
var text = [];
|
|
|
|
for (var _i = 2; _i < arguments.length; _i++) {
|
|
|
|
text[_i - 2] = arguments[_i];
|
|
|
|
}
|
|
|
|
if (getLogLevel() >= logLevel) {
|
2021-03-16 15:10:32 -04:00
|
|
|
loadCommand().apply(void 0, tslib.__spreadArray([], tslib.__read(text)));
|
2020-12-07 13:52:40 -05:00
|
|
|
}
|
2021-03-16 15:10:32 -04:00
|
|
|
printToLogFile.apply(void 0, tslib.__spreadArray([logLevel], tslib.__read(text)));
|
2020-12-07 13:52:40 -05:00
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Retrieve the log level from environment variables, if the value found
|
|
|
|
* based on the LOG_LEVEL environment variable is undefined, return the default
|
|
|
|
* logging level.
|
|
|
|
*/
|
|
|
|
function getLogLevel() {
|
|
|
|
var logLevelEnvValue = (process.env["LOG_LEVEL"] || '').toUpperCase();
|
|
|
|
var logLevel = LOG_LEVELS[logLevelEnvValue];
|
|
|
|
if (logLevel === undefined) {
|
|
|
|
return DEFAULT_LOG_LEVEL;
|
|
|
|
}
|
|
|
|
return logLevel;
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* The number of columns used in the prepended log level information on each line of the logging
|
|
|
|
* output file.
|
|
|
|
*/
|
|
|
|
var LOG_LEVEL_COLUMNS = 7;
|
|
|
|
/** Write the provided text to the log file, prepending each line with the log level. */
|
|
|
|
function printToLogFile(logLevel) {
|
|
|
|
var text = [];
|
|
|
|
for (var _i = 1; _i < arguments.length; _i++) {
|
|
|
|
text[_i - 1] = arguments[_i];
|
|
|
|
}
|
|
|
|
var logLevelText = (LOG_LEVELS[logLevel] + ":").padEnd(LOG_LEVEL_COLUMNS);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @license
|
|
|
|
* Copyright Google LLC All Rights Reserved.
|
|
|
|
*
|
|
|
|
* 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
|
|
|
|
*/
|
|
|
|
/** Whether ts-node has been installed and is available to ng-dev. */
|
|
|
|
function isTsNodeAvailable() {
|
|
|
|
try {
|
|
|
|
require.resolve('ts-node');
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
catch (_a) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @license
|
|
|
|
* Copyright Google LLC All Rights Reserved.
|
|
|
|
*
|
|
|
|
* 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
|
|
|
|
*/
|
|
|
|
/**
|
|
|
|
* The filename expected for creating the ng-dev config, without the file
|
|
|
|
* extension to allow either a typescript or javascript file to be used.
|
|
|
|
*/
|
|
|
|
var CONFIG_FILE_PATH = '.ng-dev/config';
|
|
|
|
/** The configuration for ng-dev. */
|
|
|
|
var cachedConfig = null;
|
2021-04-12 17:50:50 -04:00
|
|
|
function getConfig(baseDir) {
|
2020-12-07 13:52:40 -05:00
|
|
|
// If the global config is not defined, load it from the file system.
|
|
|
|
if (cachedConfig === null) {
|
2021-06-03 09:59:20 -04:00
|
|
|
baseDir = baseDir || GitClient.get().baseDir;
|
2020-12-07 13:52:40 -05:00
|
|
|
// The full path to the configuration file.
|
2021-04-12 17:50:50 -04:00
|
|
|
var configPath = path.join(baseDir, CONFIG_FILE_PATH);
|
2020-12-07 13:52:40 -05:00
|
|
|
// Read the configuration and validate it before caching it for the future.
|
|
|
|
cachedConfig = validateCommonConfig(readConfigFile(configPath));
|
|
|
|
}
|
|
|
|
// Return a clone of the cached global config to ensure that a new instance of the config
|
|
|
|
// is returned each time, preventing unexpected effects of modifications to the config object.
|
|
|
|
return tslib.__assign({}, cachedConfig);
|
|
|
|
}
|
|
|
|
/** Validate the common configuration has been met for the ng-dev command. */
|
|
|
|
function validateCommonConfig(config) {
|
|
|
|
var errors = [];
|
|
|
|
// Validate the github configuration.
|
|
|
|
if (config.github === undefined) {
|
|
|
|
errors.push("Github repository not configured. Set the \"github\" option.");
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (config.github.name === undefined) {
|
|
|
|
errors.push("\"github.name\" is not defined");
|
|
|
|
}
|
|
|
|
if (config.github.owner === undefined) {
|
|
|
|
errors.push("\"github.owner\" is not defined");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
assertNoErrors(errors);
|
|
|
|
return config;
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Resolves and reads the specified configuration file, optionally returning an empty object if the
|
|
|
|
* configuration file cannot be read.
|
|
|
|
*/
|
|
|
|
function readConfigFile(configPath, returnEmptyObjectOnError) {
|
|
|
|
if (returnEmptyObjectOnError === void 0) { returnEmptyObjectOnError = false; }
|
2021-01-14 14:03:58 -05:00
|
|
|
// If the `.ts` extension has not been set up already, and a TypeScript based
|
2020-12-07 13:52:40 -05:00
|
|
|
// version of the given configuration seems to exist, set up `ts-node` if available.
|
|
|
|
if (require.extensions['.ts'] === undefined && fs.existsSync(configPath + ".ts") &&
|
|
|
|
isTsNodeAvailable()) {
|
|
|
|
// Ensure the module target is set to `commonjs`. This is necessary because the
|
|
|
|
// dev-infra tool runs in NodeJS which does not support ES modules by default.
|
|
|
|
// Additionally, set the `dir` option to the directory that contains the configuration
|
|
|
|
// file. This allows for custom compiler options (such as `--strict`).
|
|
|
|
require('ts-node').register({ dir: path.dirname(configPath), transpileOnly: true, compilerOptions: { module: 'commonjs' } });
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
return require(configPath);
|
|
|
|
}
|
|
|
|
catch (e) {
|
|
|
|
if (returnEmptyObjectOnError) {
|
|
|
|
debug("Could not read configuration file at " + configPath + ", returning empty object instead.");
|
|
|
|
debug(e);
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
error("Could not read configuration file at " + configPath + ".");
|
|
|
|
error(e);
|
|
|
|
process.exit(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Asserts the provided array of error messages is empty. If any errors are in the array,
|
|
|
|
* logs the errors and exit the process as a failure.
|
|
|
|
*/
|
|
|
|
function assertNoErrors(errors) {
|
|
|
|
var e_1, _a;
|
|
|
|
if (errors.length == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
error("Errors discovered while loading configuration file:");
|
|
|
|
try {
|
|
|
|
for (var errors_1 = tslib.__values(errors), errors_1_1 = errors_1.next(); !errors_1_1.done; errors_1_1 = errors_1.next()) {
|
|
|
|
var err = errors_1_1.value;
|
|
|
|
error(" - " + err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
|
|
finally {
|
|
|
|
try {
|
|
|
|
if (errors_1_1 && !errors_1_1.done && (_a = errors_1.return)) _a.call(errors_1);
|
|
|
|
}
|
|
|
|
finally { if (e_1) throw e_1.error; }
|
|
|
|
}
|
|
|
|
process.exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @license
|
|
|
|
* Copyright Google LLC All Rights Reserved.
|
|
|
|
*
|
|
|
|
* 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
|
|
|
|
*/
|
|
|
|
/** Retrieve and validate the config as `ReleaseConfig`. */
|
|
|
|
function getReleaseConfig(config = getConfig()) {
|
|
|
|
var _a, _b, _c;
|
|
|
|
// List of errors encountered validating the config.
|
|
|
|
const errors = [];
|
|
|
|
if (config.release === undefined) {
|
|
|
|
errors.push(`No configuration defined for "release"`);
|
|
|
|
}
|
|
|
|
if (((_a = config.release) === null || _a === void 0 ? void 0 : _a.npmPackages) === undefined) {
|
|
|
|
errors.push(`No "npmPackages" configured for releasing.`);
|
|
|
|
}
|
|
|
|
if (((_b = config.release) === null || _b === void 0 ? void 0 : _b.buildPackages) === undefined) {
|
|
|
|
errors.push(`No "buildPackages" function configured for releasing.`);
|
|
|
|
}
|
2021-04-19 14:58:32 -04:00
|
|
|
if (((_c = config.release) === null || _c === void 0 ? void 0 : _c.releaseNotes) === undefined) {
|
|
|
|
errors.push(`No "releaseNotes" configured for releasing.`);
|
2020-12-07 13:52:40 -05:00
|
|
|
}
|
|
|
|
assertNoErrors(errors);
|
|
|
|
return config.release;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @license
|
|
|
|
* Copyright Google LLC All Rights Reserved.
|
|
|
|
*
|
|
|
|
* 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
|
|
|
|
*/
|
|
|
|
// Start the release package building.
|
2021-05-25 12:42:36 -04:00
|
|
|
main(process.argv[2] === 'true');
|
2020-12-07 13:52:40 -05:00
|
|
|
/** Main function for building the release packages. */
|
2021-05-25 12:42:36 -04:00
|
|
|
function main(stampForRelease) {
|
2020-12-07 13:52:40 -05:00
|
|
|
return tslib.__awaiter(this, void 0, void 0, function* () {
|
|
|
|
if (process.send === undefined) {
|
|
|
|
throw Error('This script needs to be invoked as a NodeJS worker.');
|
|
|
|
}
|
|
|
|
const config = getReleaseConfig();
|
2021-05-25 12:42:36 -04:00
|
|
|
const builtPackages = yield config.buildPackages(stampForRelease);
|
2020-12-07 13:52:40 -05:00
|
|
|
// Transfer the built packages back to the parent process.
|
|
|
|
process.send(builtPackages);
|
|
|
|
});
|
|
|
|
}
|