From 515dd769a57ff30c79f1cdb36200e8f50ba0f840 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Louv-Jansen?= Date: Thu, 17 Oct 2019 13:59:37 +0200 Subject: [PATCH] Add option `sourceBranch` v4.8.0-beta.0 Set version to 4.7.3 --- README.md | 14 +- docs/configuration.md | 6 + package.json | 22 +- src/__snapshots__/runWithOptions.test.ts.snap | 9 +- src/options/cliArgs.ts | 7 +- src/options/options.test.ts | 128 ++++++++--- src/options/options.ts | 35 ++- src/runWithOptions.test.ts | 11 +- src/runWithOptions.ts | 3 - src/services/child-process-promisified.ts | 2 + src/services/git.ts | 14 +- src/services/github/Commit.d.ts | 1 + .../fetchCommitsByAuthor.test.ts.snap | 4 +- .../github/fetchCommitByPullNumber.ts | 13 +- src/services/github/fetchCommitBySha.test.ts | 1 + src/services/github/fetchCommitBySha.ts | 2 + src/services/github/fetchCommitsByAuthor.ts | 17 +- src/services/github/getDefaultRepoBranch.ts | 39 ++++ src/services/github/verifyAccessToken.ts | 4 +- src/services/logger.ts | 7 +- .../__snapshots__/integration.test.ts.snap | 30 ++- src/test/integration/createSpies.ts | 22 +- src/test/integration/integration.test.ts | 2 + src/types/Config.ts | 1 + ...herrypickAndCreatePullRequest.test.ts.snap | 12 +- src/ui/cherrypickAndCreatePullRequest.test.ts | 14 +- src/ui/cherrypickAndCreatePullRequest.ts | 15 +- src/ui/getCommitBySha.test.ts | 1 + yarn.lock | 205 ++++++++++++------ 29 files changed, 451 insertions(+), 190 deletions(-) create mode 100644 src/services/github/getDefaultRepoBranch.ts diff --git a/README.md b/README.md index ec30730d..366edff9 100644 --- a/README.md +++ b/README.md @@ -22,12 +22,13 @@ This tools is for anybody who is working on a codebase where they have to mainta `backport` is a CLI that will ask you which commit(s) to backport and to which branch(es) and then cherry-pick accordingly, and create pull requests. It will always perform the git operation in a temporary folder (`~/.backport/repositories/`) separate from your working directory, thus never interfering with any unstages changes your might have. **Features:** - - interactively backport one or more commits to one or more branches with an intuitive UI - - backport a commit by specifying a PR (`--pr 1337`) - - list and backport commits by a particular user (`--author john`) - - list and backport commits by a particular path (`--path src/plugins/chatbot`) - - see which commits have been backported and to which branches - - add a custom title, description and labels to the created backport PR + +- interactively backport one or more commits to one or more branches with an intuitive UI +- backport a commit by specifying a PR (`--pr 1337`) +- list and backport commits by a particular user (`--author john`) +- list and backport commits by a particular path (`--path src/plugins/chatbot`) +- see which commits have been backported and to which branches +- add a custom title, description and labels to the created backport PR ## Requirements @@ -109,6 +110,7 @@ The above commands will start an interactive prompt. You can use the `arrow keys | --pr | Pull request to backport | | number | | --reset-author | Set yourself as commit author | | boolean | | --sha | Sha of commit to backport | | string | +| --sourceBranch | Backport commits from a non-default branch | | string | | --upstream | Name of organization and repository | | string | | --username | Github username | | string | | --help | Show help | | | diff --git a/docs/configuration.md b/docs/configuration.md index 6f428282..e7fc1d5b 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -142,6 +142,12 @@ alias backport-skip-ci='backport --prDescription "[skip-ci]"' CLI: `--prDescription "skip-ci"` +#### `sourceBranch` + +By default the list of commits will be sourced from the repository's default branch (mostly "master"). Use `sourceBranch` to list and backport commits from other branches than the default. + +CLI: `--sourceBranch 7.x` + #### `gitHostname` Hostname for Github. diff --git a/package.json b/package.json index 926888fc..4f3fe00e 100644 --- a/package.json +++ b/package.json @@ -74,35 +74,35 @@ "lodash.isempty": "^4.4.0", "lodash.isstring": "^4.0.1", "make-dir": "^3.0.0", - "ora": "^4.0.2", + "ora": "^3.4.0", "strip-json-comments": "^3.0.1", "winston": "^3.2.1", - "yargs": "^14.0.0" + "yargs": "^14.2.0" }, "devDependencies": { "@types/core-js": "^2.5.2", "@types/inquirer": "^6.5.0", - "@types/jest": "^24.0.18", - "@types/lodash": "^4.14.141", + "@types/jest": "^24.0.19", + "@types/lodash": "^4.14.144", "@types/lodash.flatten": "^4.4.6", "@types/lodash.get": "^4.4.6", "@types/lodash.isempty": "^4.4.6", "@types/lodash.isstring": "^4.0.6", - "@types/node": "^12.7.9", + "@types/node": "^12.11.1", "@types/yargs": "^13.0.3", - "@typescript-eslint/eslint-plugin": "^2.3.2", - "@typescript-eslint/parser": "^2.3.2", + "@typescript-eslint/eslint-plugin": "^2.4.0", + "@typescript-eslint/parser": "^2.4.0", "eslint": "^6.5.1", - "eslint-config-prettier": "^6.3.0", + "eslint-config-prettier": "^6.4.0", "eslint-plugin-import": "^2.18.2", "eslint-plugin-prettier": "^3.1.1", - "husky": "^3.0.8", + "husky": "^3.0.9", "jest": "^24.9.0", - "lint-staged": "^9.4.1", + "lint-staged": "^9.4.2", "lodash": "^4.17.15", "prettier": "^1.18.2", "ts-jest": "^24.1.0", "ts-node": "^8.4.1", - "typescript": "^3.6.3" + "typescript": "^3.6.4" } } diff --git a/src/__snapshots__/runWithOptions.test.ts.snap b/src/__snapshots__/runWithOptions.test.ts.snap index 0f26ee67..6b8b153c 100644 --- a/src/__snapshots__/runWithOptions.test.ts.snap +++ b/src/__snapshots__/runWithOptions.test.ts.snap @@ -39,7 +39,7 @@ Array [ }, ], Array [ - "git fetch elastic master:master --force && git cherry-pick 2e63475c483f7844b0f2833bc57fdee32095bacb", + "git fetch elastic mySourceBranch:mySourceBranch --force && git cherry-pick 2e63475c483f7844b0f2833bc57fdee32095bacb", Object { "cwd": "/myHomeDir/.backport/repositories/elastic/kibana", }, @@ -51,7 +51,7 @@ Array [ }, ], Array [ - "git checkout master && git branch -D backport/6.x/commit-2e63475c", + "git checkout mySourceBranch && git branch -D backport/6.x/commit-2e63475c", Object { "cwd": "/myHomeDir/.backport/repositories/elastic/kibana", }, @@ -82,6 +82,7 @@ Array [ "name": "1. Add 👻 (2e63475c) ", "short": "Add 👻 (2e63475c)", "value": Object { + "branch": "mySourceBranch", "existingBackports": Array [], "message": "Add 👻 (2e63475c)", "pullNumber": undefined, @@ -92,6 +93,7 @@ Array [ "name": "2. Add witch (#85) ", "short": "Add witch (#85)", "value": Object { + "branch": "mySourceBranch", "existingBackports": Array [], "message": "Add witch (#85)", "pullNumber": 85, @@ -102,6 +104,7 @@ Array [ "name": "3. Add SF mention (#80) 6.3", "short": "Add SF mention (#80)", "value": Object { + "branch": "mySourceBranch", "existingBackports": Array [ Object { "branch": "6.3", @@ -117,6 +120,7 @@ Array [ "name": "4. Add backport config (3827bbba) ", "short": "Add backport config (3827bbba)", "value": Object { + "branch": "mySourceBranch", "existingBackports": Array [], "message": "Add backport config (3827bbba)", "pullNumber": undefined, @@ -127,6 +131,7 @@ Array [ "name": "5. Initial commit (5ea0da55) ", "short": "Initial commit (5ea0da55)", "value": Object { + "branch": "mySourceBranch", "existingBackports": Array [], "message": "Initial commit (5ea0da55)", "pullNumber": undefined, diff --git a/src/options/cliArgs.ts b/src/options/cliArgs.ts index a3a332c4..bb82eac4 100644 --- a/src/options/cliArgs.ts +++ b/src/options/cliArgs.ts @@ -15,7 +15,6 @@ export function getOptionsFromCliArgs( .wrap(Math.max(100, Math.min(120, yargs.terminalWidth()))) .option('accessToken', { alias: 'accesstoken', - default: configOptions.accessToken, description: 'Github access token', type: 'string' }) @@ -121,6 +120,11 @@ export function getOptionsFromCliArgs( description: 'Name of repository', type: 'string' }) + .option('sourceBranch', { + default: configOptions.sourceBranch, + description: `List commits to backport from another branch than master`, + type: 'string' + }) .option('username', { default: configOptions.username, description: 'Github username', @@ -140,6 +144,7 @@ export function getOptionsFromCliArgs( return { ...rest, + accessToken: cliArgs.accessToken || configOptions.accessToken, branchChoices: configOptions.branchChoices, multipleBranches: cliArgs.multipleBranches || cliArgs.multiple, multipleCommits: cliArgs.multipleCommits || cliArgs.multiple diff --git a/src/options/options.test.ts b/src/options/options.test.ts index b7af35d7..6a58a337 100644 --- a/src/options/options.test.ts +++ b/src/options/options.test.ts @@ -1,34 +1,106 @@ import { OptionsFromCliArgs } from './cliArgs'; -import { validateRequiredOptions } from './options'; - -const validOptions: OptionsFromCliArgs = { - accessToken: 'myAccessToken', - all: false, - apiHostname: 'api.github.com', - author: undefined, - backportCreatedLabels: [], - branchChoices: [], - branches: ['branchA'], - commitsCount: 10, - editor: 'code', - fork: true, - gitHostname: 'github.com', - labels: [], - multiple: false, - multipleBranches: true, - multipleCommits: false, - path: undefined, - prDescription: undefined, - prTitle: 'myPrTitle', - pullNumber: undefined, - resetAuthor: false, - sha: undefined, - upstream: 'elastic/kibana', - username: 'sqren', - verbose: false -}; +import { + validateRequiredOptions, + getOptions, + BackportOptions +} from './options'; +import axios from 'axios'; + +describe('getOptions', () => { + let axiosHeadSpy: jest.SpyInstance; + let axiosPostSpy: jest.SpyInstance; + let options: BackportOptions; + + beforeEach(async () => { + jest.clearAllMocks(); + axiosHeadSpy = jest.spyOn(axios, 'head').mockReturnValueOnce(true as any); + axiosPostSpy = jest.spyOn(axios, 'post').mockReturnValueOnce({ + data: { + data: { repository: { defaultBranchRef: { name: 'myDefaultBranch' } } } + } + } as any); + + const argv = [ + '--branch', + '6.0', + '--branch', + '6.1', + '--upstream', + 'elastic/kibana' + ]; + + options = await getOptions(argv); + }); + + it('should check whether access token is valid', () => { + expect(axiosHeadSpy).toHaveBeenCalledTimes(1); + expect(axiosHeadSpy).toHaveBeenCalledWith( + 'https://api.github.com/repos/elastic/kibana?access_token=myAccessToken' + ); + }); + + it('should get default repositry branch', () => { + expect(axiosPostSpy).toHaveBeenCalledTimes(1); + }); + + it('should return options', async () => { + expect(options).toEqual({ + accessToken: 'myAccessToken', + all: false, + apiHostname: 'api.github.com', + author: 'sqren', + backportCreatedLabels: [], + branchChoices: [ + { checked: false, name: '6.0' }, + { checked: false, name: '5.9' } + ], + branches: ['6.0', '6.1'], + fork: true, + gitHostname: 'github.com', + labels: [], + multiple: false, + multipleBranches: true, + multipleCommits: false, + prTitle: '[{baseBranch}] {commitMessages}', + repoName: 'kibana', + repoOwner: 'elastic', + resetAuthor: false, + sourceBranch: 'myDefaultBranch', + username: 'sqren', + verbose: false + }); + }); +}); describe('validateRequiredOptions', () => { + const validOptions: OptionsFromCliArgs = { + accessToken: 'myAccessToken', + all: false, + apiHostname: 'api.github.com', + author: undefined, + backportCreatedLabels: [], + branchChoices: [], + branches: ['branchA'], + commitsCount: 10, + editor: 'code', + fork: true, + gitHostname: 'github.com', + labels: [], + multiple: false, + multipleBranches: true, + multipleCommits: false, + path: undefined, + prDescription: undefined, + prTitle: 'myPrTitle', + pullNumber: undefined, + resetAuthor: false, + sha: undefined, + sourceBranch: 'mySourceBranch', + upstream: 'elastic/kibana', + username: 'sqren', + verbose: false + }; + describe('should not throw', () => { it('when all options are valid and `branches` is given', () => { expect(() => validateRequiredOptions(validOptions)).not.toThrow(); diff --git a/src/options/options.ts b/src/options/options.ts index 5397c994..965b7a09 100644 --- a/src/options/options.ts +++ b/src/options/options.ts @@ -2,14 +2,25 @@ import isEmpty from 'lodash.isempty'; import { HandledError } from '../services/HandledError'; import { PromiseReturnType } from '../types/commons'; import { getGlobalConfigPath } from '../services/env'; -import { OptionsFromCliArgs, getOptionsFromCliArgs } from './cliArgs'; +import { getOptionsFromCliArgs, OptionsFromCliArgs } from './cliArgs'; import { getOptionsFromConfigFiles } from './config/config'; +import { verifyAccessToken } from '../services/github/verifyAccessToken'; +import { getDefaultRepoBranch } from '../services/github/getDefaultRepoBranch'; export type BackportOptions = Readonly>; export async function getOptions(argv: string[]) { const optionsFromConfig = await getOptionsFromConfigFiles(); const optionsFromCli = getOptionsFromCliArgs(optionsFromConfig, argv); - return validateRequiredOptions(optionsFromCli); + const validatedOptions = validateRequiredOptions(optionsFromCli); + + await verifyAccessToken(validatedOptions); + + return { + ...validatedOptions, + sourceBranch: validatedOptions.sourceBranch + ? validatedOptions.sourceBranch + : await getDefaultRepoBranch(validatedOptions) + }; } const GLOBAL_CONFIG_DOCS_LINK = @@ -18,16 +29,6 @@ const GLOBAL_CONFIG_DOCS_LINK = const PROJECT_CONFIG_DOCS_LINK = 'https://github.com/sqren/backport/blob/f0535241177c5b343b04fe12844bfb513237d847/docs/configuration.md#project-config-backportrcjson'; -function getErrorMessage({ - field, - exampleValue -}: { - field: keyof OptionsFromCliArgs; - exampleValue: string; -}) { - return `Invalid option "${field}"\n\nYou can add it with either:\n - Config file: ".backportrc.json". Read more: ${PROJECT_CONFIG_DOCS_LINK}\n - CLI: "--${field} ${exampleValue}"`; -} - export function validateRequiredOptions({ upstream = '', ...options @@ -61,3 +62,13 @@ export function validateRequiredOptions({ author: options.author || options.username }; } + +function getErrorMessage({ + field, + exampleValue +}: { + field: keyof OptionsFromCliArgs; + exampleValue: string; +}) { + return `Invalid option "${field}"\n\nYou can add it with either:\n - Config file: ".backportrc.json". Read more: ${PROJECT_CONFIG_DOCS_LINK}\n - CLI: "--${field} ${exampleValue}"`; +} diff --git a/src/runWithOptions.test.ts b/src/runWithOptions.test.ts index e7662634..8910154e 100644 --- a/src/runWithOptions.test.ts +++ b/src/runWithOptions.test.ts @@ -12,7 +12,6 @@ describe('runWithOptions', () => { let rpcExecMock: jest.SpyInstance; let rpcExecOriginalMock: jest.SpyInstance; let inquirerPromptMock: jest.SpyInstance; - let axiosHeadSpy: jest.SpyInstance; afterEach(() => { jest.clearAllMocks(); @@ -49,6 +48,7 @@ describe('runWithOptions', () => { repoOwner: 'elastic', resetAuthor: false, sha: undefined, + sourceBranch: 'mySourceBranch', username: 'sqren', verbose: false }; @@ -70,9 +70,6 @@ describe('runWithOptions', () => { return { promptResult: args[0].choices[0].name }; }) as any); - // mock verifyAccessToken - axiosHeadSpy = jest.spyOn(axios, 'head').mockReturnValueOnce(true as any); - // mock axios post request (graphql) jest .spyOn(axios, 'post') @@ -106,12 +103,6 @@ describe('runWithOptions', () => { await runWithOptions(options); }); - it('should check whether access token is valid', () => { - expect(axiosHeadSpy).toHaveBeenCalledWith( - 'https://api.github.com/repos/elastic/kibana?access_token=myAccessToken' - ); - }); - it('getCommit should be called with correct args', () => { expect(fetchCommitsByAuthor.fetchCommitsByAuthor).toHaveBeenCalledWith( expect.objectContaining({ diff --git a/src/runWithOptions.ts b/src/runWithOptions.ts index 3e350608..437ce977 100755 --- a/src/runWithOptions.ts +++ b/src/runWithOptions.ts @@ -1,5 +1,4 @@ import { BackportOptions } from './options/options'; -import { verifyAccessToken } from './services/github/verifyAccessToken'; import { getBranches } from './ui/getBranches'; import { getCommits } from './ui/getCommits'; import { maybeSetupRepo } from './ui/maybeSetupRepo'; @@ -11,8 +10,6 @@ import { addLabelsToPullRequest } from './services/github/addLabelsToPullRequest import { withSpinner } from './ui/withSpinner'; export async function runWithOptions(options: BackportOptions) { - await verifyAccessToken(options); - const commits = await getCommits(options); const branches = await getBranches(options); diff --git a/src/services/child-process-promisified.ts b/src/services/child-process-promisified.ts index 489eee51..d6fc17a5 100644 --- a/src/services/child-process-promisified.ts +++ b/src/services/child-process-promisified.ts @@ -1,7 +1,9 @@ import child_process from 'child_process'; import { promisify } from 'util'; +import { logger } from './logger'; export const exec = (cmd: string, options: child_process.ExecOptions = {}) => { + logger.info(`exec: ${cmd}`); const execPromisified = promisify(child_process.exec); return execPromisified(cmd, { maxBuffer: 100 * 1024 * 1024, ...options }); }; diff --git a/src/services/git.ts b/src/services/git.ts index 8465f3d0..4905a04e 100644 --- a/src/services/git.ts +++ b/src/services/git.ts @@ -4,6 +4,7 @@ import { HandledError } from './HandledError'; import { stat } from './fs-promisified'; import { getRepoOwnerPath, getRepoPath } from './env'; import { execAsCallback, exec } from './child-process-promisified'; +import { CommitSelected } from './github/Commit'; async function folderExists(path: string): Promise { try { @@ -94,9 +95,9 @@ export async function addRemote(options: BackportOptions, remoteName: string) { } } -export function cherrypick(options: BackportOptions, commitSha: string) { +export function cherrypick(options: BackportOptions, commit: CommitSelected) { return exec( - `git fetch ${options.repoOwner} master:master --force && git cherry-pick ${commitSha}`, + `git fetch ${options.repoOwner} ${commit.branch}:${commit.branch} --force && git cherry-pick ${commit.sha}`, { cwd: getRepoPath(options) } @@ -152,9 +153,12 @@ export function deleteFeatureBranch( options: BackportOptions, featureBranch: string ) { - return exec(`git checkout master && git branch -D ${featureBranch}`, { - cwd: getRepoPath(options) - }); + return exec( + `git checkout ${options.sourceBranch} && git branch -D ${featureBranch}`, + { + cwd: getRepoPath(options) + } + ); } export function getRemoteName(options: BackportOptions) { diff --git a/src/services/github/Commit.d.ts b/src/services/github/Commit.d.ts index 589d7587..0eb8055f 100644 --- a/src/services/github/Commit.d.ts +++ b/src/services/github/Commit.d.ts @@ -1,5 +1,6 @@ // Commit object selected from list or via commit sha export interface CommitSelected { + branch: string; sha: string; message: string; pullNumber?: number; diff --git a/src/services/github/__snapshots__/fetchCommitsByAuthor.test.ts.snap b/src/services/github/__snapshots__/fetchCommitsByAuthor.test.ts.snap index de70be89..5140be9d 100644 --- a/src/services/github/__snapshots__/fetchCommitsByAuthor.test.ts.snap +++ b/src/services/github/__snapshots__/fetchCommitsByAuthor.test.ts.snap @@ -29,11 +29,12 @@ Array [ $repoOwner: String! $repoName: String! $commitsCount: Int! + $sourceBranch: String! $authorId: ID $historyPath: String ) { repository(owner: $repoOwner, name: $repoName) { - ref(qualifiedName: \\"master\\") { + ref(qualifiedName: $sourceBranch) { target { ... on Commit { history( @@ -100,6 +101,7 @@ Array [ "historyPath": null, "repoName": "kibana", "repoOwner": "elastic", + "sourceBranch": undefined, }, }, ] diff --git a/src/services/github/fetchCommitByPullNumber.ts b/src/services/github/fetchCommitByPullNumber.ts index 447c5a46..70a7eb5b 100644 --- a/src/services/github/fetchCommitByPullNumber.ts +++ b/src/services/github/fetchCommitByPullNumber.ts @@ -1,8 +1,8 @@ import { BackportOptions } from '../../options/options'; -import { HandledError } from '../HandledError'; import { CommitSelected } from './Commit'; import { getFormattedCommitMessage } from './commitFormatters'; import { gqlRequest } from './gqlRequest'; +import { HandledError } from '../HandledError'; export async function fetchCommitByPullNumber( options: BackportOptions & { pullNumber: number } @@ -39,13 +39,11 @@ export async function fetchCommitByPullNumber( } }); - const baseRef = res.repository.pullRequest.baseRef.name; - if (baseRef !== 'master') { - throw new HandledError( - `The pull request #${pullNumber} was merged into ${baseRef}. Only commits in master can be backported` - ); + if (res.repository.pullRequest.mergeCommit === null) { + throw new HandledError(`The PR #${pullNumber} is not merged`); } + const baseBranch = res.repository.pullRequest.baseRef.name; const sha = res.repository.pullRequest.mergeCommit.oid; const message = getFormattedCommitMessage({ message: res.repository.pullRequest.mergeCommit.message, @@ -54,6 +52,7 @@ export async function fetchCommitByPullNumber( }); return { + branch: baseBranch, sha, message, pullNumber @@ -69,7 +68,7 @@ interface DataResponse { mergeCommit: { oid: string; message: string; - }; + } | null; }; }; } diff --git a/src/services/github/fetchCommitBySha.test.ts b/src/services/github/fetchCommitBySha.test.ts index bcbf620e..a0655110 100644 --- a/src/services/github/fetchCommitBySha.test.ts +++ b/src/services/github/fetchCommitBySha.test.ts @@ -23,6 +23,7 @@ describe('fetchCommitBySha', () => { }); expect(await fetchCommitBySha({ ...options, sha: commitSha })).toEqual({ + branch: 'master', message: 'myMessage (sha12345)', pullNumber: undefined, sha: 'sha123456789' diff --git a/src/services/github/fetchCommitBySha.ts b/src/services/github/fetchCommitBySha.ts index 5e169d3a..6fe22a90 100644 --- a/src/services/github/fetchCommitBySha.ts +++ b/src/services/github/fetchCommitBySha.ts @@ -21,6 +21,7 @@ export async function fetchCommitBySha( } ); + // TODO: it should be possible to backport from other branches than master if (isEmpty(res.data.items)) { throw new HandledError(`No commit found on master with sha "${sha}"`); } @@ -34,6 +35,7 @@ export async function fetchCommitBySha( }); return { + branch: 'master', message, sha: fullSha }; diff --git a/src/services/github/fetchCommitsByAuthor.ts b/src/services/github/fetchCommitsByAuthor.ts index 6ef64426..e9b416a1 100644 --- a/src/services/github/fetchCommitsByAuthor.ts +++ b/src/services/github/fetchCommitsByAuthor.ts @@ -7,6 +7,7 @@ import { getFormattedCommitMessage } from './commitFormatters'; import { gqlRequest } from './gqlRequest'; +import { HandledError } from '../HandledError'; export async function fetchCommitsByAuthor( options: BackportOptions @@ -17,7 +18,8 @@ export async function fetchCommitsByAuthor( commitsCount, path, repoName, - repoOwner + repoOwner, + sourceBranch } = options; const query = /* GraphQL */ ` @@ -25,11 +27,12 @@ export async function fetchCommitsByAuthor( $repoOwner: String! $repoName: String! $commitsCount: Int! + $sourceBranch: String! $authorId: ID $historyPath: String ) { repository(owner: $repoOwner, name: $repoName) { - ref(qualifiedName: "master") { + ref(qualifiedName: $sourceBranch) { target { ... on Commit { history( @@ -99,12 +102,19 @@ export async function fetchCommitsByAuthor( variables: { repoOwner, repoName, + sourceBranch, commitsCount: commitsCount || 10, authorId, historyPath: path || null } }); + if (res.repository.ref === null) { + throw new HandledError( + `The upstream branch "${sourceBranch}" does not exist. Try specifying a different branch with "--sourceBranch "` + ); + } + return res.repository.ref.target.history.edges.map(edge => { const historyNode = edge.node; const associatedPullRequest = getAssociatedPullRequest( @@ -130,6 +140,7 @@ export async function fetchCommitsByAuthor( }); return { + branch: sourceBranch, sha, message, pullNumber, @@ -202,7 +213,7 @@ export interface DataResponse { edges: HistoryEdge[]; }; }; - }; + } | null; }; } diff --git a/src/services/github/getDefaultRepoBranch.ts b/src/services/github/getDefaultRepoBranch.ts new file mode 100644 index 00000000..dcaa4c51 --- /dev/null +++ b/src/services/github/getDefaultRepoBranch.ts @@ -0,0 +1,39 @@ +import { gqlRequest } from './gqlRequest'; +import { validateRequiredOptions } from '../../options/options'; + +export interface DataResponse { + repository: { + defaultBranchRef: { + name: string; + }; + }; +} + +export async function getDefaultRepoBranch({ + accessToken, + apiHostname, + repoName, + repoOwner +}: ReturnType) { + const query = /* GraphQL */ ` + query getDefaultBranch($repoOwner: String!, $repoName: String!) { + repository(owner: $repoOwner, name: $repoName) { + defaultBranchRef { + name + } + } + } + `; + + const res = await gqlRequest({ + apiHostname, + accessToken, + query, + variables: { + repoOwner, + repoName + } + }); + + return res.repository.defaultBranchRef.name; +} diff --git a/src/services/github/verifyAccessToken.ts b/src/services/github/verifyAccessToken.ts index ebdd7c6a..f1a5932a 100644 --- a/src/services/github/verifyAccessToken.ts +++ b/src/services/github/verifyAccessToken.ts @@ -1,8 +1,8 @@ import axios from 'axios'; import get from 'lodash.get'; -import { BackportOptions } from '../../options/options'; import { HandledError } from '../HandledError'; import { GithubApiError } from './GithubApiTypes'; +import { validateRequiredOptions } from '../../options/options'; function getSSOAuthUrl(error: GithubApiError) { const githubSSO: string | undefined = get( @@ -19,7 +19,7 @@ export async function verifyAccessToken({ apiHostname, repoName, repoOwner -}: BackportOptions) { +}: ReturnType) { try { return await axios.head( `https://${apiHostname}/repos/${repoOwner}/${repoName}?access_token=${accessToken}` diff --git a/src/services/logger.ts b/src/services/logger.ts index b2d58ce2..8887eeb1 100644 --- a/src/services/logger.ts +++ b/src/services/logger.ts @@ -11,6 +11,8 @@ export function consoleLog(...args: unknown[]) { console.log(...args); } +const enabledVerboseLogging = argv.verbose || argv.v; + export let logger = ({ info: () => {}, verbose: () => {} @@ -21,12 +23,11 @@ export function initLogger() { transports: [ // log to file new winston.transports.File({ - level: argv.verbose ? 'verbose' : 'info', + level: enabledVerboseLogging ? 'verbose' : 'info', format: combine( timestamp(), printf( - ({ level, message, timestamp }) => - `${timestamp} ${level}: ${formatMessage(message)}` + ({ message, timestamp }) => `${timestamp} ${formatMessage(message)}` ) ), filename: getLogfilePath() diff --git a/src/test/integration/__snapshots__/integration.test.ts.snap b/src/test/integration/__snapshots__/integration.test.ts.snap index 7b572c3b..a3dc49bd 100644 --- a/src/test/integration/__snapshots__/integration.test.ts.snap +++ b/src/test/integration/__snapshots__/integration.test.ts.snap @@ -22,11 +22,12 @@ Object { $repoOwner: String! $repoName: String! $commitsCount: Int! + $sourceBranch: String! $authorId: ID $historyPath: String ) { repository(owner: $repoOwner, name: $repoName) { - ref(qualifiedName: \\"master\\") { + ref(qualifiedName: $sourceBranch) { target { ... on Commit { history( @@ -93,6 +94,7 @@ Object { "historyPath": null, "repoName": "backport-demo", "repoOwner": "elastic", + "sourceBranch": "master", }, } `; @@ -109,6 +111,24 @@ Object { `; exports[`when a single commit is backported should make correct API requests 1`] = ` +Object { + "query": " + query getDefaultBranch($repoOwner: String!, $repoName: String!) { + repository(owner: $repoOwner, name: $repoName) { + defaultBranchRef { + name + } + } + } + ", + "variables": Object { + "repoName": "backport-demo", + "repoOwner": "elastic", + }, +} +`; + +exports[`when a single commit is backported should make correct API requests 2`] = ` Object { "query": " query getIdByLogin($login: String!) { @@ -123,18 +143,19 @@ Object { } `; -exports[`when a single commit is backported should make correct API requests 2`] = ` +exports[`when a single commit is backported should make correct API requests 3`] = ` Object { "query": " query getCommitsByAuthorQuery( $repoOwner: String! $repoName: String! $commitsCount: Int! + $sourceBranch: String! $authorId: ID $historyPath: String ) { repository(owner: $repoOwner, name: $repoName) { - ref(qualifiedName: \\"master\\") { + ref(qualifiedName: $sourceBranch) { target { ... on Commit { history( @@ -201,11 +222,12 @@ Object { "historyPath": null, "repoName": "backport-demo", "repoOwner": "elastic", + "sourceBranch": "master", }, } `; -exports[`when a single commit is backported should make correct API requests 3`] = ` +exports[`when a single commit is backported should make correct API requests 4`] = ` Object { "base": "6.0", "body": "Backports the following commits to 6.0: diff --git a/src/test/integration/createSpies.ts b/src/test/integration/createSpies.ts index d5093e9d..71066d66 100644 --- a/src/test/integration/createSpies.ts +++ b/src/test/integration/createSpies.ts @@ -37,6 +37,13 @@ export function createSpies({ commitCount }: { commitCount: number }) { const axiosPostSpy = jest .spyOn(axios, 'post') + // mock getDefaultRepoBranch + .mockReturnValueOnce({ + data: { + data: { repository: { defaultBranchRef: { name: 'master' } } } + } + } as any) + // mock author id .mockReturnValueOnce({ data: { @@ -56,12 +63,7 @@ export function createSpies({ commitCount }: { commitCount: number }) { } as any) // mock create pull request - .mockReturnValueOnce({ - data: { - html_url: 'pull request url', - number: 1337 - } - } as any); + .mockReturnValueOnce({ data: {} } as any); // mock prompt jest @@ -82,12 +84,18 @@ export function createSpies({ commitCount }: { commitCount: number }) { return { getAxiosCalls: () => { const [ + getDefaultRepoBranch, getAuthorPayload, getCommitsPayload, createPullRequestPayload ] = axiosPostSpy.mock.calls.map(call => call[1]); - return { getAuthorPayload, getCommitsPayload, createPullRequestPayload }; + return { + getDefaultRepoBranch, + getAuthorPayload, + getCommitsPayload, + createPullRequestPayload + }; } }; } diff --git a/src/test/integration/integration.test.ts b/src/test/integration/integration.test.ts index 8c9cde09..e8e01864 100644 --- a/src/test/integration/integration.test.ts +++ b/src/test/integration/integration.test.ts @@ -35,11 +35,13 @@ describe('when a single commit is backported', () => { it('should make correct API requests', () => { const { + getDefaultRepoBranch, getAuthorPayload, getCommitsPayload, createPullRequestPayload } = spies.getAxiosCalls(); + expect(getDefaultRepoBranch).toMatchSnapshot(); expect(getAuthorPayload).toMatchSnapshot(); expect(getCommitsPayload).toMatchSnapshot(); expect(createPullRequestPayload).toMatchSnapshot(); diff --git a/src/types/Config.ts b/src/types/Config.ts index e0827f2b..a9f8aa5e 100644 --- a/src/types/Config.ts +++ b/src/types/Config.ts @@ -13,6 +13,7 @@ export interface Config { branches?: (string | BranchChoice)[]; upstream?: string; fork?: boolean; + sourceBranch?: string; // both all?: boolean; diff --git a/src/ui/__snapshots__/cherrypickAndCreatePullRequest.test.ts.snap b/src/ui/__snapshots__/cherrypickAndCreatePullRequest.test.ts.snap index 2286998e..cc9996e2 100644 --- a/src/ui/__snapshots__/cherrypickAndCreatePullRequest.test.ts.snap +++ b/src/ui/__snapshots__/cherrypickAndCreatePullRequest.test.ts.snap @@ -9,7 +9,7 @@ Array [ }, ], Array [ - "git fetch elastic master:master --force && git cherry-pick mySha", + "git fetch elastic 7.x:7.x --force && git cherry-pick mySha", Object { "cwd": "/myHomeDir/.backport/repositories/elastic/kibana", }, @@ -26,7 +26,7 @@ Array [ }, ], Array [ - "git fetch elastic master:master --force && git cherry-pick mySha", + "git fetch elastic 7.x:7.x --force && git cherry-pick mySha", Object { "cwd": "/myHomeDir/.backport/repositories/elastic/kibana", }, @@ -44,7 +44,7 @@ Array [ }, ], Array [ - "git checkout master && git branch -D backport/6.x/commit-mySha", + "git checkout myDefaultRepoBaseBranch && git branch -D backport/6.x/commit-mySha", Object { "cwd": "/myHomeDir/.backport/repositories/elastic/kibana", }, @@ -61,13 +61,13 @@ Array [ }, ], Array [ - "git fetch elastic master:master --force && git cherry-pick mySha", + "git fetch elastic 7.x:7.x --force && git cherry-pick mySha", Object { "cwd": "/myHomeDir/.backport/repositories/elastic/kibana", }, ], Array [ - "git fetch elastic master:master --force && git cherry-pick mySha2", + "git fetch elastic 7.x:7.x --force && git cherry-pick mySha2", Object { "cwd": "/myHomeDir/.backport/repositories/elastic/kibana", }, @@ -79,7 +79,7 @@ Array [ }, ], Array [ - "git checkout master && git branch -D backport/6.x/pr-1000_pr-2000", + "git checkout myDefaultRepoBaseBranch && git branch -D backport/6.x/pr-1000_pr-2000", Object { "cwd": "/myHomeDir/.backport/repositories/elastic/kibana", }, diff --git a/src/ui/cherrypickAndCreatePullRequest.test.ts b/src/ui/cherrypickAndCreatePullRequest.test.ts index 9a6f1460..30d49291 100644 --- a/src/ui/cherrypickAndCreatePullRequest.test.ts +++ b/src/ui/cherrypickAndCreatePullRequest.test.ts @@ -41,16 +41,19 @@ describe('cherrypickAndCreatePullRequest', () => { prTitle: '[{baseBranch}] {commitMessages}', repoName: 'kibana', repoOwner: 'elastic', - username: 'sqren' + username: 'sqren', + sourceBranch: 'myDefaultRepoBaseBranch' } as BackportOptions; const commits = [ { + branch: '7.x', sha: 'mySha', message: 'myCommitMessage (#1000)', pullNumber: 1000 }, { + branch: '7.x', sha: 'mySha2', message: 'myOtherCommitMessage (#2000)', pullNumber: 2000 @@ -112,7 +115,9 @@ myPrSuffix` await cherrypickAndCreatePullRequest({ options, - commits: [{ sha: 'mySha', message: 'myCommitMessage (mySha)' }], + commits: [ + { branch: '7.x', sha: 'mySha', message: 'myCommitMessage (mySha)' } + ], baseBranch: '6.x' }); }); @@ -167,12 +172,13 @@ myPrSuffix` prTitle: '[{baseBranch}] {commitMessages}', repoName: 'kibana', repoOwner: 'elastic', - username: 'sqren' + username: 'sqren', + sourceBranch: 'myDefaultRepoBaseBranch' } as BackportOptions; const promise = cherrypickAndCreatePullRequest({ options, - commits: [{ sha: 'mySha', message: 'myCommitMessage' }], + commits: [{ branch: '7.x', sha: 'mySha', message: 'myCommitMessage' }], baseBranch: '6.x' }); diff --git a/src/ui/cherrypickAndCreatePullRequest.ts b/src/ui/cherrypickAndCreatePullRequest.ts index 8e1ab1e1..735af46b 100644 --- a/src/ui/cherrypickAndCreatePullRequest.ts +++ b/src/ui/cherrypickAndCreatePullRequest.ts @@ -45,9 +45,7 @@ export async function cherrypickAndCreatePullRequest({ createFeatureBranch(options, baseBranch, featureBranch) ); - await sequentially(commits, commit => - cherrypickAndConfirm(options, commit.sha) - ); + await sequentially(commits, commit => cherrypickAndConfirm(options, commit)); if (options.resetAuthor) { await withSpinner( @@ -88,10 +86,15 @@ function getFeatureBranchName(baseBranch: string, commits: CommitSelected[]) { return `backport/${baseBranch}/${refValues}`; } -async function cherrypickAndConfirm(options: BackportOptions, sha: string) { - const spinner = ora(`Cherry-picking commit ${getShortSha(sha)}`).start(); +async function cherrypickAndConfirm( + options: BackportOptions, + commit: CommitSelected +) { + const spinner = ora( + `Cherry-picking commit ${getShortSha(commit.sha)}` + ).start(); try { - await cherrypick(options, sha); + await cherrypick(options, commit); spinner.succeed(); } catch (e) { const repoPath = getRepoPath(options); diff --git a/src/ui/getCommitBySha.test.ts b/src/ui/getCommitBySha.test.ts index dbe8365d..11d17dad 100644 --- a/src/ui/getCommitBySha.test.ts +++ b/src/ui/getCommitBySha.test.ts @@ -17,6 +17,7 @@ describe('getCommitBySha', () => { } as BackportOptions & { sha: string }); expect(commit).toEqual({ + branch: 'master', message: '[Chrome] Bootstrap Angular into document.body (myCommit)', sha: 'myCommitSha', pullNumber: undefined diff --git a/yarn.lock b/yarn.lock index e0c4d464..137d5922 100644 --- a/yarn.lock +++ b/yarn.lock @@ -404,10 +404,10 @@ resolved "https://registry.yarnpkg.com/@types/jest-diff/-/jest-diff-20.0.1.tgz#35cc15b9c4f30a18ef21852e255fdb02f6d59b89" integrity sha512-yALhelO3i0hqZwhjtcr6dYyaLoCHbAMshwtj6cGxTvHZAKXHsYGdff6E8EPw3xLKY0ELUTQ69Q1rQiJENnccMA== -"@types/jest@^24.0.18": - version "24.0.18" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-24.0.18.tgz#9c7858d450c59e2164a8a9df0905fc5091944498" - integrity sha512-jcDDXdjTcrQzdN06+TSVsPPqxvsZA/5QkYfIZlq1JMw7FdP5AZylbOc+6B/cuDurctRe+MziUMtQ3xQdrbjqyQ== +"@types/jest@^24.0.19": + version "24.0.19" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-24.0.19.tgz#f7036058d2a5844fe922609187c0ad8be430aff5" + integrity sha512-YYiqfSjocv7lk5H/T+v5MjATYjaTMsUkbDnjGqSMoO88jWdtJXJV4ST/7DKZcoMHMBvB2SeSfyOzZfkxXHR5xg== dependencies: "@types/jest-diff" "*" @@ -444,21 +444,31 @@ dependencies: "@types/lodash" "*" -"@types/lodash@*", "@types/lodash@^4.14.141": +"@types/lodash@*": version "4.14.141" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.141.tgz#d81f4d0c562abe28713406b571ffb27692a82ae6" integrity sha512-v5NYIi9qEbFEUpCyikmnOYe4YlP8BMUdTcNCAquAKzu+FA7rZ1onj9x80mbnDdOW/K5bFf3Tv5kJplP33+gAbQ== +"@types/lodash@^4.14.144": + version "4.14.144" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.144.tgz#12e57fc99064bce45e5ab3c8bc4783feb75eab8e" + integrity sha512-ogI4g9W5qIQQUhXAclq6zhqgqNUr7UlFaqDHbch7WLSLeeM/7d3CRaw7GLajxvyFvhJqw4Rpcz5bhoaYtIx6Tg== + "@types/minimatch@*": version "3.0.3" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== -"@types/node@*", "@types/node@^12.7.9": +"@types/node@*": version "12.7.9" resolved "https://registry.yarnpkg.com/@types/node/-/node-12.7.9.tgz#da0210f91096aa67138cf5afd04c4d629f8a406a" integrity sha512-P57oKTJ/vYivL2BCfxCC5tQjlS8qW31pbOL6qt99Yrjm95YdHgNZwjrTTjMBh+C2/y6PXIX4oz253+jUzxKKfQ== +"@types/node@^12.11.1": + version "12.11.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.11.1.tgz#1fd7b821f798b7fa29f667a1be8f3442bb8922a3" + integrity sha512-TJtwsqZ39pqcljJpajeoofYRfeZ7/I/OMUQ5pR4q5wOKf2ocrUvBAZUMhWsOvKx3dVc/aaV5GluBivt0sWqA5A== + "@types/normalize-package-data@^2.4.0": version "2.4.0" resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e" @@ -488,41 +498,42 @@ dependencies: "@types/yargs-parser" "*" -"@typescript-eslint/eslint-plugin@^2.3.2": - version "2.3.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.3.2.tgz#7e112ca0bb29044d915baf10163a8199a20f7c69" - integrity sha512-tcnpksq1bXzcIRbYLeXkgp6l+ggEMXXUcl1wsSvL807fRtmvVQKygElwEUf4hBA76dNag3VAK1q2m3vd7qJaZA== +"@typescript-eslint/eslint-plugin@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.4.0.tgz#aaf6b542ff75b78f4191a8bf1c519184817caa24" + integrity sha512-se/YCk7PUoyMwSm/u3Ii9E+BgDUc736uw/lXCDpXEqRgPGsoBTtS8Mntue/vZX8EGyzGplYuePBuVyhZDM9EpQ== dependencies: - "@typescript-eslint/experimental-utils" "2.3.2" + "@typescript-eslint/experimental-utils" "2.4.0" eslint-utils "^1.4.2" functional-red-black-tree "^1.0.1" regexpp "^2.0.1" tsutils "^3.17.1" -"@typescript-eslint/experimental-utils@2.3.2": - version "2.3.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.3.2.tgz#e50f31264507e6fec7b33840bb6af260c24f4ea8" - integrity sha512-t+JGdTT6dRbmvKDlhlVkEueoZa0fhJNfG6z2cpnRPLwm3VwYr2BjR//acJGC1Yza0I9ZNcDfRY7ubQEvvfG6Jg== +"@typescript-eslint/experimental-utils@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.4.0.tgz#dd8f3f466be25c3610a06fed22cfb6e6aa17f6d9" + integrity sha512-2cvhNaJoWavgTtnC7e1jUSPZQ7e4U2X9Yoy5sQmkS7lTESuyuZrlRcaoNuFfYEd6hgrmMU7+QoSp8Ad+kT1nfA== dependencies: "@types/json-schema" "^7.0.3" - "@typescript-eslint/typescript-estree" "2.3.2" + "@typescript-eslint/typescript-estree" "2.4.0" eslint-scope "^5.0.0" -"@typescript-eslint/parser@^2.3.2": - version "2.3.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.3.2.tgz#e9b742e191cd1209930da469cde379591ad0af5b" - integrity sha512-nq1UQeNGdKdqdgF6Ww+Ov2OidWgiL96+JYdXXZ2rkP/OWyc6KMNSbs6MpRCpI8q+PmDa7hBnHNQIo7w/drYccA== +"@typescript-eslint/parser@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.4.0.tgz#fe43ed5fec14af03d3594fce2c3b7ec4c8df0243" + integrity sha512-IouAKi/grJ4MFrwdXIJ1GHAwbPWYgkT3b/x8Q49F378c9nwgxVkO76e0rZeUVpwHMaUuoKG2sUeK0XGkwdlwkw== dependencies: "@types/eslint-visitor-keys" "^1.0.0" - "@typescript-eslint/experimental-utils" "2.3.2" - "@typescript-eslint/typescript-estree" "2.3.2" + "@typescript-eslint/experimental-utils" "2.4.0" + "@typescript-eslint/typescript-estree" "2.4.0" eslint-visitor-keys "^1.1.0" -"@typescript-eslint/typescript-estree@2.3.2": - version "2.3.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.3.2.tgz#107414aa04e689fe6f7251eb63fb500217f2b7f4" - integrity sha512-eZNEAai16nwyhIVIEaWQlaUgAU3S9CkQ58qvK0+3IuSdLJD3W1PNuehQFMIhW/mTP1oFR9GNoTcLg7gtXz6lzA== +"@typescript-eslint/typescript-estree@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.4.0.tgz#722c95493e1b7682893edaaaec0e69f36917feef" + integrity sha512-/DzDAtMqF5d9IlXrrvu/Id/uoKjnSxf/3FbtKK679a/T7lbDM8qQuirtGvFy6Uh+x0hALuCMwnMfUf0P24/+Iw== dependencies: + chokidar "^3.0.2" glob "^7.1.4" is-glob "^4.0.1" lodash.unescape "4.0.1" @@ -641,6 +652,14 @@ anymatch@^2.0.0: micromatch "^3.1.4" normalize-path "^2.1.1" +anymatch@~3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142" + integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + aproba@^1.0.3: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" @@ -829,6 +848,11 @@ bcrypt-pbkdf@^1.0.0: dependencies: tweetnacl "^0.14.3" +binary-extensions@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.0.0.tgz#23c0df14f6a88077f5f986c0d167ec03c3d5537c" + integrity sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow== + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -853,7 +877,7 @@ braces@^2.3.1: split-string "^3.0.2" to-regex "^3.0.1" -braces@^3.0.1: +braces@^3.0.1, braces@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== @@ -977,6 +1001,21 @@ chardet@^0.7.0: resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== +chokidar@^3.0.2: + version "3.2.1" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.2.1.tgz#4634772a1924512d990d4505957bf3a510611387" + integrity sha512-/j5PPkb5Feyps9e+jo07jUZGvkB5Aj953NrI4s8xSVScrAo/RHeILrtdb4uzR7N6aaFFxxJ+gt8mA8HfNpw76w== + dependencies: + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.1.3" + optionalDependencies: + fsevents "~2.1.0" + chownr@^1.1.1: version "1.1.3" resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.3.tgz#42d837d5239688d55f303003a508230fa6727142" @@ -1016,7 +1055,7 @@ cli-cursor@^3.1.0: dependencies: restore-cursor "^3.1.0" -cli-spinners@^2.2.0: +cli-spinners@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.2.0.tgz#e8b988d9206c692302d8ee834e7a85c0144d8f77" integrity sha512-tgU3fKwzYjiLEQgPMD9Jt+JjHVL9kW93FiIMX/l7rivvOD4/LL0Mf7gda3+4U2KJBloybwgj5KEoQgGRioMiKQ== @@ -1482,10 +1521,10 @@ escodegen@^1.9.1: optionalDependencies: source-map "~0.6.1" -eslint-config-prettier@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.3.0.tgz#e73b48e59dc49d950843f3eb96d519e2248286a3" - integrity sha512-EWaGjlDAZRzVFveh2Jsglcere2KK5CJBhkNSa1xs3KfMUGdRiT7lG089eqPdvlzWHpAqaekubOsOMu8W8Yk71A== +eslint-config-prettier@^6.4.0: + version "6.4.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.4.0.tgz#0a04f147e31d33c6c161b2dd0971418ac52d0477" + integrity sha512-YrKucoFdc7SEko5Sxe4r6ixqXPDP1tunGw91POeZTTRKItf/AMFYt/YLEQtZMkR2LVpAVhcAcZgcWpm1oGPW7w== dependencies: get-stdin "^6.0.0" @@ -1942,6 +1981,11 @@ fsevents@^1.2.7: nan "^2.12.1" node-pre-gyp "^0.12.0" +fsevents@~2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.1.tgz#74c64e21df71721845d0c44fe54b7f56b82995a9" + integrity sha512-4FRPXWETxtigtJW/gxzEDsX1LVbPAM93VleB83kZB+ellqbHMkyt2aJfuzNLRvFPnGi6bcE5SvfxgbXPeKteJw== + function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" @@ -2012,7 +2056,7 @@ getpass@^0.1.1: dependencies: assert-plus "^1.0.0" -glob-parent@^5.0.0: +glob-parent@^5.0.0, glob-parent@~5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.0.tgz#5f4c1d1e748d30cd73ad2944b3577a81b081e8c2" integrity sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw== @@ -2165,20 +2209,20 @@ http-signature@~1.2.0: jsprim "^1.2.2" sshpk "^1.7.0" -husky@^3.0.8: - version "3.0.8" - resolved "https://registry.yarnpkg.com/husky/-/husky-3.0.8.tgz#8de3fed26ce9b43034ef51013c4ad368b6b74ea8" - integrity sha512-HFOsgcyrX3qe/rBuqyTt+P4Gxn5P0seJmr215LAZ/vnwK3jWB3r0ck7swbzGRUbufCf9w/lgHPVbF/YXQALgfQ== +husky@^3.0.9: + version "3.0.9" + resolved "https://registry.yarnpkg.com/husky/-/husky-3.0.9.tgz#a2c3e9829bfd6b4957509a9500d2eef5dbfc8044" + integrity sha512-Yolhupm7le2/MqC1VYLk/cNmYxsSsqKkTyBhzQHhPK1jFnC89mmmNVuGtLNabjDI6Aj8UNIr0KpRNuBkiC4+sg== dependencies: chalk "^2.4.2" + ci-info "^2.0.0" cosmiconfig "^5.2.1" execa "^1.0.0" get-stdin "^7.0.0" - is-ci "^2.0.0" opencollective-postinstall "^2.0.2" pkg-dir "^4.2.0" please-upgrade-node "^3.2.0" - read-pkg "^5.1.1" + read-pkg "^5.2.0" run-node "^1.0.0" slash "^3.0.0" @@ -2327,6 +2371,13 @@ is-arrayish@^0.3.1: resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + is-buffer@^1.1.5: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" @@ -2430,18 +2481,13 @@ is-generator-fn@^2.0.0: resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== -is-glob@^4.0.0, is-glob@^4.0.1: +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== dependencies: is-extglob "^2.1.1" -is-interactive@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" - integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== - is-number@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" @@ -3114,10 +3160,10 @@ lines-and-columns@^1.1.6: resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= -lint-staged@^9.4.1: - version "9.4.1" - resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-9.4.1.tgz#60c0f85745bd398e6460aa7f5adb3cad3a2b862c" - integrity sha512-zFRbo1bAJEVf1m33paTTjDVfy2v3lICCqHfmQSgNoI+lWpi7HPG5y/R2Y7Whdce+FKxlZYs/U1sDSx8+nmQdDA== +lint-staged@^9.4.2: + version "9.4.2" + resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-9.4.2.tgz#14cb577a9512f520691f8b5aefce6a8f7ead6c04" + integrity sha512-OFyGokJSWTn2M6vngnlLXjaHhi8n83VIZZ5/1Z26SULRUWgR3ITWpAEQC9Pnm3MC/EpCxlwts/mQWDHNji2+zA== dependencies: chalk "^2.4.2" commander "^2.20.0" @@ -3268,6 +3314,13 @@ log-symbols@^1.0.2: dependencies: chalk "^1.0.0" +log-symbols@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" + integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg== + dependencies: + chalk "^2.0.1" + log-symbols@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-3.0.0.tgz#f3a08516a5dea893336a7dee14d18a1cfdab77c4" @@ -3580,7 +3633,7 @@ normalize-path@^2.1.1: dependencies: remove-trailing-separator "^1.0.1" -normalize-path@^3.0.0: +normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== @@ -3744,16 +3797,15 @@ optionator@^0.8.1, optionator@^0.8.2: type-check "~0.3.2" wordwrap "~1.0.0" -ora@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/ora/-/ora-4.0.2.tgz#0e1e68fd45b135d28648b27cf08081fa6e8a297d" - integrity sha512-YUOZbamht5mfLxPmk4M35CD/5DuOkAacxlEUbStVXpBAt4fyhBf+vZHI/HRkI++QUp3sNoeA2Gw4C+hi4eGSig== +ora@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/ora/-/ora-3.4.0.tgz#bf0752491059a3ef3ed4c85097531de9fdbcd318" + integrity sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg== dependencies: chalk "^2.4.2" - cli-cursor "^3.1.0" - cli-spinners "^2.2.0" - is-interactive "^1.0.0" - log-symbols "^3.0.0" + cli-cursor "^2.1.0" + cli-spinners "^2.0.0" + log-symbols "^2.2.0" strip-ansi "^5.2.0" wcwidth "^1.0.1" @@ -3950,7 +4002,7 @@ performance-now@^2.1.0: resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= -picomatch@^2.0.5: +picomatch@^2.0.4, picomatch@^2.0.5: version "2.0.7" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.0.7.tgz#514169d8c7cd0bdbeecc8a2609e34a7163de69f6" integrity sha512-oLHIdio3tZ0qH76NybpeneBhYVj0QFTfXEFTc/B3zKQspYfYYkWYgFsmzo+4kvId/bQRcNkVeguI3y+CD22BtA== @@ -4137,7 +4189,7 @@ read-pkg@^3.0.0: normalize-package-data "^2.3.2" path-type "^3.0.0" -read-pkg@^5.1.1: +read-pkg@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg== @@ -4169,6 +4221,13 @@ readable-stream@^3.1.1: string_decoder "^1.1.1" util-deprecate "^1.0.1" +readdirp@~3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.1.3.tgz#d6e011ed5b9240a92f08651eeb40f7942ceb6cc1" + integrity sha512-ZOsfTGkjO2kqeR5Mzr5RYDbTGYneSkdNKX2fOX2P5jF7vMrd/GNnIAUtDldeHHumHUCQ3V05YfWUdxMPAsRu9Q== + dependencies: + picomatch "^2.0.4" + realpath-native@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-1.1.0.tgz#2003294fea23fb0672f2476ebe22fcf498a2d65c" @@ -4996,10 +5055,10 @@ type-fest@^0.6.0: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== -typescript@^3.6.3: - version "3.6.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.6.3.tgz#fea942fabb20f7e1ca7164ff626f1a9f3f70b4da" - integrity sha512-N7bceJL1CtRQ2RiG0AQME13ksR7DiuQh/QehubYcghzv20tnh+MQnQIuJddTmsbqYj+dztchykemz0zFzlvdQw== +typescript@^3.6.4: + version "3.6.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.6.4.tgz#b18752bb3792bc1a0281335f7f6ebf1bbfc5b91d" + integrity sha512-unoCll1+l+YK4i4F8f22TaNVPRHcD9PA3yCuZ8g5e0qGqlVlJ/8FSateOLLSagn+Yg5+ZwuPkL8LFUc0Jcvksg== uglify-js@^3.1.4: version "3.6.0" @@ -5267,6 +5326,14 @@ yargs-parser@^13.1.1: camelcase "^5.0.0" decamelize "^1.2.0" +yargs-parser@^15.0.0: + version "15.0.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-15.0.0.tgz#cdd7a97490ec836195f59f3f4dbe5ea9e8f75f08" + integrity sha512-xLTUnCMc4JhxrPEPUYD5IBR1mWCK/aT6+RJ/K29JY2y1vD+FhtgKK0AXRWvI262q3QSffAQuTouFIKUuHX89wQ== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + yargs@^13.3.0: version "13.3.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.0.tgz#4c657a55e07e5f2cf947f8a366567c04a0dedc83" @@ -5283,10 +5350,10 @@ yargs@^13.3.0: y18n "^4.0.0" yargs-parser "^13.1.1" -yargs@^14.0.0: - version "14.0.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-14.0.0.tgz#ba4cacc802b3c0b3e36a9e791723763d57a85066" - integrity sha512-ssa5JuRjMeZEUjg7bEL99AwpitxU/zWGAGpdj0di41pOEmJti8NR6kyUIJBkR78DTYNPZOU08luUo0GTHuB+ow== +yargs@^14.2.0: + version "14.2.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-14.2.0.tgz#f116a9242c4ed8668790b40759b4906c276e76c3" + integrity sha512-/is78VKbKs70bVZH7w4YaZea6xcJWOAwkhbR0CFuZBmYtfTYF0xjGJF43AYd8g2Uii1yJwmS5GR2vBmrc32sbg== dependencies: cliui "^5.0.0" decamelize "^1.2.0" @@ -5298,7 +5365,7 @@ yargs@^14.0.0: string-width "^3.0.0" which-module "^2.0.0" y18n "^4.0.0" - yargs-parser "^13.1.1" + yargs-parser "^15.0.0" yn@^3.0.0: version "3.1.1"