-
Notifications
You must be signed in to change notification settings - Fork 28
/
cli.js
executable file
Β·151 lines (125 loc) Β· 3.86 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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
#!/usr/bin/env node
/* eslint-disable no-console */
// @ts-check
const findUp = require('find-up');
const fs = require('fs');
const ignore = require('ignore');
const intersection = require('lodash.intersection');
const padEnd = require('lodash.padend');
const path = require('path');
const program = require('commander');
const { walkStream } = require('@nodelib/fs.walk');
const Codeowners = require('./codeowners.js');
const rootPath = process.cwd();
const gitignorePath = findUp.sync('.gitignore', { cwd: rootPath });
const gitignoreMatcher = ignore();
if (gitignorePath) {
gitignoreMatcher.add(fs.readFileSync(gitignorePath).toString());
}
program
.command('audit')
.description('list the owners for all files')
.option('-u, --unowned', 'unowned files only')
.option('-w, --width <columns>', 'how much should filenames be padded?', '32')
.option(
'-c, --codeowners-filename <codeowners_filename>',
'specify CODEOWNERS filename',
'CODEOWNERS'
)
.action((options) => {
let codeowners;
try {
codeowners = new Codeowners(rootPath, options.codeownersFilename);
} catch (e) {
console.error(e.message);
process.exit(1);
}
const padding = parseInt(options.width, 10);
const stream = walkStream(rootPath, {
deepFilter: (entry) => {
const split = entry.path.split(path.sep);
return (
!split.includes('node_modules') && !split.includes('.git') && !split.includes('.cache')
);
},
errorFilter: (error) =>
error.code === 'ENOENT' || error.code === 'EACCES' || error.code === 'EPERM',
});
stream.on('data', (file) => {
const relative = path
.relative(codeowners.codeownersDirectory, file.path)
.replace(/(\r)/g, '\\r');
const owners = codeowners.getOwner(relative);
if (options.unowned) {
if (!owners.length) {
console.log(relative);
}
} else {
console.log(
`${padEnd(relative, padding)} ${owners.length ? owners.join(' ') : 'nobody'}`
);
}
});
stream.on('error', (err) => {
console.error(err);
});
});
program
.command('verify <path> <users...>')
.description('verify users/teams own a specific path')
.option(
'-c, --codeowners-filename <codeowners_filename>',
'specify CODEOWNERS filename',
'CODEOWNERS'
)
.action((checkPath, users, options) => {
let codeowners;
// instantiate new Codeowners obj
try {
codeowners = new Codeowners(rootPath, options.codeownersFilename);
} catch (e) {
console.error(e.message);
process.exit(1);
}
// call getOwner() on `path`
const owners = codeowners.getOwner(checkPath);
// check if any `users` are in the results of getOwner()
const verifiedOwners = intersection(users, owners);
// if verifiedOwners is empty, exit with error
if (verifiedOwners.length < 1) {
console.error(`None of the users/teams specified own the path ${checkPath}`);
process.exit(1);
}
// print owners
for (const currOwner of verifiedOwners) {
console.log(`${checkPath} ${currOwner}`);
}
});
program
.command('list <path>')
.description('list users/teams that own a specific path')
.option(
'-c, --codeowners-filename <codeowners_filename>',
'specify CODEOWNERS filename',
'CODEOWNERS'
)
.action((checkPath, options) => {
let codeowners;
// instantiate new Codeowners obj
try {
codeowners = new Codeowners(rootPath, options.codeownersFilename);
} catch (e) {
console.error(e.message);
process.exit(1);
}
// call getOwner() on `path`
const owners = codeowners.getOwner(checkPath);
// print owners
for (const currOwner of owners) {
console.log(`${currOwner}`);
}
})
if (!process.argv.slice(2).length) {
program.outputHelp();
}
program.parse(process.argv);