Skip to content
This repository was archived by the owner on Apr 28, 2025. It is now read-only.

Commit e94b0c8

Browse files
authored
Merge pull request #3 from groupon/dbushong/feature/master/setup-fix
setup fix & code cleanup
2 parents 2e9cd9b + dfd6136 commit e94b0c8

26 files changed

+327
-118
lines changed

lib/cli.js

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -41,26 +41,35 @@ const { UIError, log } = require('./common');
4141

4242
let ranAction = false;
4343

44+
/**
45+
* @param {string} cmd
46+
* @param {import('./typedefs').ActionFn} fn
47+
* @return {(...args: any[]) => void}
48+
*/
4449
function wrapAction(cmd, fn) {
50+
/** @type {import('./typedefs').CmdDeps} */
4551
const deps = { git, log };
46-
return function wrappedAction() {
52+
return (...args) => {
4753
ranAction = true;
48-
const args = [deps].concat(Array.from(arguments));
49-
const opts = args[args.length - 1];
54+
/** @type {import('./typedefs').CmdOpts} */
55+
const opts = args.pop();
5056
if (opts.parent.yes) {
5157
deps.forceBool = true;
52-
if (opts.parent.no)
58+
if (opts.parent.no) {
5359
throw new UIError('--yes and --no are mutually exclusive');
60+
}
5461
}
5562
if (opts.parent.no) deps.forceBool = false;
5663
verifySetup(cmd, deps)
57-
.then(() => fn.apply(null, args))
58-
.catch(err => {
59-
const justMessage = !err.stack || err instanceof UIError;
60-
// eslint-disable-next-line no-console
61-
console.error(`🚢💥 ${justMessage ? err.message : err.stack}`);
62-
process.exit(1);
63-
});
64+
.then(() => fn({ deps, opts, args }))
65+
.catch(
66+
/** @param {Error} err */ err => {
67+
const justMessage = !err.stack || err instanceof UIError;
68+
// eslint-disable-next-line no-console
69+
console.error(`🚢💥 ${justMessage ? err.message : err.stack}`);
70+
process.exit(1);
71+
}
72+
);
6473
};
6574
}
6675

lib/commands/abort.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@
3434

3535
const { featureParent, inferRemote, remoteFromLocal } = require('../common');
3636

37-
async function abortAction({ git, log }) {
37+
/** @type {import('../typedefs').ActionFn} */
38+
async function abortAction({ deps: { git, log } }) {
3839
const { current: feature } = await git.branchLocal();
3940
const { parent } = await featureParent(git, feature);
4041

@@ -61,6 +62,7 @@ async function abortAction({ git, log }) {
6162
log(`${feature} aborted; last SHA was ${finalSHA.trim()}`);
6263
}
6364

65+
/** @type {import('../typedefs').Action} */
6466
module.exports = {
6567
action: abortAction,
6668
command(prog, wrapAction) {

lib/commands/cut-release.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,14 @@ const { action: mergeBackAction } = require('./merge-back');
3838

3939
const { ghURL, assertNoFork } = require('../common');
4040

41-
async function cutReleaseAction({ git, log }, branch, opts) {
41+
/** @type {import('../typedefs').ActionFn} */
42+
async function cutReleaseAction({ deps: { git, log }, args: [branch], opts }) {
4243
await assertNoFork(git, 'cut-release');
4344

4445
if (!branch) branch = 'master';
4546

4647
log('Ensuring all changes are merged back');
47-
await mergeBackAction({ git, log });
48+
await mergeBackAction({ deps: { git, log }, opts, args: [] });
4849

4950
log(`Creating PR to fast-forward merge ${branch} onto release`);
5051
const prURL = await ghURL(git, `/compare/release...${branch}`, {
@@ -55,6 +56,7 @@ async function cutReleaseAction({ git, log }, branch, opts) {
5556
if (opts.parent.open) await open(prURL);
5657
}
5758

59+
/** @type {import('../typedefs').Action} */
5860
module.exports = {
5961
action: cutReleaseAction,
6062
command(prog, wrapAction) {

lib/commands/done.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,25 +34,27 @@
3434

3535
const {
3636
featureParent,
37-
prompt,
37+
yesNo,
3838
UIError,
3939
plural,
4040
inferRemote,
4141
} = require('../common');
4242

43-
async function doneAction({ git, log, forceBool }) {
43+
/** @type {import('../typedefs').ActionFn} */
44+
async function doneAction({ deps: { git, log, forceBool } }) {
4445
const { current: feature } = await git.branchLocal();
4546
const { parent } = await featureParent(git, feature);
4647

4748
log(
4849
`Switching to detected parent branch '${parent}' and pulling latest commits`
4950
);
5051
await git.checkout(parent);
52+
// @ts-ignore
5153
await git.pull({ '--no-rebase': true });
5254

5355
const unmerged = (await git.log([`..${feature}`])).total;
5456
if (unmerged > 0) {
55-
const ok = await prompt(
57+
const ok = await yesNo(
5658
`Feature branch '${feature}' contains ${plural(
5759
unmerged,
5860
'commit'
@@ -73,6 +75,7 @@ async function doneAction({ git, log, forceBool }) {
7375
await git.raw(['remote', 'prune', remote]);
7476
}
7577

78+
/** @type {import('../typedefs').Action} */
7679
module.exports = {
7780
action: doneAction,
7881
command(prog, wrapAction) {

lib/commands/hotfix.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,19 +34,23 @@
3434

3535
const { assertNoFork } = require('../common');
3636

37-
async function hotfixAction({ git, log }, buildTag) {
37+
/** @type {import('../typedefs').ActionFn} */
38+
async function hotfixAction({ deps: { git, log }, args: [buildTag] }) {
3839
await assertNoFork(git, 'hotfix');
3940

4041
log("Switching to branch 'hotfix' and pulling latest commits and tags");
4142
await git.checkout('hotfix');
43+
// @ts-ignore
4244
await git.pull({ '--no-rebase': true });
45+
// @ts-ignore
4346
await git.fetch({ '--tags': true });
4447

4548
log(`Fast-forwarding 'hotfix' to '${buildTag}' and pushing`);
4649
await git.merge([buildTag, '--ff-only']);
4750
await git.push();
4851
}
4952

53+
/** @type {import('../typedefs').Action} */
5054
module.exports = {
5155
action: hotfixAction,
5256
command(prog, wrapAction) {

lib/commands/merge-back.js

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,23 +35,38 @@
3535
const { action: startAction } = require('./start');
3636
const { UIError, cmdLine, assertNoFork } = require('../common');
3737

38+
/**
39+
* @param {import('../typedefs').CmdDeps} deps
40+
* @param {string} from
41+
*/
3842
async function createFeatureMerge({ git, log }, from) {
3943
await git.reset('hard');
40-
await startAction({ git, log }, `merge-${from}`);
41-
try {
42-
await git.merge([from]);
43-
} catch (err) {
44-
/* */
45-
}
44+
await startAction({
45+
deps: { git, log },
46+
args: [`merge-${from}`],
47+
opts: { parent: {} },
48+
});
49+
await git.merge([from]).catch(() => {});
4650
throw new UIError('When conflicts are resolved, commit and `wf pr`');
4751
}
4852

53+
/**
54+
* @param {import('simple-git/promise').SimpleGit} git
55+
* @param {string} branch
56+
*/
4957
async function switchAndPull(git, branch) {
5058
await git.checkout(branch);
59+
// @ts-ignore
5160
await git.pull({ '--no-rebase': true });
5261
}
5362

54-
async function tryMerge({ git, log }, from, to) {
63+
/**
64+
* @param {import('../typedefs').CmdDeps} deps
65+
* @param {string} from
66+
* @param {string} to
67+
*/
68+
async function tryMerge(deps, from, to) {
69+
const { git, log } = deps;
5570
log(`${to}${from}`);
5671
await switchAndPull(git, to);
5772

@@ -65,7 +80,7 @@ async function tryMerge({ git, log }, from, to) {
6580
if (/\nCONFLICT /.test(output)) throw new Error(output);
6681
} catch (err) {
6782
log('Automated merge failed; creating feature branch for resolution');
68-
await createFeatureMerge({ git, log }, from); // will throw
83+
await createFeatureMerge(deps, from); // will throw
6984
}
7085

7186
log(`Merged cleanly; committing & pushing results to ${to} branch`);
@@ -76,7 +91,8 @@ async function tryMerge({ git, log }, from, to) {
7691
await git.push();
7792
}
7893

79-
async function mergeBackAction(deps) {
94+
/** @type {import('../typedefs').ActionFn} */
95+
async function mergeBackAction({ deps }) {
8096
const { git, log } = deps;
8197

8298
await assertNoFork(git, 'merge-back');
@@ -94,6 +110,7 @@ async function mergeBackAction(deps) {
94110
return true;
95111
}
96112

113+
/** @type {import('../typedefs').Action} */
97114
module.exports = {
98115
action: mergeBackAction,
99116
command(prog, wrapAction) {

lib/commands/pr.js

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,25 +42,39 @@ const {
4242
UIError,
4343
} = require('../common');
4444

45+
/**
46+
* @param {string} str
47+
*/
4548
function stripNLMPrefix(str) {
4649
return str.replace(/^(feat|fix|docs|style|refactor|perf|test|chore):\s+/, '');
4750
}
4851

52+
/**
53+
* @param {string[]} subjects
54+
*/
4955
function findTitle(subjects) {
5056
const eligible = subjects.filter(s => !/^(docs|test):\s/.test(s));
5157
return eligible.length === 1 ? stripNLMPrefix(eligible[0]) : null;
5258
}
5359

5460
// extracts the organization from the "fork" remote's url
61+
/**
62+
* @param {import('simple-git/promise').SimpleGit} git
63+
*/
5564
function forkPrefix(git) {
5665
return git.getRemotes(true).then(remotes => {
57-
const remote = remotes.find(r => r.name === 'fork').refs.fetch;
58-
const owner = remote.match(/([\w-]+)\/[^\/]+$/)[1];
59-
return `${owner}:`;
66+
const forkRemote = remotes.find(r => r.name === 'fork');
67+
if (!forkRemote) throw new Error('Could not find fork remote');
68+
const ownerMatch = forkRemote.refs.fetch.match(/([\w-]+)\/[^\/]+$/);
69+
if (!ownerMatch) {
70+
throw new Error(`Could not match owner in ${forkRemote.refs.fetch}`);
71+
}
72+
return `${ownerMatch[1]}:`;
6073
});
6174
}
6275

63-
async function prAction({ git, log }, opts) {
76+
/** @type {import('../typedefs').ActionFn} */
77+
async function prAction({ deps: { git, log }, opts }) {
6478
log('Ensuring all work is pushed to remote');
6579
try {
6680
await git.push();
@@ -96,7 +110,7 @@ async function prAction({ git, log }, opts) {
96110
if (gitLog.total > 1) {
97111
title =
98112
findTitle(gitLog.all.map(c => c.subject)) || current.replace(/-/g, ' ');
99-
body = gitLog.all
113+
body = [...gitLog.all]
100114
.reverse()
101115
.map(c => `* ${c.subject}`)
102116
.join('\n');
@@ -118,6 +132,7 @@ async function prAction({ git, log }, opts) {
118132
if (opts.parent.open) await open(prURL);
119133
}
120134

135+
/** @type {import('../typedefs').Action} */
121136
module.exports = {
122137
action: prAction,
123138
command(prog, wrapAction) {

lib/commands/qa.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,27 +45,31 @@ function genBuildTag() {
4545
return `build-${ts}`;
4646
}
4747

48-
async function qaAction({ git, log }, branch, opts) {
48+
/** @type {import('../typedefs').ActionFn} */
49+
async function qaAction({ deps: { git, log }, args: [branch], opts }) {
4950
await assertNoFork(git, 'qa');
5051

5152
if (branch) await git.checkout(branch);
5253
else branch = (await git.branchLocal()).current;
5354

5455
if (branch === 'release' && opts.mergeBack) {
5556
log('Requiring clean merge-back for release qa');
56-
await mergeBackAction({ git, log });
57+
await mergeBackAction({ deps: { git, log }, opts, args: [] });
5758
} else {
5859
log(`Pulling latest commits for '${branch}'`);
60+
// @ts-ignore
5961
await git.pull({ '--no-rebase': true });
6062
}
6163

6264
const tag = genBuildTag();
6365

6466
log(`Creating and pushing tag to github: ${tag}`);
6567
await git.tag([tag]);
68+
// @ts-ignore
6669
await git.push(['origin', 'tag', tag]);
6770
}
6871

72+
/** @type {import('../typedefs').Action} */
6973
module.exports = {
7074
action: qaAction,
7175
command(prog, wrapAction) {

lib/commands/rename.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@
3434

3535
const { UIError, featureParent, remoteFromLocal } = require('../common');
3636

37-
async function renameAction({ git, log }, newBranch) {
37+
/** @type {import('../typedefs').ActionFn} */
38+
async function renameAction({ deps: { git, log }, args: [newBranch] }) {
3839
const { current } = await git.branchLocal();
3940

4041
const { parent, remote } = await featureParent(git, current);
@@ -49,6 +50,7 @@ async function renameAction({ git, log }, newBranch) {
4950
}
5051

5152
log(`Fetching ${remote}`);
53+
// @ts-ignore
5254
await git.fetch([remote]);
5355

5456
log(`Creating ${newRemoteBranch} on ${remote}`);
@@ -66,6 +68,7 @@ async function renameAction({ git, log }, newBranch) {
6668
await git.push(remote, `:${oldRemoteBranch}`);
6769
}
6870

71+
/** @type {import('../typedefs').Action} */
6972
module.exports = {
7073
action: renameAction,
7174
command(prog, wrapAction) {

lib/commands/start.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,8 @@
3434

3535
const { remoteFromLocal, UIError, inferRemote } = require('../common');
3636

37-
async function startAction({ git, log }, branch, opts) {
38-
opts = opts || {};
39-
37+
/** @type {import('../typedefs').ActionFn} */
38+
async function startAction({ deps: { git, log }, args: [branch], opts }) {
4039
if (!/^[\w-]+$/.test(branch)) {
4140
throw new UIError('branch must match /^[\\w-]+$/');
4241
}
@@ -67,6 +66,7 @@ async function startAction({ git, log }, branch, opts) {
6766
}
6867

6968
log(`Updating current branch '${current}'`);
69+
// @ts-ignore
7070
await git.pull({ '--no-rebase': true });
7171

7272
const pushSpec = `${branch}:${remoteBranch}`;
@@ -80,6 +80,7 @@ async function startAction({ git, log }, branch, opts) {
8080
}
8181
}
8282

83+
/** @type {import('../typedefs').Action} */
8384
module.exports = {
8485
action: startAction,
8586
command(prog, wrapAction) {

0 commit comments

Comments
 (0)