-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathcli.js
98 lines (79 loc) · 3 KB
/
cli.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#!/usr/bin/env node
const program = require('commander');
const fs = require('fs-extra');
const parse = require('parse-git-numstat');
const multimatch = require('multimatch');
const { table } = require('table');
const { createObjectCsvStringifier } = require('csv-writer');
const normalizeRenames = require('./lib/normalize-renames');
const markDeleted = require('./lib/mark-deleted');
const buildBusfactors = require('./lib/build-busfactors');
program.option('-g, --gitlog <gitlog>', 'path to git log with numstat generated by "git log --numstat > gitlog.txt"', 'gitlog.txt');
program.option('-t, --threshold <threshold>', 'report busfactor below this threshold', 3);
program.option('-e, --exclude <glob>', 'glob pattern to exclude files', repeatable, []);
program.option('-i, --include <glob>', 'glob pattern to include files', repeatable, []);
program.option('-r, --report <table/csv>', 'report bus factors in csv or console table format', 'table');
program.parse(process.argv);
async function parseGitlog(args) {
if (!(await fs.exists(args.gitlog))) {
console.error(`Could not open gitlog file ${args.gitlog}. You could generate it using "git log --numstat > gitlog.txt"`);
process.exit(1);
}
const gitlogRaw = await fs.readFile(args.gitlog, { encoding: 'utf-8' });
return parse(gitlogRaw);
}
async function buildReport(args) {
const gitlog = await parseGitlog(args);
const commits = markDeleted(normalizeRenames(gitlog.filter(commit => !commit.merge)));
const matchPattern = buildMatchPattern(args.include, args.exclude);
const busfactors = buildBusfactors(commits)
.filter(file => file.busfactor < args.threshold)
.filter(file => multimatch([file.filepath], matchPattern).length == 1);
if (busfactors.length == 0) {
console.log(`No files with bus factor less than threshold ${args.threshold} found`);
return;
}
if (args.report == 'csv') {
const records = busfactors.map(file => ({
filepath: file.filepath,
authors: file.authors,
busfactor: file.busfactor,
}));
const writer = createObjectCsvStringifier({
header: [
{ id: 'filepath', title: 'Filepath' },
{ id: 'authors', title: 'Authors' },
{ id: 'busfactor', title: 'Bus Factor' },
],
});
console.log(writer.stringifyRecords(records));
} else {
const tableData = busfactors.map(file => [file.filepath, file.authors, file.busfactor]);
const config = {
columns: {
0: {
width: 60,
},
1: {
width: 20,
},
2: {
width: 2,
},
},
};
const busfactorTable = table(tableData, config);
console.log(busfactorTable);
console.log(`Found ${tableData.length} files with a bus factor of ${args.threshold - 1} or below`);
}
}
function buildMatchPattern(includes, excludes) {
return [].concat(
includes.length > 0 ? includes : '**/*.*',
excludes.map(e => `!${e}`),
);
}
function repeatable(value, previous) {
return [...previous, value];
}
buildReport(program);