diff --git a/.vscode/launch.json b/.vscode/launch.json index 3d564342..254bb712 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -7,7 +7,7 @@ { "type": "node", "request": "launch", - "name": "Launch Program", + "name": "Run file", "program": "${file}" }, { diff --git a/src/cli.js b/src/cli.js index 73bbf0af..b3b24f3d 100755 --- a/src/cli.js +++ b/src/cli.js @@ -1,5 +1,9 @@ const github = require('./github'); -const { ensureConfigAndFoldersExists, validateConfig } = require('./configs'); +const { + ensureConfigAndFoldersExists, + validateConfig, + getRepoConfig +} = require('./configs'); const { promptRepoInfo, promptCommit, @@ -11,7 +15,7 @@ const { } = require('./cliService'); function init(config, options) { - let commit, versions, reference, owner, repoName; + let commit, versions, reference, owner, repoName, repoConfig; return ensureConfigAndFoldersExists() .then(() => validateConfig(config)) @@ -20,14 +24,13 @@ function init(config, options) { .then(({ owner: _owner, repoName: _repoName }) => { owner = _owner; repoName = _repoName; + repoConfig = getRepoConfig(owner, repoName, config.repositories); }) .then(() => promptCommit(owner, repoName, options.own ? config.username : null) ) .then(c => (commit = c)) - .then(() => - promptVersions(owner, repoName, config.repositories, options.multiple) - ) + .then(() => promptVersions(repoConfig.versions, options.multiple)) .then(v => (versions = v)) .then(() => getReference(owner, repoName, commit.sha)) .then(ref => (reference = ref)) @@ -39,7 +42,8 @@ function init(config, options) { commit, reference, versions, - username: config.username + username: config.username, + labels: repoConfig.labels }) ) .catch(handleErrors); diff --git a/src/cliService.js b/src/cliService.js index 6d409a10..daa64f91 100644 --- a/src/cliService.js +++ b/src/cliService.js @@ -15,13 +15,6 @@ const { setupRepo } = require('./git'); -function sequentially(items, handler) { - return items.reduce( - (p, item) => p.then(() => handler(item)), - Promise.resolve() - ); -} - const service = {}; service.doBackportVersions = ({ owner, @@ -29,7 +22,8 @@ service.doBackportVersions = ({ commit, reference, versions, - username + username, + labels }) => { return sequentially(versions, version => { return service @@ -39,7 +33,8 @@ service.doBackportVersions = ({ commit, reference, version, - username + username, + labels }) .then(res => console.log(`View pull request: ${res.data.html_url}\n`)) .catch(service.handleErrors); @@ -52,13 +47,12 @@ service.doBackportVersion = ({ commit, reference, version, - username + username, + labels = [] }) => { const backportBranchName = getBackportBranchName(version, reference); - console.log( - `Backporting ${service.getReferenceValue(reference)} to ${version}` - ); + console.log(`Backporting ${getReferenceLong(reference)} to ${version}`); return withSpinner( resetAndPullMaster(owner, repoName).then(() => @@ -81,7 +75,14 @@ service.doBackportVersion = ({ username ); return withSpinner( - github.createPullRequest(owner, repoName, payload), + github.createPullRequest(owner, repoName, payload).then(res => { + if (labels.length > 0) { + return github + .addLabels(owner, repoName, res.data.number, labels) + .then(() => res); + } + return res; + }), 'Creating pull request' ); }); @@ -108,9 +109,7 @@ service.promptRepoInfo = (repositories, cwd) => { console.log(`Repository: ${currentFullRepoName}`); return currentFullRepoName; } - return prompts - .listFullRepoName(fullRepoNames) - .then(({ fullRepoName }) => fullRepoName); + return prompts.listFullRepoName(fullRepoNames); }) .then(fullRepoName => { const [owner, repoName] = fullRepoName.split('/'); @@ -140,22 +139,15 @@ service.promptCommit = (owner, repoName, username) => { .then(commits => { spinner.stop(); return prompts.listCommits(commits); - }) - .then(({ commit }) => commit); + }); }; -service.promptVersions = ( - owner, - repoName, - repositories, - multipleChoice = false -) => { - const versions = getVersions(owner, repoName, repositories); +service.promptVersions = (versions, multipleChoice = false) => { if (multipleChoice) { - return prompts.checkboxVersions(versions).then(({ versions }) => versions); + return prompts.checkboxVersions(versions); } - return prompts.listVersions(versions).then(({ version }) => [version]); + return prompts.listVersions(versions); }; service.handleErrors = e => { @@ -207,15 +199,27 @@ service.handleErrors = e => { } }; -service.getReferenceValue = reference => { - return reference.type === 'pullRequest' - ? `pull request #${reference.value}` - : `commit ${reference.value}`; -}; +function sequentially(items, handler) { + return items.reduce( + (p, item) => p.then(() => handler(item)), + Promise.resolve() + ); +} + +function getReferenceValue({ type, value }, { short }) { + if (type === 'pullRequest') { + return short ? `pr-${value}` : `pull request #${value}`; + } + + return short ? `commit-${value}` : `commit ${value}`; +} -function getVersions(owner, repoName, repositories) { - return repositories.find(repo => repo.name === `${owner}/${repoName}`) - .versions; +function getReferenceLong(reference) { + return getReferenceValue(reference, { short: false }); +} + +function getReferenceShort(reference) { + return getReferenceValue(reference, { short: true }); } function isCherrypickConflict(e) { @@ -235,7 +239,7 @@ function cherrypickAndPrompt(owner, repoName, sha) { throw e; } - return prompts.confirmConflictResolved().then(({ isConflictResolved }) => { + return prompts.confirmConflictResolved().then(isConflictResolved => { if (!isConflictResolved) { const error = new Error(constants.CHERRYPICK_CONFLICT_NOT_HANDLED); error.details = e.message; @@ -246,16 +250,10 @@ function cherrypickAndPrompt(owner, repoName, sha) { } function getBackportBranchName(version, reference) { - const refValue = getReferenceValueShort(reference); + const refValue = getReferenceShort(reference); return `backport/${version}/${refValue}`; } -function getReferenceValueShort(reference) { - return reference.type === 'pullRequest' - ? `pr-${reference.value}` - : `commit-${reference.value}`; -} - function getCurrentFullRepoName(fullRepoNames, cwd) { const currentDir = path.basename(cwd); return fullRepoNames.find(name => name.endsWith(`/${currentDir}`)); @@ -263,7 +261,7 @@ function getCurrentFullRepoName(fullRepoNames, cwd) { function getPullRequestPayload(commitMessage, version, reference, username) { const backportBranchName = getBackportBranchName(version, reference); - const refValue = service.getReferenceValue(reference); + const refValue = getReferenceLong(reference); return { title: `[${version}] ${commitMessage}`, diff --git a/src/configs.js b/src/configs.js index a6184e8f..feb71b49 100644 --- a/src/configs.js +++ b/src/configs.js @@ -27,6 +27,10 @@ function ensureConfigAndFoldersExists() { }); } +function getRepoConfig(owner, repoName, repositories) { + return repositories.find(repo => repo.name === `${owner}/${repoName}`); +} + function getConfigTemplate() { return utils.readFile(path.join(__dirname, 'configTemplate.json'), 'utf8'); } @@ -63,5 +67,6 @@ function getConfig() { module.exports = { ensureConfigAndFoldersExists, getConfig, + getRepoConfig, validateConfig }; diff --git a/src/git.js b/src/git.js index 17651a81..9ce5c0ab 100644 --- a/src/git.js +++ b/src/git.js @@ -79,16 +79,11 @@ function resetAndPullMaster(owner, repoName) { ); } -function getCommit(repo, sha) { - return repo.getCommit(sha); -} - module.exports = { resetAndPullMaster, cherrypick, cloneRepo, createAndCheckoutBranch, - getCommit, repoExists, setupRepo, push diff --git a/src/github.js b/src/github.js index d9f584f5..0b7f27ec 100644 --- a/src/github.js +++ b/src/github.js @@ -55,6 +55,15 @@ function createPullRequest(owner, repoName, payload) { .catch(throwGithubError); } +function addLabels(owner, repoName, pullNumber, labels) { + return axios + .post( + `https://api.github.com/repos/${owner}/${repoName}/issues/${pullNumber}/labels?access_token=${accessToken}`, + labels + ) + .catch(throwGithubError); +} + function getPullRequestByCommit(owner, repoName, commitSha) { return axios( `https://api.github.com/search/issues?q=repo:${owner}/${repoName}+${commitSha}&access_token=${accessToken}` @@ -69,6 +78,7 @@ function setAccessToken(_accessToken) { module.exports = { setAccessToken, + addLabels, createPullRequest, getCommits, getPullRequestByCommit diff --git a/src/prompts.js b/src/prompts.js index 32f5f9a4..cedcf73a 100644 --- a/src/prompts.js +++ b/src/prompts.js @@ -1,66 +1,76 @@ const inquirer = require('inquirer'); function listFullRepoName(repoNames) { - return inquirer.prompt([ - { - type: 'list', - name: 'fullRepoName', - message: 'Select repository', - choices: repoNames - } - ]); + return inquirer + .prompt([ + { + type: 'list', + name: 'fullRepoName', + message: 'Select repository', + choices: repoNames + } + ]) + .then(({ fullRepoName }) => fullRepoName); } function listCommits(commits) { const pageSize = Math.min(10, commits.length); - return inquirer.prompt([ - { - pageSize, - type: 'list', - name: 'commit', - message: 'Select commit to backport', - choices: commits - .map(commit => ({ - name: commit.message, - value: commit, - short: commit.message - })) - .concat(commits.length > pageSize ? new inquirer.Separator() : []) - } - ]); + return inquirer + .prompt([ + { + pageSize, + type: 'list', + name: 'commit', + message: 'Select commit to backport', + choices: commits + .map(commit => ({ + name: commit.message, + value: commit, + short: commit.message + })) + .concat(commits.length > pageSize ? new inquirer.Separator() : []) + } + ]) + .then(({ commit }) => commit); } function listVersions(versions) { - return inquirer.prompt([ - { - type: 'list', - name: 'version', - message: 'Select version to backport to', - choices: versions - } - ]); + return inquirer + .prompt([ + { + type: 'list', + name: 'version', + message: 'Select version to backport to', + choices: versions + } + ]) + .then(({ version }) => [version]); } function checkboxVersions(versions) { - return inquirer.prompt([ - { - type: 'checkbox', - name: 'versions', - message: 'Select version to backport to', - choices: versions - } - ]); + return inquirer + .prompt([ + { + type: 'checkbox', + name: 'versions', + message: 'Select version to backport to', + choices: versions + } + ]) + .then(({ versions }) => versions); } function confirmConflictResolved() { - return inquirer.prompt([ - { - type: 'confirm', - name: 'isConflictResolved', - message: 'Have you solved the merge conflict?' - } - ]); + return inquirer + .prompt([ + { + type: 'confirm', + name: 'isConflictResolved', + message: 'Have you solved the merge conflict?' + } + ]) + .then(({ isConflictResolved }) => isConflictResolved); } module.exports = { diff --git a/test/cliService.test.js b/test/cliService.test.js index 32a4d7f5..1d3d9b2d 100644 --- a/test/cliService.test.js +++ b/test/cliService.test.js @@ -13,15 +13,27 @@ describe('doBackportVersion', () => { beforeEach(() => { mockBackportDirPath(); utils.exec = jest.fn().mockReturnValue(Promise.resolve()); - nock('https://api.github.com') - .post(`/repos/elastic/kibana/pulls`) + + this.addLabelMock = nock('https://api.github.com') + .post(`/repos/elastic/kibana/issues/1337/labels`, ['backport']) + .query(true) + .reply(200, {}); + }); + + it('with pull request reference', () => { + this.createPRMock = nock('https://api.github.com') + .post(`/repos/elastic/kibana/pulls`, { + title: '[6.x] myCommitMessage', + body: 'Backports pull request #myPullRequest to 6.x', + head: 'sqren:backport/6.x/pr-myPullRequest', + base: '6.x' + }) .query(true) .reply(200, { + number: 1337, html_url: 'myHtmlUrl' }); - }); - it('with pull request reference', () => { return cliService .doBackportVersion({ owner: 'elastic', @@ -29,21 +41,31 @@ describe('doBackportVersion', () => { commit: { message: 'myCommitMessage' }, reference: { type: 'pullRequest', value: 'myPullRequest' }, version: '6.x', - username: 'sqren' + username: 'sqren', + labels: ['backport'] }) .then(res => { - expect(JSON.parse(res.config.data)).toEqual({ - base: '6.x', - body: 'Backports pull request #myPullRequest to 6.x', - head: 'sqren:backport/6.x/pr-myPullRequest', - title: '[6.x] myCommitMessage' - }); expect(res.config).toMatchSnapshot(); expect(utils.exec.mock.calls).toMatchSnapshot(); + expect(this.createPRMock.isDone()).toBe(true); + expect(this.addLabelMock.isDone()).toBe(true); }); }); it('with commit reference', () => { + this.createPRMock = nock('https://api.github.com') + .post(`/repos/elastic/kibana/pulls`, { + title: '[6.x] myCommitMessage', + body: 'Backports commit myCommitSha to 6.x', + head: 'sqren:backport/6.x/commit-myCommitSha', + base: '6.x' + }) + .query(true) + .reply(200, { + number: 1337, + html_url: 'myHtmlUrl' + }); + return cliService .doBackportVersion({ owner: 'elastic', @@ -54,14 +76,10 @@ describe('doBackportVersion', () => { username: 'sqren' }) .then(res => { - expect(JSON.parse(res.config.data)).toEqual({ - base: '6.x', - body: 'Backports commit myCommitSha to 6.x', - head: 'sqren:backport/6.x/commit-myCommitSha', - title: '[6.x] myCommitMessage' - }); expect(res.config).toMatchSnapshot(); expect(utils.exec.mock.calls).toMatchSnapshot(); + expect(this.createPRMock.isDone()).toBe(true); + expect(this.addLabelMock.isDone()).toBe(false); }); }); });