diff --git a/package.json b/package.json index 89aa0183418e..a0acfe3e6455 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,8 @@ "docs": "gulp docs", "api": "gulp api-docs", "breaking-changes": "gulp breaking-changes", - "gulp": "gulp" + "gulp": "gulp", + "stage-release": "bash ./tools/release/stage-release-bin.sh" }, "version": "7.0.0-rc.2", "requiredAngularVersion": ">=7.0.0-rc.0", @@ -65,6 +66,7 @@ "@types/gulp": "3.8.32", "@types/gulp-util": "^3.0.34", "@types/hammerjs": "^2.0.35", + "@types/inquirer": "^0.0.43", "@types/jasmine": "^2.8.8", "@types/merge2": "^0.3.30", "@types/minimist": "^1.2.0", @@ -104,6 +106,7 @@ "hammerjs": "^2.0.8", "highlight.js": "^9.11.0", "http-rewrite-middleware": "^0.1.6", + "inquirer": "^6.2.0", "jasmine-core": "^3.2.0", "karma": "^3.0.0", "karma-browserstack-launcher": "^1.3.0", diff --git a/tools/release/BUILD.bazel b/tools/release/BUILD.bazel new file mode 100644 index 000000000000..6d9e2d456d53 --- /dev/null +++ b/tools/release/BUILD.bazel @@ -0,0 +1,25 @@ +package(default_visibility=["//visibility:public"]) + +load("@build_bazel_rules_nodejs//:defs.bzl", "nodejs_binary") +load("@build_bazel_rules_typescript//:defs.bzl", "ts_library") + +ts_library( + name = "release-sources", + srcs = glob(["**/*.ts"]), + deps = [ + "@npm//@types/node", + "@npm//@types/inquirer", + "@npm//chalk", + "@npm//inquirer" + ], + tsconfig = ":tsconfig.json" +) + +nodejs_binary( + name = "stage-release", + data = [ + "@npm//source-map-support", + ":release-sources", + ], + entry_point = "angular_material/tools/release/stage-release.js", +) diff --git a/tools/release/git/git-client.ts b/tools/release/git/git-client.ts new file mode 100644 index 000000000000..939e52e864ad --- /dev/null +++ b/tools/release/git/git-client.ts @@ -0,0 +1,51 @@ +import {spawnSync} from 'child_process'; + +/** + * Class that can be used to execute Git commands within a given project directory. + * + * Relying on the working directory of the current process is not good because it's not + * guaranteed that the working directory is always the target project directory. + */ +export class GitClient { + + constructor(public projectDir: string, public remoteGitUrl: string) {} + + /** Gets the currently checked out branch for the project directory. */ + getCurrentBranch() { + return spawnSync('git', ['symbolic-ref', '--short', 'HEAD'], {cwd: this.projectDir}) + .stdout.toString().trim(); + } + + /** Gets the commit SHA for the specified remote repository branch. */ + getRemoteCommitSha(branchName: string): string { + return spawnSync('git', ['ls-remote', this.remoteGitUrl, '-h', `refs/heads/${branchName}`], + {cwd: this.projectDir}).stdout.toString().trim(); + } + + /** Gets the latest commit SHA for the specified git reference. */ + getLocalCommitSha(refName: string) { + return spawnSync('git', ['rev-parse', refName], {cwd: this.projectDir}) + .stdout.toString().trim(); + } + + /** Gets whether the current Git repository has uncommitted changes. */ + hasUncommittedChanges(): boolean { + return spawnSync('git', ['diff-index', '--quiet', 'HEAD'], {cwd: this.projectDir}).status !== 0; + } + + /** Creates a new branch which is based on the previous active branch. */ + checkoutNewBranch(branchName: string): boolean { + return spawnSync('git', ['checkout', '-b', branchName], {cwd: this.projectDir}).status === 0; + } + + /** Stages all changes by running `git add -A`. */ + stageAllChanges(): boolean { + return spawnSync('git', ['add', '-A'], {cwd: this.projectDir}).status === 0; + } + + /** Creates a new commit within the current branch with the given commit message. */ + createNewCommit(message: string): boolean { + return spawnSync('git', ['commit', '-m', message], {cwd: this.projectDir}).status === 0; + } +} + diff --git a/tools/release/prompt/new-version-prompt.ts b/tools/release/prompt/new-version-prompt.ts new file mode 100644 index 000000000000..4a9dbc5e99f7 --- /dev/null +++ b/tools/release/prompt/new-version-prompt.ts @@ -0,0 +1,57 @@ +import {ChoiceType, prompt, Separator} from 'inquirer'; +import {createNewVersion, ReleaseType} from '../version-name/create-version'; +import {parseVersionName, Version} from '../version-name/parse-version'; + +/** Answers that will be prompted for. */ +type VersionPromptAnswers = { + versionName: string; + manualCustomVersion: string; +}; + +/** + * Prompts the current user-input interface for a new version name. The new version will be + * validated to be a proper increment of the specified current version. + */ +export async function promptForNewVersion(currentVersion: Version): Promise { + const versionChoices: ChoiceType[] = [ + new Separator(), + {value: 'custom-release', name: 'Release w/ custom version'} + ]; + + if (currentVersion.prereleaseLabel) { + versionChoices.unshift( + createVersionChoice(currentVersion, 'pre-release', 'Pre-release'), + createVersionChoice(currentVersion, 'stable-release', 'Stable release')); + } else { + versionChoices.unshift( + createVersionChoice(currentVersion, 'major', 'Major release'), + createVersionChoice(currentVersion, 'minor', 'Minor release'), + createVersionChoice(currentVersion, 'patch', 'Patch release')); + } + + const answers = await prompt([{ + type: 'list', + name: 'versionName', + message: `What's the type of the new release?`, + choices: versionChoices, + }, { + type: 'input', + name: 'manualCustomVersion', + message: 'Please provide a custom release name:', + validate: enteredVersion => + !!parseVersionName(enteredVersion) || 'This is not a valid Semver version', + when: ({versionName}) => versionName === 'custom-release' + }]); + + return parseVersionName(answers.manualCustomVersion || answers.versionName); +} + +/** Creates a new choice for selecting a version inside of an Inquirer list prompt. */ +function createVersionChoice(currentVersion: Version, releaseType: ReleaseType, message: string) { + const versionName = createNewVersion(currentVersion, releaseType).format(); + + return { + value: versionName, + name: `${message} (${versionName})` + }; +} diff --git a/tools/release/stage-release-bin.sh b/tools/release/stage-release-bin.sh new file mode 100644 index 000000000000..3c659a410bce --- /dev/null +++ b/tools/release/stage-release-bin.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +# Script that builds and launches the stage release script through Bazel. An additional script is +# needed because environment variables (like $PWD) are not being interpolated within NPM scripts. + +# Go to project directory. +cd $(dirname ${0})/../.. + +# Build and run the stage release script. +bazel run //tools/release:stage-release -- $PWD diff --git a/tools/release/stage-release.ts b/tools/release/stage-release.ts new file mode 100644 index 000000000000..142438571325 --- /dev/null +++ b/tools/release/stage-release.ts @@ -0,0 +1,184 @@ +import {bold, cyan, green, italic, red, yellow} from 'chalk'; +import {existsSync, readFileSync, writeFileSync} from 'fs'; +import {prompt} from 'inquirer'; +import {join} from 'path'; +import {GitClient} from './git/git-client'; +import {promptForNewVersion} from './prompt/new-version-prompt'; +import {parseVersionName, Version} from './version-name/parse-version'; +import {getExpectedPublishBranch} from './version-name/publish-branch'; + +/** + * Class that can be instantiated in order to stage a new release. The tasks requires user + * interaction/input through command line prompts. + * + * Staging a release involves the following the steps: + * + * 1) Prompt for release type (with version suggestion) + * 2) Prompt for version name if no suggestions has been selected + * 3) Assert that the proper publish branch is checked out (e.g. 6.4.x for patches) + * 4) Assert that there are no local changes which are uncommitted. + * 5) Assert that the local branch is up to date with the remote branch. + * 6) Creates a new branch for the release staging (release-stage/{VERSION}) + * 7) Switches to the staging branch and updates the package.json + * 8) Waits for the user to continue (users can generate the changelog in the meanwhile) + * 9) Create a commit that includes all changes in the staging branch. + */ +class StageReleaseTask { + + /** Path to the project package JSON. */ + packageJsonPath: string; + + /** Serialized package.json of the specified project. */ + packageJson: any; + + /** Parsed current version of the project. */ + currentVersion: Version; + + /** Instance of a wrapper that can execute Git commands. */ + git: GitClient; + + constructor(public projectDir: string) { + this.packageJsonPath = join(projectDir, 'package.json'); + + console.log(this.projectDir); + + if (!existsSync(this.packageJsonPath)) { + console.error(red(`The specified directory is not referring to a project directory. ` + + `There must be a ${italic('package.json')} file in the project directory.`)); + process.exit(1); + } + + this.packageJson = JSON.parse(readFileSync(this.packageJsonPath, 'utf-8')); + this.currentVersion = parseVersionName(this.packageJson.version); + + if (!this.currentVersion) { + console.error(red(`Cannot parse current version in ${italic('package.json')}. Please ` + + `make sure "${this.packageJson.version}" is a valid Semver version.`)); + process.exit(1); + } + + this.git = new GitClient(projectDir, this.packageJson.repository.url); + } + + async run() { + console.log(); + console.log(cyan('-----------------------------------------')); + console.log(cyan(' Angular Material stage release script')); + console.log(cyan('-----------------------------------------')); + console.log(); + + const newVersion = await promptForNewVersion(this.currentVersion); + const expectedPublishBranch = getExpectedPublishBranch(newVersion); + + // After the prompt for the new version, we print a new line because we want the + // new log messages to be more in the foreground. + console.log(); + + this.verifyPublishBranch(expectedPublishBranch); + this.verifyLocalCommitsMatchUpstream(expectedPublishBranch); + this.verifyNoUncommittedChanges(); + + // TODO(devversion): Assert that GitHub statuses succeed for this branch. + + const newVersionName = newVersion.format(); + const stagingBranch = `release-stage/${newVersionName}`; + + if (!this.git.checkoutNewBranch(stagingBranch)) { + console.error(red(`Could not create release staging branch: ${stagingBranch}. Aborting...`)); + process.exit(1); + } + + this.updatePackageJsonVersion(newVersionName); + + console.log(green(` ✓ Updated the version to "${bold(newVersionName)}" inside of the ` + + `${italic('package.json')}`)); + + // TODO(devversion): run changelog script w/prompts in the future. + // For now, we just let users make modifications and stage the changes. + + console.log(yellow(` ⚠ Please generate the ${bold('CHANGELOG')} for the new version. ` + + `You can also make other unrelated modifications. After the changes have been made, ` + + `just continue here.`)); + console.log(); + + const {shouldContinue} = await prompt<{shouldContinue: boolean}>({ + type: 'confirm', + name: 'shouldContinue', + message: 'Do you want to proceed and commit the changes?' + }); + + if (!shouldContinue) { + console.log(); + console.log(yellow('Aborting release staging...')); + process.exit(1); + } + + this.git.stageAllChanges(); + // this.git.createNewCommit(`chore: bump version to ${newVersionName} w/ changelog`); + + console.info(); + console.info(green(` ✓ Created the staging commit for: "${newVersionName}".`)); + console.info(green(` ✓ Please push the changes and submit a PR on GitHub.`)); + console.info(); + + // TODO(devversion): automatic push and PR open URL shortcut. + } + + /** Verifies that the user is on the specified publish branch. */ + private verifyPublishBranch(expectedPublishBranch: string) { + const currentBranchName = this.git.getCurrentBranch(); + + // Check if current branch matches the expected publish branch. + if (expectedPublishBranch !== currentBranchName) { + console.error(red(`Cannot stage release from "${italic(currentBranchName)}". Please stage ` + + `the release from "${bold(expectedPublishBranch)}".`)); + process.exit(1); + } + } + + /** Verifies that the local branch is up to date with the given publish branch. */ + private verifyLocalCommitsMatchUpstream(publishBranch: string) { + const upstreamCommitSha = this.git.getRemoteCommitSha(publishBranch); + const localCommitSha = this.git.getLocalCommitSha('HEAD'); + + // Check if the current branch is in sync with the remote branch. + if (upstreamCommitSha !== localCommitSha) { + console.error(red(`Cannot stage release. The current branch is not in sync with the remote ` + + `branch. Please make sure your local branch "${italic(publishBranch)}" is up to date.`)); + process.exit(1); + } + } + + /** Verifies that there are no uncommitted changes in the project. */ + private verifyNoUncommittedChanges() { + if (this.git.hasUncommittedChanges()) { + console.error(red(`Cannot stage release. There are changes which are not committed and ` + + `should be stashed.`)); + process.exit(1); + } + } + + /** Updates the version of the project package.json and writes the changes to disk. */ + private updatePackageJsonVersion(newVersionName: string) { + const newPackageJson = {...this.packageJson, version: newVersionName}; + writeFileSync(this.packageJsonPath, JSON.stringify(newPackageJson, null, 2)); + } +} + +/** Entry-point for the release staging script. */ +async function main() { + const projectDir = process.argv.slice(2)[0]; + + if (!projectDir) { + console.error(red(`You specified no project directory. Cannot run stage release script.`)); + console.error(red(`Usage: bazel run //tools/release:stage-release `)); + process.exit(1); + } + + return new StageReleaseTask(projectDir).run(); +} + +if (require.main === module) { + main(); +} + diff --git a/tools/release/tsconfig.json b/tools/release/tsconfig.json new file mode 100644 index 000000000000..711798730388 --- /dev/null +++ b/tools/release/tsconfig.json @@ -0,0 +1,6 @@ +{ + "compilerOptions": { + "lib": ["es2015"], + "types": ["node"] + } +} diff --git a/tools/release/version-name/create-version.ts b/tools/release/version-name/create-version.ts new file mode 100644 index 000000000000..f65798da5853 --- /dev/null +++ b/tools/release/version-name/create-version.ts @@ -0,0 +1,34 @@ +import {Version} from './parse-version'; +import {VersionType} from './publish-branch'; + +/** Type of a new release */ +export type ReleaseType = VersionType | 'stable-release' | 'pre-release' | 'custom-release'; + +/** Creates a new version that can be used for the given release type. */ +export function createNewVersion(currentVersion: Version, releaseType: ReleaseType): + Version { + // Clone the version object in order to keep the original version info un-modified. + const newVersion = currentVersion.clone(); + + if (releaseType === 'pre-release') { + newVersion.prereleaseNumber++; + } else { + // For all other release types, the pre-release label and number should be removed + // because the new version is not another pre-release. + newVersion.prereleaseLabel = null; + newVersion.prereleaseNumber = null; + } + + if (releaseType === 'major') { + newVersion.major++; + newVersion.minor = 0; + newVersion.patch = 0; + } else if (releaseType === 'minor') { + newVersion.minor++; + newVersion.patch = 0; + } else if (releaseType === 'patch') { + newVersion.patch++; + } + + return newVersion; +} diff --git a/tools/release/version-name/parse-version.ts b/tools/release/version-name/parse-version.ts new file mode 100644 index 000000000000..3b75fe0086ff --- /dev/null +++ b/tools/release/version-name/parse-version.ts @@ -0,0 +1,59 @@ +/** Regular expression that matches version names and the individual version segments. */ +const versionNameRegex = /^(\d+)\.(\d+)\.(\d+)(?:-(alpha|beta|rc)\.(\d)+)?$/; + +export class Version { + + constructor( + /** Major version number */ + public major: number, + /** Minor version number */ + public minor: number, + /** Patch version number */ + public patch: number, + /** Pre-release label for the version (e.g. alpha, beta, rc) */ + public prereleaseLabel?: string, + /** Number for the pre-release. There can be multiple pre-releases for a version. */ + public prereleaseNumber?: number) {} + + /** Serializes the version info into a string formatted version name. */ + format(): string { + return serializeVersion(this); + } + + clone(): Version { + return new Version(this.major, this.minor, this.patch, this.prereleaseLabel, + this.prereleaseNumber); + } +} + +/** + * Parses the specified version and returns an object that represents the individual + * version segments. + */ +export function parseVersionName(version: string): Version | null { + const matches = version.match(versionNameRegex); + + if (!matches) { + return null; + } + + return new Version( + Number(matches[1]), + Number(matches[2]), + Number(matches[3]), + matches[4], + Number(matches[5])); +} + +/** Serializes the specified version into a string. */ +export function serializeVersion(newVersion: Version): string { + const {major, minor, patch, prereleaseLabel, prereleaseNumber} = newVersion; + + let versionString = `${major}.${minor}.${patch}`; + + if (prereleaseLabel && prereleaseNumber) { + versionString += `-${prereleaseLabel}.${prereleaseNumber}`; + } + + return versionString; +} diff --git a/tools/release/version-name/publish-branch.ts b/tools/release/version-name/publish-branch.ts new file mode 100644 index 000000000000..c85dbe8fa850 --- /dev/null +++ b/tools/release/version-name/publish-branch.ts @@ -0,0 +1,27 @@ +import {Version} from './parse-version'; + +export type VersionType = 'major' | 'minor' | 'patch'; + +/** Determines the expected branch name for publishing the specified version. */ +export function getExpectedPublishBranch(version: Version): string { + const versionType = getSemverVersionType(version); + + if (versionType === 'major') { + return 'master'; + } else if (versionType === 'minor') { + return `${version.major}.x`; + } else if (versionType === 'patch') { + return `${version.major}.${version.minor}.x`; + } +} + +/** Determines the type of the specified Semver version. */ +export function getSemverVersionType(version: Version): VersionType { + if (version.minor === 0 && version.patch === 0) { + return 'major'; + } else if (version.patch === 0) { + return 'minor'; + } else { + return 'patch'; + } +} diff --git a/yarn.lock b/yarn.lock index e873cdb460cf..43c6e23ab417 100644 --- a/yarn.lock +++ b/yarn.lock @@ -720,6 +720,14 @@ resolved "https://registry.yarnpkg.com/@types/hammerjs/-/hammerjs-2.0.36.tgz#17ce0a235e9ffbcdcdf5095646b374c2bf615a4c" integrity sha512-7TUK/k2/QGpEAv/BCwSHlYu3NXZhQ9ZwBYpzr9tjlPIL2C5BeGhH3DmVavRx3ZNyELX5TLC91JTz/cen6AAtIQ== +"@types/inquirer@^0.0.43": + version "0.0.43" + resolved "https://registry.yarnpkg.com/@types/inquirer/-/inquirer-0.0.43.tgz#1eb0bbb4648e6cc568bd396c1e989f620ad01273" + integrity sha512-xgyfKZVMFqE8aIKy1xfFVsX2MxyXUNgjgmbF6dRbR3sL+ZM5K4ka/9L4mmTwX8eTeVYtduyXu0gUVwVJa1HbNw== + dependencies: + "@types/rx" "*" + "@types/through" "*" + "@types/jasmine@^2.8.8": version "2.8.9" resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-2.8.9.tgz#e028c891e8551fdf6de905d959581fc4fa0b5509" @@ -815,6 +823,107 @@ "@types/gulp" "*" "@types/node" "*" +"@types/rx-core-binding@*": + version "4.0.4" + resolved "https://registry.yarnpkg.com/@types/rx-core-binding/-/rx-core-binding-4.0.4.tgz#d969d32f15a62b89e2862c17b3ee78fe329818d3" + integrity sha512-5pkfxnC4w810LqBPUwP5bg7SFR/USwhMSaAeZQQbEHeBp57pjKXRlXmqpMrLJB4y1oglR/c2502853uN0I+DAQ== + dependencies: + "@types/rx-core" "*" + +"@types/rx-core@*": + version "4.0.3" + resolved "https://registry.yarnpkg.com/@types/rx-core/-/rx-core-4.0.3.tgz#0b3354b1238cedbe2b74f6326f139dbc7a591d60" + integrity sha1-CzNUsSOM7b4rdPYybxOdvHpZHWA= + +"@types/rx-lite-aggregates@*": + version "4.0.3" + resolved "https://registry.yarnpkg.com/@types/rx-lite-aggregates/-/rx-lite-aggregates-4.0.3.tgz#6efb2b7f3d5f07183a1cb2bd4b1371d7073384c2" + integrity sha512-MAGDAHy8cRatm94FDduhJF+iNS5//jrZ/PIfm+QYw9OCeDgbymFHChM8YVIvN2zArwsRftKgE33QfRWvQk4DPg== + dependencies: + "@types/rx-lite" "*" + +"@types/rx-lite-async@*": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@types/rx-lite-async/-/rx-lite-async-4.0.2.tgz#27fbf0caeff029f41e2d2aae638b05e91ceb600c" + integrity sha512-vTEv5o8l6702ZwfAM5aOeVDfUwBSDOs+ARoGmWAKQ6LOInQ8J4/zjM7ov12fuTpktUKdMQjkeCp07Vd73mPkxw== + dependencies: + "@types/rx-lite" "*" + +"@types/rx-lite-backpressure@*": + version "4.0.3" + resolved "https://registry.yarnpkg.com/@types/rx-lite-backpressure/-/rx-lite-backpressure-4.0.3.tgz#05abb19bdf87cc740196c355e5d0b37bb50b5d56" + integrity sha512-Y6aIeQCtNban5XSAF4B8dffhIKu6aAy/TXFlScHzSxh6ivfQBQw6UjxyEJxIOt3IT49YkS+siuayM2H/Q0cmgA== + dependencies: + "@types/rx-lite" "*" + +"@types/rx-lite-coincidence@*": + version "4.0.3" + resolved "https://registry.yarnpkg.com/@types/rx-lite-coincidence/-/rx-lite-coincidence-4.0.3.tgz#80bd69acc4054a15cdc1638e2dc8843498cd85c0" + integrity sha512-1VNJqzE9gALUyMGypDXZZXzR0Tt7LC9DdAZQ3Ou/Q0MubNU35agVUNXKGHKpNTba+fr8GdIdkC26bRDqtCQBeQ== + dependencies: + "@types/rx-lite" "*" + +"@types/rx-lite-experimental@*": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@types/rx-lite-experimental/-/rx-lite-experimental-4.0.1.tgz#c532f5cbdf3f2c15da16ded8930d1b2984023cbd" + integrity sha1-xTL1y98/LBXaFt7Ykw0bKYQCPL0= + dependencies: + "@types/rx-lite" "*" + +"@types/rx-lite-joinpatterns@*": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@types/rx-lite-joinpatterns/-/rx-lite-joinpatterns-4.0.1.tgz#f70fe370518a8432f29158cc92ffb56b4e4afc3e" + integrity sha1-9w/jcFGKhDLykVjMkv+1a05K/D4= + dependencies: + "@types/rx-lite" "*" + +"@types/rx-lite-testing@*": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@types/rx-lite-testing/-/rx-lite-testing-4.0.1.tgz#21b19d11f4dfd6ffef5a9d1648e9c8879bfe21e9" + integrity sha1-IbGdEfTf1v/vWp0WSOnIh5v+Iek= + dependencies: + "@types/rx-lite-virtualtime" "*" + +"@types/rx-lite-time@*": + version "4.0.3" + resolved "https://registry.yarnpkg.com/@types/rx-lite-time/-/rx-lite-time-4.0.3.tgz#0eda65474570237598f3448b845d2696f2dbb1c4" + integrity sha512-ukO5sPKDRwCGWRZRqPlaAU0SKVxmWwSjiOrLhoQDoWxZWg6vyB9XLEZViKOzIO6LnTIQBlk4UylYV0rnhJLxQw== + dependencies: + "@types/rx-lite" "*" + +"@types/rx-lite-virtualtime@*": + version "4.0.3" + resolved "https://registry.yarnpkg.com/@types/rx-lite-virtualtime/-/rx-lite-virtualtime-4.0.3.tgz#4b30cacd0fe2e53af29f04f7438584c7d3959537" + integrity sha512-3uC6sGmjpOKatZSVHI2xB1+dedgml669ZRvqxy+WqmGJDVusOdyxcKfyzjW0P3/GrCiN4nmRkLVMhPwHCc5QLg== + dependencies: + "@types/rx-lite" "*" + +"@types/rx-lite@*": + version "4.0.5" + resolved "https://registry.yarnpkg.com/@types/rx-lite/-/rx-lite-4.0.5.tgz#b3581525dff69423798daa9a0d33c1e66a5e8c4c" + integrity sha512-KZk5XTR1dm/kNgBx8iVpjno6fRYtAUQWBOmj+O8j724+nk097sz4fOoHJNpCkOJUtHUurZlJC7QvSFCZHbkC+w== + dependencies: + "@types/rx-core" "*" + "@types/rx-core-binding" "*" + +"@types/rx@*": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@types/rx/-/rx-4.1.1.tgz#598fc94a56baed975f194574e0f572fd8e627a48" + integrity sha1-WY/JSla67ZdfGUV04PVy/Y5iekg= + dependencies: + "@types/rx-core" "*" + "@types/rx-core-binding" "*" + "@types/rx-lite" "*" + "@types/rx-lite-aggregates" "*" + "@types/rx-lite-async" "*" + "@types/rx-lite-backpressure" "*" + "@types/rx-lite-coincidence" "*" + "@types/rx-lite-experimental" "*" + "@types/rx-lite-joinpatterns" "*" + "@types/rx-lite-testing" "*" + "@types/rx-lite-time" "*" + "@types/rx-lite-virtualtime" "*" + "@types/selenium-webdriver@^3.0.0": version "3.0.12" resolved "https://registry.yarnpkg.com/@types/selenium-webdriver/-/selenium-webdriver-3.0.12.tgz#6affe5aed1ba379175075a889adbe2bc3aa62159" @@ -827,6 +936,13 @@ dependencies: "@types/node" "*" +"@types/through@*": + version "0.0.29" + resolved "https://registry.yarnpkg.com/@types/through/-/through-0.0.29.tgz#72943aac922e179339c651fa34a4428a4d722f93" + integrity sha512-9a7C5VHh+1BKblaYiq+7Tfc+EOmjMdZaD1MYtkQjSoxgB69tBjW98ry6SKsi4zEIWztLOMRuL87A3bdT/Fc/4w== + dependencies: + "@types/node" "*" + "@types/tough-cookie@*": version "2.3.3" resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-2.3.3.tgz#7f226d67d654ec9070e755f46daebf014628e9d9" @@ -1039,6 +1155,11 @@ ansi-escapes@^1.1.0: resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e" integrity sha1-06ioOzGapneTZisT52HHkRQiMG4= +ansi-escapes@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.1.0.tgz#f73207bb81207d75fd6c83f125af26eea378ca30" + integrity sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw== + ansi-gray@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/ansi-gray/-/ansi-gray-0.1.1.tgz#2962cf54ec9792c48510a3deb524436861ef7251" @@ -2107,6 +2228,11 @@ character-reference-invalid@^1.0.0: resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-1.1.2.tgz#21e421ad3d84055952dab4a43a04e73cd425d3ed" integrity sha512-7I/xceXfKyUJmSAn/jw8ve/9DyOP7XxufNYLI9Px7CmsKgEUaZLUTax6nZxGQtaoiZCjpu6cHPj20xC/vqRReQ== +chardet@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== + chokidar@2.0.4, chokidar@^2.0.0, chokidar@^2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.0.4.tgz#356ff4e2b0e8e43e322d18a372460bbcf3accd26" @@ -2211,6 +2337,13 @@ cli-cursor@^1.0.1, cli-cursor@^1.0.2: dependencies: restore-cursor "^1.0.1" +cli-cursor@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" + integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU= + dependencies: + restore-cursor "^2.0.0" + cli-spinners@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-0.1.2.tgz#bb764d88e185fb9e1e6a2a1f19772318f605e31c" @@ -3848,6 +3981,15 @@ extend@3, extend@^3.0.0, extend@^3.0.1, extend@~3.0.0, extend@~3.0.1, extend@~3. resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== +external-editor@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.0.3.tgz#5866db29a97826dbe4bf3afd24070ead9ea43a27" + integrity sha512-bn71H9+qWoOQKyZDo25mOMVpSmXROAsTJVVVYzrrtol3d4y+AsKjf4Iwl2Q+IuT0kFSQ1qo166UuIwqYq7mGnA== + dependencies: + chardet "^0.7.0" + iconv-lite "^0.4.24" + tmp "^0.0.33" + extglob@^0.3.1: version "0.3.2" resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" @@ -3959,6 +4101,13 @@ figures@^1.3.5: escape-string-regexp "^1.0.5" object-assign "^4.1.0" +figures@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" + integrity sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI= + dependencies: + escape-string-regexp "^1.0.5" + file-entry-cache@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-2.0.0.tgz#c392990c3e684783d838b8c84a45d8a048458361" @@ -5612,7 +5761,7 @@ iconv-lite@0.4.23: dependencies: safer-buffer ">= 2.1.2 < 3" -iconv-lite@0.4.24, iconv-lite@^0.4.13, iconv-lite@^0.4.4, iconv-lite@~0.4.11, iconv-lite@~0.4.13: +iconv-lite@0.4.24, iconv-lite@^0.4.13, iconv-lite@^0.4.24, iconv-lite@^0.4.4, iconv-lite@~0.4.11, iconv-lite@~0.4.13: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== @@ -5745,6 +5894,25 @@ inquirer@^0.12.0: strip-ansi "^3.0.0" through "^2.3.6" +inquirer@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.2.0.tgz#51adcd776f661369dc1e894859c2560a224abdd8" + integrity sha512-QIEQG4YyQ2UYZGDC4srMZ7BjHOmNk1lR2JQj5UknBapklm6WHA+VVH7N+sUdX3A7NeCfGF8o4X1S3Ao7nAcIeg== + dependencies: + ansi-escapes "^3.0.0" + chalk "^2.0.0" + cli-cursor "^2.1.0" + cli-width "^2.0.0" + external-editor "^3.0.0" + figures "^2.0.0" + lodash "^4.17.10" + mute-stream "0.0.7" + run-async "^2.2.0" + rxjs "^6.1.0" + string-width "^2.1.0" + strip-ansi "^4.0.0" + through "^2.3.6" + interpret@^1.0.0, interpret@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.1.0.tgz#7ed1b1410c6a0e0f78cf95d3b8440c63f78b8614" @@ -6081,7 +6249,7 @@ is-primitive@^2.0.0: resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" integrity sha1-IHurkWOEmcB7Kt8kCkGochADRXU= -is-promise@^2.1: +is-promise@^2.1, is-promise@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" integrity sha1-eaKp7OfwlugPNtKy87wWwf9L8/o= @@ -7741,7 +7909,7 @@ mute-stream@0.0.5: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.5.tgz#8fbfabb0a98a253d3184331f9e8deb7372fac6c0" integrity sha1-j7+rsKmKJT0xhDMfno3rc3L6xsA= -mute-stream@~0.0.4: +mute-stream@0.0.7, mute-stream@~0.0.4: version "0.0.7" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= @@ -8219,6 +8387,13 @@ onetime@^1.0.0: resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" integrity sha1-ofeDj4MUxRbwXs78vEzP4EtO14k= +onetime@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" + integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ= + dependencies: + mimic-fn "^1.0.0" + opn@^5.3.0: version "5.4.0" resolved "https://registry.yarnpkg.com/opn/-/opn-5.4.0.tgz#cb545e7aab78562beb11aa3bfabc7042e1761035" @@ -9772,6 +9947,14 @@ restore-cursor@^1.0.1: exit-hook "^1.0.0" onetime "^1.0.0" +restore-cursor@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" + integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368= + dependencies: + onetime "^2.0.0" + signal-exit "^3.0.2" + ret@~0.1.10: version "0.1.15" resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" @@ -9867,6 +10050,13 @@ run-async@^0.1.0: dependencies: once "^1.3.0" +run-async@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" + integrity sha1-A3GrSuC91yDUFm19/aZP96RFpsA= + dependencies: + is-promise "^2.1.0" + run-sequence@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/run-sequence/-/run-sequence-1.2.2.tgz#5095a0bebe98733b0140bd08dd80ec030ddacdeb" @@ -9880,7 +10070,7 @@ rx-lite@^3.1.2: resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102" integrity sha1-Gc5QLKVyZl87ZHsQk5+X/RYV8QI= -rxjs@6.3.3, rxjs@^6.3.3: +rxjs@6.3.3, rxjs@^6.1.0, rxjs@^6.3.3: version "6.3.3" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.3.3.tgz#3c6a7fa420e844a81390fb1158a9ec614f4bad55" integrity sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw== @@ -11133,7 +11323,7 @@ tmp@0.0.30: dependencies: os-tmpdir "~1.0.1" -tmp@0.0.33, tmp@0.0.x: +tmp@0.0.33, tmp@0.0.x, tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==