Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add reading of PR title and body from branch config #15

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Now whenever you have a chain of branches, list them in `.pr-train.yml` to tell

**Pre-requisite**: Create a `${HOME}/.pr-train` file with a single line which is your GH personal access token (you can create one [here](https://github.com/settings/tokens)). The `repo` scope, with ` Full control of private repositories` is needed.

Run `git pr-train -p --create-prs` to create GitHub PRs with a "content table" section. PR titles are taken from the commit message titles of each branch HEAD. You'll be promted before the PRs are created.
Run `git pr-train -p --create-prs` to create GitHub PRs with a "content table" section. PR titles and descriptions are taken from `title` and `body` values of branch configs in `.yml` file detailed further down, or from commit message titles and bodies of each branch HEAD. You'll be promted before the PRs are created.

If you run with `--create-prs` again, `pr-train` will only override the Table of Contents in your PR, it will _not_ change the rest of the PR descriptions.

Expand Down Expand Up @@ -60,6 +60,8 @@ trains:
big billing refactoring:
- fred_billing-refactor_frontend_bits
- fred_billing-refactor_backend_bits
title: BE changes for billing refactor # optional PR title
body: This refactor... # optional PR description
- fred_billing-refactor_tests
#
# ...config for older trains follows...
Expand Down
47 changes: 33 additions & 14 deletions github.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,29 @@ const colors = require('colors');
const emoji = require('node-emoji');
const simpleGit = require('simple-git/promise');

/**
* @typedef {string | Object.<string, { title?: string, body?: string, combined: boolean, initSha?: string }>} BranchCfg
*/

/**
*
* @param {simpleGit.SimpleGit} sg
* @param {string} branch
* @param {string} branchName
* @param {Object.<string, BranchCfg>} cfgMap
* @return Promise.<{title: string, body: string}>
*/
async function constructPrMsg(sg, branch) {
const title = await sg.raw(['log', '--format=%s', '-n', '1', branch]);
const body = await sg.raw(['log', '--format=%b', '-n', '1', branch]);
async function constructPrMsg(sg, branchName, cfgMap) {
let title = await sg.raw(['log', '--format=%s', '-n', '1', branchName]);
let body = await sg.raw(['log', '--format=%b', '-n', '1', branchName]);

// Take PR title and body from the config if title exists there
if (typeof cfgMap[branchName] !== 'string' && cfgMap[branchName].title) {
title = cfgMap[branchName].title;
// If body was not specified in config, but title was, then body from
// git log is no longer needed.
body = cfgMap[branchName].body || '';
}

return {
title: title.trim(),
body: body.trim(),
Expand Down Expand Up @@ -79,9 +93,10 @@ function upsertNavigationInBody(newNavigation, body) {
* @param {simpleGit.SimpleGit} sg
* @param {Array.<string>} allBranches
* @param {string} combinedBranch
* @param {Object.<string, BranchCfg>} cfgMap
* @param {string} remote
*/
async function ensurePrsExist(sg, allBranches, combinedBranch, remote = DEFAULT_REMOTE) {
async function ensurePrsExist(sg, allBranches, combinedBranch, cfgMap, remote = DEFAULT_REMOTE) {
//const allBranches = combinedBranch ? sortedBranches.concat(combinedBranch) : sortedBranches;
const octoClient = octo.client(readGHKey());
// TODO: take remote name from `-r` value.
Expand All @@ -94,12 +109,16 @@ async function ensurePrsExist(sg, allBranches, combinedBranch, remote = DEFAULT_
/** @type string */
let combinedBranchTitle;
if (combinedBranch) {
console.log();
console.log(`Now I will need to know what to call your "combined" branch PR in GitHub.`);
combinedBranchTitle = await promptly.prompt(colors.bold(`Combined branch PR title:`));
if (!combinedBranchTitle) {
console.log(`Cannot continue.`.red, `(I need to know what the title of your combined branch PR should be.)`);
process.exit(5);
if (!cfgMap[combinedBranch].title) {
console.log();
console.log(`Now I will need to know what to call your "combined" branch PR in GitHub.`);
combinedBranchTitle = await promptly.prompt(colors.bold(`Combined branch PR title:`));
if (!combinedBranchTitle) {
console.log(`Cannot continue.`.red, `(I need to know what the title of your combined branch PR should be.)`);
process.exit(5);
}
} else {
combinedBranchTitle = cfgMap[combinedBranch].title
}
}

Expand All @@ -114,7 +133,7 @@ async function ensurePrsExist(sg, allBranches, combinedBranch, remote = DEFAULT_
await memo;
const {
title
} = branch === combinedBranch ? getCombinedBranchPrMsg() : await constructPrMsg(sg, branch);
} = branch === combinedBranch ? getCombinedBranchPrMsg() : await constructPrMsg(sg, branch, cfgMap);
console.log(` -> ${branch.green} (${title.italic})`);
}, Promise.resolve());

Expand Down Expand Up @@ -144,7 +163,7 @@ async function ensurePrsExist(sg, allBranches, combinedBranch, remote = DEFAULT_
const {
title,
body
} = branch === combinedBranch ? getCombinedBranchPrMsg() : await constructPrMsg(sg, branch);
} = branch === combinedBranch ? getCombinedBranchPrMsg() : await constructPrMsg(sg, branch, cfgMap);
const base = index === 0 || branch === combinedBranch ? 'master' : allBranches[index - 1];
process.stdout.write(`Checking if PR for branch ${branch} already exists... `);
const prs = await ghRepo.prsAsync({
Expand Down Expand Up @@ -195,7 +214,7 @@ async function ensurePrsExist(sg, allBranches, combinedBranch, remote = DEFAULT_
:
branch === combinedBranch ?
getCombinedBranchPrMsg() :
await constructPrMsg(sg, branch);
await constructPrMsg(sg, branch, cfgMap);
const navigation = constructTrainNavigation(prDict, branch, combinedBranch);
const newBody = upsertNavigationInBody(navigation, body);
process.stdout.write(`Updating PR for branch ${branch}...`);
Expand Down
33 changes: 31 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ async function getConfigPath(sg) {
}

/**
* @typedef {string | Object.<string, { combined: boolean, initSha?: string }>} BranchCfg
* @typedef {string | Object.<string, { title?: string, body?: string, combined: boolean, initSha?: string }>} BranchCfg
* @typedef {Object.<string, Array.<string | BranchCfg>>} TrainCfg
*/

Expand Down Expand Up @@ -116,6 +116,32 @@ async function getBranchesConfigInCurrentTrain(sg, config) {
return key && trains[key];
}

/**
* Returns object where keys are branch names, and values are
* branch configs (with `title`, `body`, `combined` etc.) or branch names
* (stubs for compatibility).
*
* This object is useful for quick check of branch config when looping
* over branch names.
*
* @param {Array.<BranchCfg>} trainCfg
* @return {Object.<string, BranchCfg>}
*/
function getConfigMapFromTrainBranchesConfig(trainCfg) {
const map = {};

for (branch of trainCfg) {
if (typeof branch === 'string') {
map[branch] = branch;
} else {
const branchName = Object.keys(branch)[0];
map[branchName] = branch[branchName];
}
}

return map;
}

/**
* @param {Array.<BranchCfg>} branchConfig
*/
Expand Down Expand Up @@ -286,7 +312,10 @@ async function main() {
// the PR titles and descriptions). Just push and create the PRs.
if (program.createPrs) {
await findAndPushBranches();
await ensurePrsExist(sg, sortedTrainBranches, combinedTrainBranch, program.remote);

const cfgMap = getConfigMapFromTrainBranchesConfig(trainCfg);

await ensurePrsExist(sg, sortedTrainBranches, combinedTrainBranch, cfgMap, program.remote);
return;
}

Expand Down