Skip to content

Commit

Permalink
Merge 3f20bce into 897d427
Browse files Browse the repository at this point in the history
  • Loading branch information
sorenlouv committed Oct 18, 2019
2 parents 897d427 + 3f20bce commit 9134235
Show file tree
Hide file tree
Showing 29 changed files with 451 additions and 190 deletions.
14 changes: 8 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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 | | |
Expand Down
6 changes: 6 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
22 changes: 11 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
9 changes: 7 additions & 2 deletions src/__snapshots__/runWithOptions.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -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",
},
Expand All @@ -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",
},
Expand Down Expand Up @@ -82,6 +82,7 @@ Array [
"name": "1. Add 👻 (2e63475c) ",
"short": "Add 👻 (2e63475c)",
"value": Object {
"branch": "mySourceBranch",
"existingBackports": Array [],
"message": "Add 👻 (2e63475c)",
"pullNumber": undefined,
Expand All @@ -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,
Expand All @@ -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",
Expand All @@ -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,
Expand All @@ -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,
Expand Down
7 changes: 6 additions & 1 deletion src/options/cliArgs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
})
Expand Down Expand Up @@ -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',
Expand All @@ -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
Expand Down
128 changes: 100 additions & 28 deletions src/options/options.test.ts
Original file line number Diff line number Diff line change
@@ -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();
Expand Down
35 changes: 23 additions & 12 deletions src/options/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<PromiseReturnType<typeof getOptions>>;
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 =
Expand All @@ -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
Expand Down Expand Up @@ -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}"`;
}
Loading

0 comments on commit 9134235

Please sign in to comment.