virtual-code-owners 6.2.0 → 7.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,82 +1,34 @@
1
1
  ## What?
2
2
 
3
- This takes a
3
+ This generates your `CODEOWNERS` file (_patterns_ x _users_) from
4
4
 
5
- - VIRTUAL-CODEOWNERS.txt file with (virtual) teams
6
- - a `virtual-teams.yml` file which define the teams
5
+ - a `VIRTUAL-CODEOWNERS.txt` (_patterns_ x _teams_)
6
+ - a `virtual-teams.yml` (_teams_ x _users_)
7
7
 
8
- ... and merges them into a CODEOWNERS with user names.
8
+ ... which makes it easier to keep `CODEOWNERS` in sync on multi-team mono repos.
9
+ When those teams are not defined on GitHub level.
9
10
 
10
11
  ## Usage
11
12
 
12
13
  - Rename your `.github/CODEOWNERS` to `.github/VIRTUAL-CODEOWNERS.txt` and put team names in them.
13
- - Specify team names that don't (yet) exist on GitHub level in a `.github/virtual-teams.yml`
14
+ - Define teams that don't (yet) exist on GitHub level in `.github/virtual-teams.yml`
14
15
  - Run this:
15
16
 
16
17
  ```
17
18
  npx virtual-code-owners
19
+ # Wrote '.github/CODEOWNERS'
18
20
  ```
19
21
 
20
- or, if you want to be verbose
21
-
22
- ```
23
- npx virtual-code-owners \
24
- --virtualCodeOwners .github/VIRTUAL-CODEOWNERS.txt \
25
- --virtualTeams .github/virtual-teams.yml \
26
- --codeOwners .github/CODEOWNERS
27
- ```
28
-
29
- ## Why?
30
-
31
- Organizations sometimes have large mono repositories with loads of code owners.
32
- They or their bureaucracy haven't landed on actually using GitHub teams to clearly
33
- demarcate that. Teams in those organizations who want to have clear code ownership
34
- have the following choices:
35
-
36
- - Wrestle the bureaucracy.
37
- This is the recommended approach. It might take a while, though - and even
38
- though there are good people on many levels in bureaucracies, it might
39
- eventually not pan out because #reasons.
40
- - Maintain a CODEOWNERS file with code assigned to large lists of individuals.
41
- An option, but laborious to maintain, even for smaller projects; for example:
42
-
43
- ```CODEOWNERS
44
- # catch-all to ensure there at least _is_ a code owner, even when
45
- # it's _everyone_
46
-
47
- * @cloud-heroes-all
48
-
49
- # admin & ci stuff => transversal
50
-
51
- .github/ @abraham-lincoln @benjamin-franklin @koos-koets @luke-the-lucky-ch @mary-the-merry-ch @naomi-the-namegiver-ch
52
-
53
- # generic stuff
54
-
55
- apps/framework/ @abraham-lincoln @benjamin-franklin @koos-koets @luke-the-lucky-ch @mary-the-merry-ch @naomi-the-namegiver-ch
56
- apps/ux-portal/ @abraham-lincoln @benjamin-franklin @charlotte-de-bourbon-ch @davy-davidson-ch @joe-dalton-ch @john-johnson-ch @koos-koets @luke-the-lucky-ch @mary-the-merry-ch @naomi-the-namegiver-ch
57
- libs/components/ @charlotte-de-bourbon-ch @davy-davidson-ch @joe-dalton-ch @john-johnson-ch @koos-koets
58
-
59
- # specific functionality
60
-
61
- libs/ubc-sales/ @abraham-ableton-ch @boris-bubbleblower-ch @charlotte-charleston-ch @dagny-taggert-ch @gregory-gregson-ch @jane-doe-ch @karl-marx-ch
62
- libs/ubc-after-sales/ @daisy-duck @donald-duck @john-doe-ch @pete-peterson-ch @william-the-fourth-ch
63
- libs/ubc-pre-sales/ @averel-dalton-ch @jean-claude-ch @john-galt-ch @valerie-valerton-ch
64
- libs/ubc-refund/ @abraham-ableton-ch @boris-bubbleblower-ch @charlotte-charleston-ch @dagny-taggert-ch @daisy-duck @donald-duck @gregory-gregson-ch @jane-doe-ch @john-doe-ch @karl-marx-ch @pete-peterson-ch @william-the-fourth-ch
65
- libs/ubc-baarden/ jan@example.com korneel@example.com pier@example.com tjorus@example.com
66
- ```
67
-
68
- This is where `virtual-code-owners` comes in.
22
+ - :sparkles:
69
23
 
70
24
  ## Formats
71
25
 
72
26
  ### VIRTUAL-CODEOWNERS.txt
73
27
 
74
- `VIRTUAL-CODEOWNERS.txt` is a regular, valid GitHub [CODEOWNERS](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners) file.
75
- The only difference between VIRTUAL-CODEOWNERS.txt and a CODEOWNERS file is that
76
- the _teams_ the former uses might not exist yet, except in a `virtual-teams.yml`.
77
- This enables you to write a _much_ easier to maintain list of code owners.
28
+ `VIRTUAL-CODEOWNERS.txt` sticks to the [CODEOWNERS](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners) format,
29
+ but adds the ability to include teams defined in `virtual-teams.yml`.
78
30
 
79
- For example the CODEOWNERS file above can then look like this:
31
+ For example a CODEOWNERS file can look like this:
80
32
 
81
33
  ```CODEOWNERS
82
34
  #! comments that start with #! won't appear in the CODEOWNERS output
@@ -108,11 +60,14 @@ libs/ubc-refund/ @ch/sales @ch/after-sales
108
60
  libs/ubc-baarden/ @ch/mannen-met-baarden
109
61
  ```
110
62
 
63
+ ... where only the @cloud-heroes-all is a 'real' team on GitHub level. The other
64
+ ones are defined in `virtual-teams.yml`.
65
+
111
66
  ### virtual-teams.yml
112
67
 
113
- A valid YAML file that contains a list of teams, with for each team its members.
114
- If a new team member joins, you can enter it here, run `npx virtual-code-owners`
115
- to update CODEOWNERS.
68
+ A valid YAML file that contains a list of teams and their members.
69
+ Update it whenever you have new team members and run `npx virtual-code-owners`
70
+ to keep CODEOWNERS current.
116
71
 
117
72
  ```yaml
118
73
  # yaml-language-server: $schema=https://raw.githubusercontent.com/sverweij/virtual-code-owners/main/src/virtual-teams.schema.json
@@ -148,61 +103,58 @@ ch/transversal:
148
103
  - benjamin-franklin
149
104
  - koos-koets
150
105
  - abraham-lincoln
106
+ ch/mannen-met-baarden:
107
+ - jan@example.com
108
+ - pier@example.com
109
+ - tjorus@example.com
110
+ - korneel@example.com
151
111
  ```
152
112
 
153
- ## FAQ
154
-
155
- ### Can I mix real and virtual teams in `VIRTUAL-CODEOWNERS.txt`?
113
+ ### CODEOWNERS
156
114
 
157
- Yes.
115
+ Running `npx virtual-code-owners` will combine these into a CODEOWNERS file like this:
158
116
 
159
- It might be you already have a team or two defined, but just want to use
160
- _additional_ teams. In that case just don't specify the already-defined teams
161
- in `virtual-teams.yml` and _virtual-code-owners_ will leave them alone.
162
-
163
- ### Can I still use usernames in `VIRTUAL-CODEOWNERS.txt`?
164
-
165
- Yes.
117
+ ```CODEOWNERS
118
+ #
119
+ # DO NOT EDIT - this file is generated and your edits will be overwritten
120
+ #
121
+ # To make changes:
122
+ #
123
+ # - edit .github/VIRTUAL-CODEOWNERS.txt
124
+ # - and/ or add team members to .github/virtual-teams.yml
125
+ # - run 'npx virtual-code-owners' (or 'npx virtual-code-owners --emitLabeler' if you also
126
+ # want to generate a .github/labeler.yml)
127
+ #
166
128
 
167
- Just make sure there's no name clashes between the username and a (virtual)
168
- team name and _virtual-code-owners_ will leave the real name alone.
129
+ # catch-all to ensure there at least _is_ a code owner, even when
130
+ # it's _everyone_
169
131
 
170
- ### What validations does virtual-code-owners perform?
132
+ * @cloud-heroes-all
171
133
 
172
- On the VIRTUAL-CODEOWNERS.txt file it performs is a little bit of validation:
134
+ # admin & ci stuff => transversal
173
135
 
174
- - it will find invalid user/ team names (those that don't start with an `@` or
175
- aren't an e-mail address)
176
- - it will find invalid 'rules'; which is the case when there is a file pattern on
177
- the line, but no user or team names.
136
+ .github/ @abraham-lincoln @benjamin-franklin @koos-koets @luke-the-lucky-ch @mary-the-merry-ch @naomi-the-namegiver-ch
178
137
 
179
- When it encounters any of these virtual-code-owners will emit a clear error message
180
- with the location of the error and exit with a non-zero code, to prevent the
181
- creation of a potentially invalid CODEOWNERS file.
138
+ # generic stuff
182
139
 
183
- It _does not_ check whether the user or team names actually exist in the current
184
- project, though. Although nice, there's already tooling on the generated CODEOWNERS
185
- file that will check that for you.
140
+ apps/framework/ @abraham-lincoln @benjamin-franklin @koos-koets @luke-the-lucky-ch @mary-the-merry-ch @naomi-the-namegiver-ch
141
+ apps/ux-portal/ @abraham-lincoln @benjamin-franklin @charlotte-de-bourbon-ch @davy-davidson-ch @joe-dalton-ch @john-johnson-ch @koos-koets @luke-the-lucky-ch @mary-the-merry-ch @naomi-the-namegiver-ch
142
+ libs/components/ @charlotte-de-bourbon-ch @davy-davidson-ch @joe-dalton-ch @john-johnson-ch @koos-koets
186
143
 
187
- ### Any limitations I should know of?
144
+ # specific functionality
188
145
 
189
- - ~~Currently only works for _user names_ to identify team members - not for e-mail
190
- addresses.~~
191
- Works with both user names and e-mail addresses
192
- - Although _virtual-code-owners_ performs basic validations on the CODEOWNER
193
- format and tries to emit clear and actionable error messages about them, it
194
- might not catch all of them - e.g. it doesn't check if the users in
195
- virtual-teams.yml actually exist.
146
+ libs/ubc-sales/ @abraham-ableton-ch @boris-bubbleblower-ch @charlotte-charleston-ch @dagny-taggert-ch @gregory-gregson-ch @jane-doe-ch @karl-marx-ch
147
+ libs/ubc-after-sales/ @daisy-duck @donald-duck @john-doe-ch @pete-peterson-ch @william-the-fourth-ch
148
+ libs/ubc-pre-sales/ @averel-dalton-ch @jean-claude-ch @john-galt-ch @valerie-valerton-ch
149
+ libs/ubc-refund/ @abraham-ableton-ch @boris-bubbleblower-ch @charlotte-charleston-ch @dagny-taggert-ch @daisy-duck @donald-duck @gregory-gregson-ch @jane-doe-ch @john-doe-ch @karl-marx-ch @pete-peterson-ch @william-the-fourth-ch
150
+ libs/ubc-baarden/ jan@example.com korneel@example.com pier@example.com tjorus@example.com
151
+ ```
196
152
 
197
- ### Why the `.txt` extension?
153
+ ## FAQ
198
154
 
199
- Various editors assume an ALL_CAPS file name with `#` characters on various lines
200
- to be markdown, and will auto format them as such - making for either very ugly
201
- or in worst cases invalid CODEOWNERS files. Usually such autoformatting is not
202
- present on text files.
155
+ ### Any gotcha's?
203
156
 
204
- Apparently these editors know about CODEOWNERS, though so this auto formatting
205
- doesn't seem to be happening over there.
157
+ It won't check whether the users or teams you entered exist.
206
158
 
207
159
  ### Do I have to run this each time I edit `VIRTUAL-CODEOWNERS.txt`?
208
160
 
@@ -220,14 +172,27 @@ like this:
220
172
  }
221
173
  ```
222
174
 
223
- ### It'd be _pretty_ handy if I could see for which virtual teams a PR is. For instance with a bunch of :label: labels.
175
+ ### Can I mix real and virtual teams in `VIRTUAL-CODEOWNERS.txt`?
176
+
177
+ Yes.
178
+
179
+ It might be you already have a team or two defined, but just want to use
180
+ _additional_ teams. In that case just don't specify the already-defined teams
181
+ in `virtual-teams.yml` and _virtual-code-owners_ will leave them alone.
182
+
183
+ ### Can I still use usernames in `VIRTUAL-CODEOWNERS.txt`?
184
+
185
+ Yes.
186
+
187
+ Just make sure there's no name clashes between the username and a (virtual)
188
+ team name and _virtual-code-owners_ will leave the real name alone.
189
+
190
+ ### Can I automatically label PR's for virtual teams?
224
191
 
225
- How do I go about that?
192
+ Yep.
226
193
 
227
- You can use the [actions/labeler](https://github.com/actions/labeler) action for
228
- for this. Maintaining the configuration file (`.github/labeler.yml`) and keeping it
229
- sync with the virtual-teams and virtual code-owners files manually is a bit of
230
- a chore, though, so `virtual-code-owners` has an option to automate that.
194
+ Use [actions/labeler](https://github.com/actions/labeler) and tickle
195
+ `virtual-code-owners` to generate the labeler config file:
231
196
 
232
197
  ```sh
233
198
  npx virtual-code-owners --emitLabeler
@@ -237,12 +202,62 @@ npx virtual-code-owners --emitLabeler
237
202
  If you have an alternate file location for the `labeler.yml` you can specify that
238
203
  with virtual-code-owner's `--labelerLocation` parameter.
239
204
 
240
- ### Can I just run virtual-code-owners to validate VIRTUAL-CODEOWNERS.txt & virtual-teams.yml?
205
+ ### What validations does virtual-code-owners perform?
206
+
207
+ virtual-code-owners checks for basic CODEOWNERS format errors and invalid
208
+ user/team names but doesn't verify their existence in the project.
209
+
210
+ - valid user/team names start with an `@` or are an e-mail address
211
+ - valid rules have a file pattern and at least one user/team name
212
+
213
+ ### I want to specify different locations for the files (e.g. because I'm using GitLab)
214
+
215
+ Here you go:
216
+
217
+ ```
218
+ npx virtual-code-owners \
219
+ --virtualCodeOwners .gitlab/VIRTUAL-CODEOWNERS.txt \
220
+ --virtualTeams .gitlab/virtual-teams.yml \
221
+ --codeOwners .gitlab/CODEOWNERS
222
+ ```
223
+
224
+ ### Can I just validate VIRTUAL-CODEOWNERS.txt & virtual-teams.yml without generating output?
241
225
 
242
226
  So _without_ generating any output?
243
227
 
244
- Yes. Use the `--dryRun` command line option:
228
+ Sure thing. Use `--dryRun`:
245
229
 
246
230
  ```
247
231
  npx virtual-code-owners --dryRun
248
232
  ```
233
+
234
+ ### Why the `.txt` extension?
235
+
236
+ It keeps editors and IDE's from messing up your formatting.
237
+
238
+ Various editors assume an ALL_CAPS file name with `#` characters on various lines
239
+ to be markdown, and will auto format them as such. This makes for either very ugly
240
+ or in worst cases invalid CODEOWNERS files. Usually such autoformatting is not
241
+ present on text files.
242
+
243
+ Apparently these editors know about CODEOWNERS, though, so they don't mess with
244
+ the formatting of _those_.
245
+
246
+ ### Why does this exist at all? Why not just use GitHub teams?
247
+
248
+ You should _totally_ use GitHub teams! If you can.
249
+
250
+ Organizations sometimes have large mono repositories with loads of code owners.
251
+ They or their bureaucracy haven't landed on actually using GitHub teams to clearly
252
+ demarcate that. Or you're working on a cross-functional team that doesn't follow
253
+ the organization chart (and hence the GitHub teams). Teams in those organizations
254
+ who want to have clear code ownership have the following choices:
255
+
256
+ - Wrestle the bureaucracy.
257
+ This is the recommended approach. It might take a while, though - and even
258
+ though there are good people on many levels in bureaucracies, it might
259
+ eventually not pan out because #reasons.
260
+ - Maintain a CODEOWNERS file with code assigned to large lists of individuals.
261
+ An option, but laborious to maintain, even for smaller projects
262
+
263
+ This is where `virtual-code-owners` comes in.
package/dist/cli.js CHANGED
@@ -1,3 +1,84 @@
1
- #!/usr/bin/env node
2
- import { cli } from "./main.js";
3
- cli();
1
+ import { EOL } from "node:os";
2
+ import { parseArgs } from "node:util";
3
+ import { main } from "./main.js";
4
+ import { VERSION } from "./version.js";
5
+ const HELP_MESSAGE = `Usage: virtual-code-owners [options]
6
+
7
+ Merges a VIRTUAL-CODEOWNERS.txt and a virtual-teams.yml into CODEOWNERS
8
+
9
+ Options:
10
+ -V, --version output the version number
11
+ -v, --virtualCodeOwners [file-name] A CODEOWNERS file with team names in them
12
+ that are defined in a virtual teams file
13
+ (default: ".github/VIRTUAL-CODEOWNERS.txt")
14
+ -t, --virtualTeams [file-name] A YAML file listing teams and their
15
+ members
16
+ (default: ".github/virtual-teams.yml")
17
+ -c, --codeOwners [file-name] The location of the CODEOWNERS file
18
+ (default: ".github/CODEOWNERS")
19
+ -l, --emitLabeler Whether or not to emit a labeler.yml to be
20
+ used with actions/labeler
21
+ (default: false)
22
+ --labelerLocation [file-name] The location of the labeler.yml file
23
+ (default: ".github/labeler.yml")
24
+ --dryRun Just validate inputs, don't generate
25
+ outputs (default: false)
26
+ -h, --help display help for command`;
27
+ export function cli(pArguments = process.argv.slice(2), pOutStream = process.stdout, pErrorStream = process.stderr, pErrorExitCode = 1) {
28
+ try {
29
+ const lOptions = getOptions(pArguments);
30
+ if (lOptions.help) {
31
+ pOutStream.write(`${HELP_MESSAGE}${EOL}`);
32
+ return;
33
+ }
34
+ if (lOptions.version) {
35
+ pOutStream.write(`${VERSION}${EOL}`);
36
+ return;
37
+ }
38
+ main(lOptions, pErrorStream);
39
+ }
40
+ catch (pError) {
41
+ pErrorStream.write(`${EOL}ERROR: ${pError.message}${EOL}${EOL}`);
42
+ process.exitCode = pErrorExitCode;
43
+ }
44
+ }
45
+ function getOptions(pArguments) {
46
+ return parseArgs({
47
+ args: pArguments,
48
+ options: {
49
+ virtualCodeOwners: {
50
+ type: "string",
51
+ short: "v",
52
+ default: ".github/VIRTUAL-CODEOWNERS.txt",
53
+ },
54
+ virtualTeams: {
55
+ type: "string",
56
+ short: "t",
57
+ default: ".github/virtual-teams.yml",
58
+ },
59
+ codeOwners: {
60
+ type: "string",
61
+ short: "c",
62
+ default: ".github/CODEOWNERS",
63
+ },
64
+ emitLabeler: {
65
+ type: "boolean",
66
+ short: "l",
67
+ default: false,
68
+ },
69
+ labelerLocation: {
70
+ type: "string",
71
+ default: ".github/labeler.yml",
72
+ },
73
+ dryRun: {
74
+ type: "boolean",
75
+ default: false,
76
+ },
77
+ help: { type: "boolean", short: "h", default: false },
78
+ version: { type: "boolean", short: "V", default: false },
79
+ },
80
+ strict: true,
81
+ allowPositionals: true,
82
+ tokens: false,
83
+ }).values;
84
+ }
@@ -1,5 +1,5 @@
1
1
  import { EOL } from "node:os";
2
- import { isEmailIshUsername } from "./utensils.js";
2
+ import { isEmailIshUsername } from "../utensils.js";
3
3
  const DEFAULT_WARNING = `#${EOL}` +
4
4
  `# DO NOT EDIT - this file is generated and your edits will be overwritten${EOL}` +
5
5
  `#${EOL}` +
@@ -36,7 +36,7 @@ function expandTeamToUserNames(pUser, pTeamMap) {
36
36
  return [pUser.raw];
37
37
  }
38
38
  function stringifyTeamMembers(pTeamMap, pTeamName) {
39
- return pTeamMap[pTeamName].map(userNameToCodeOwner);
39
+ return (pTeamMap[pTeamName] ?? []).map(userNameToCodeOwner);
40
40
  }
41
41
  function userNameToCodeOwner(pUserName) {
42
42
  if (isEmailIshUsername(pUserName)) {
package/dist/main.js CHANGED
@@ -1,92 +1,10 @@
1
1
  import { writeFileSync } from "node:fs";
2
2
  import { EOL } from "node:os";
3
- import { parseArgs } from "node:util";
4
- import generateCodeOwners from "./generate-codeowners.js";
5
- import generateLabelerYml from "./generate-labeler-yml.js";
6
- import readTeamMap from "./read-team-map.js";
7
- import readVirtualCodeOwners from "./read-virtual-code-owners.js";
8
- import { VERSION } from "./version.js";
9
- const HELP_MESSAGE = `Usage: virtual-code-owners [options]
10
-
11
- Merges a VIRTUAL-CODEOWNERS.txt and a virtual-teams.yml into CODEOWNERS
12
-
13
- Options:
14
- -V, --version output the version number
15
- -v, --virtualCodeOwners [file-name] A CODEOWNERS file with team names in them
16
- that are defined in a virtual teams file
17
- (default: ".github/VIRTUAL-CODEOWNERS.txt")
18
- -t, --virtualTeams [file-name] A YAML file listing teams and their
19
- members
20
- (default: ".github/virtual-teams.yml")
21
- -c, --codeOwners [file-name] The location of the CODEOWNERS file
22
- (default: ".github/CODEOWNERS")
23
- -l, --emitLabeler Whether or not to emit a labeler.yml to be
24
- used with actions/labeler
25
- (default: false)
26
- --labelerLocation [file-name] The location of the labeler.yml file
27
- (default: ".github/labeler.yml")
28
- --dryRun Just validate inputs, don't generate
29
- outputs (default: false)
30
- -h, --help display help for command`;
31
- export function cli(pArguments = process.argv.slice(2), pOutStream = process.stdout, pErrorStream = process.stderr, pErrorExitCode = 1) {
32
- try {
33
- const lOptions = getOptions(pArguments);
34
- if (lOptions.help) {
35
- pOutStream.write(`${HELP_MESSAGE}${EOL}`);
36
- return;
37
- }
38
- if (lOptions.version) {
39
- pOutStream.write(`${VERSION}${EOL}`);
40
- return;
41
- }
42
- main(lOptions, pErrorStream);
43
- }
44
- catch (pError) {
45
- pErrorStream.write(`${EOL}ERROR: ${pError.message}${EOL}${EOL}`);
46
- process.exitCode = pErrorExitCode;
47
- }
48
- }
49
- function getOptions(pArguments) {
50
- return parseArgs({
51
- args: pArguments,
52
- options: {
53
- virtualCodeOwners: {
54
- type: "string",
55
- short: "v",
56
- default: ".github/VIRTUAL-CODEOWNERS.txt",
57
- },
58
- virtualTeams: {
59
- type: "string",
60
- short: "t",
61
- default: ".github/virtual-teams.yml",
62
- },
63
- codeOwners: {
64
- type: "string",
65
- short: "c",
66
- default: ".github/CODEOWNERS",
67
- },
68
- emitLabeler: {
69
- type: "boolean",
70
- short: "l",
71
- default: false,
72
- },
73
- labelerLocation: {
74
- type: "string",
75
- default: ".github/labeler.yml",
76
- },
77
- dryRun: {
78
- type: "boolean",
79
- default: false,
80
- },
81
- help: { type: "boolean", short: "h", default: false },
82
- version: { type: "boolean", short: "V", default: false },
83
- },
84
- strict: true,
85
- allowPositionals: true,
86
- tokens: false,
87
- }).values;
88
- }
89
- function main(pOptions, pErrorStream) {
3
+ import generateCodeOwners from "./codeowners/generate.js";
4
+ import generateLabelerYml from "./labeler-yml/generate.js";
5
+ import readTeamMap from "./team-map/read.js";
6
+ import readVirtualCodeOwners from "./virtual-code-owners/read.js";
7
+ export function main(pOptions, pErrorStream) {
90
8
  const lTeamMap = readTeamMap(pOptions.virtualTeams);
91
9
  const lVirtualCodeOwners = readVirtualCodeOwners(pOptions.virtualCodeOwners, lTeamMap);
92
10
  const lCodeOwnersContent = generateCodeOwners(lVirtualCodeOwners, lTeamMap);
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ import { cli } from "./cli.js";
3
+ cli();
@@ -2,7 +2,7 @@ import Ajv from "ajv";
2
2
  import { readFileSync } from "node:fs";
3
3
  import { EOL } from "node:os";
4
4
  import { parse as parseYaml } from "yaml";
5
- const TEAM_MAP_SCHEMA = JSON.parse(readFileSync(new URL("./virtual-teams.schema.json", import.meta.url), "utf-8"));
5
+ import virtualTeamsSchema from "./virtual-teams.schema.js";
6
6
  export default function readTeamMap(pVirtualTeamsFileName) {
7
7
  const lVirtualTeamsAsAString = readFileSync(pVirtualTeamsFileName, {
8
8
  encoding: "utf-8",
@@ -16,7 +16,7 @@ function assertTeamMapValid(pTeamMap, pVirtualTeamsFileName) {
16
16
  allErrors: true,
17
17
  verbose: true,
18
18
  });
19
- if (!ajv.validate(TEAM_MAP_SCHEMA, pTeamMap)) {
19
+ if (!ajv.validate(virtualTeamsSchema, pTeamMap)) {
20
20
  throw new Error(`This is not a valid virtual-teams.yml:${EOL}${formatAjvErrors(ajv.errors, pVirtualTeamsFileName)}.\n`);
21
21
  }
22
22
  }
@@ -0,0 +1,15 @@
1
+ export default {
2
+ $schema: "http://json-schema.org/draft-07/schema#",
3
+ title: "virtual teams schema for virtual-code-owners",
4
+ description: "a list of teams and their team members",
5
+ $id: "org.js.virtual-code-owners/7.0.0",
6
+ type: "object",
7
+ additionalProperties: {
8
+ type: "array",
9
+ items: {
10
+ type: "string",
11
+ description: "Username or e-mail address of a team member. (Don't prefix usernames with '@')",
12
+ pattern: "^[^@][^\\s]+$",
13
+ },
14
+ },
15
+ };
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const VERSION = "6.2.0";
1
+ export const VERSION = "7.0.1";
@@ -0,0 +1,31 @@
1
+ export function getAnomalies(pVirtualCodeOwners) {
2
+ const weirdLines = pVirtualCodeOwners
3
+ .filter((pLine) => pLine.type === "unknown")
4
+ .map((pLine) => ({
5
+ ...pLine,
6
+ type: "invalid-line",
7
+ }));
8
+ const weirdUsers = pVirtualCodeOwners.flatMap((pLine) => {
9
+ if (pLine.type === "rule") {
10
+ return pLine.users
11
+ .filter((pUser) => pUser.type === "invalid")
12
+ .map((pUser) => ({
13
+ ...pUser,
14
+ line: pLine.line,
15
+ type: "invalid-user",
16
+ }));
17
+ }
18
+ return [];
19
+ });
20
+ return weirdLines.concat(weirdUsers).sort(orderAnomaly);
21
+ }
22
+ function orderAnomaly(pLeft, pRight) {
23
+ if (pLeft.line === pRight.line &&
24
+ pLeft.type === "invalid-user" &&
25
+ pRight.type === "invalid-user") {
26
+ return pLeft.userNumberWithinLine > pRight.userNumberWithinLine ? 1 : -1;
27
+ }
28
+ else {
29
+ return pLeft.line > pRight.line ? 1 : -1;
30
+ }
31
+ }
@@ -1,45 +1,14 @@
1
1
  import { EOL } from "node:os";
2
- import { isEmailIshUsername } from "./utensils.js";
2
+ import { isEmailIshUsername } from "../utensils.js";
3
3
  export function parse(pVirtualCodeOwnersAsString, pTeamMap = {}) {
4
4
  return pVirtualCodeOwnersAsString
5
5
  .split(EOL)
6
6
  .map((pUntreatedLine, pLineNo) => parseLine(pUntreatedLine, pTeamMap, pLineNo + 1));
7
7
  }
8
- export function getAnomalies(pVirtualCodeOwners) {
9
- const weirdLines = pVirtualCodeOwners
10
- .filter((pLine) => pLine.type === "unknown")
11
- .map((pLine) => ({
12
- ...pLine,
13
- type: "invalid-line",
14
- }));
15
- const weirdUsers = pVirtualCodeOwners.flatMap((pLine) => {
16
- if (pLine.type === "rule") {
17
- return pLine.users
18
- .filter((pUser) => pUser.type === "invalid")
19
- .map((pUser) => ({
20
- ...pUser,
21
- line: pLine.line,
22
- type: "invalid-user",
23
- }));
24
- }
25
- return [];
26
- });
27
- return weirdLines.concat(weirdUsers).sort(orderAnomaly);
28
- }
29
- function orderAnomaly(pLeft, pRight) {
30
- if (pLeft.line === pRight.line &&
31
- pLeft.type === "invalid-user" &&
32
- pRight.type === "invalid-user") {
33
- return pLeft.userNumberWithinLine > pRight.userNumberWithinLine ? 1 : -1;
34
- }
35
- else {
36
- return pLeft.line > pRight.line ? 1 : -1;
37
- }
38
- }
39
8
  function parseLine(pUntreatedLine, pTeamMap, pLineNo) {
40
9
  const lTrimmedLine = pUntreatedLine.trim();
41
10
  const lCommentSplitLine = lTrimmedLine.split(/\s*#/);
42
- const lRule = lCommentSplitLine[0].match(/^(?<filesPattern>[^\s]+)(?<spaces>\s+)(?<userNames>.*)$/);
11
+ const lRule = lCommentSplitLine[0]?.match(/^(?<filesPattern>[^\s]+)(?<spaces>\s+)(?<userNames>.*)$/);
43
12
  if (lTrimmedLine.startsWith("#!")) {
44
13
  return { type: "ignorable-comment", line: pLineNo, raw: pUntreatedLine };
45
14
  }
@@ -1,6 +1,7 @@
1
1
  import { readFileSync } from "node:fs";
2
2
  import { EOL } from "node:os";
3
- import { getAnomalies, parse as parseVirtualCodeOwners, } from "./parse-virtual-code-owners.js";
3
+ import { getAnomalies } from "./anomalies.js";
4
+ import { parse as parseVirtualCodeOwners } from "./parse.js";
4
5
  export default function readVirtualCodeOwners(pVirtualCodeOwnersFileName, pTeamMap) {
5
6
  const lVirtualCodeOwnersAsAString = readFileSync(pVirtualCodeOwnersFileName, {
6
7
  encoding: "utf-8",
@@ -2,7 +2,7 @@
2
2
  "$schema": "http://json-schema.org/draft-07/schema#",
3
3
  "title": "virtual teams schema for virtual-code-owners",
4
4
  "description": "a list of teams and their team members",
5
- "$id": "org.js.virtual-code-owners/6.0.0",
5
+ "$id": "org.js.virtual-code-owners/7.0.0",
6
6
  "type": "object",
7
7
  "additionalProperties": {
8
8
  "type": "array",
package/package.json CHANGED
@@ -1,60 +1,15 @@
1
1
  {
2
2
  "name": "virtual-code-owners",
3
- "version": "6.2.0",
3
+ "version": "7.0.1",
4
4
  "description": "Makes your CODEOWNERS file liveable again",
5
5
  "type": "module",
6
- "exports": {
7
- "parse": [
8
- {
9
- "import": "./dist/parse-virtual-code-owners.js",
10
- "types": "./types/parse-virtual-code-owners.d.ts"
11
- },
12
- "./dist/parse-virtual-code-owners.js"
13
- ],
14
- "generateCodeOwners": [
15
- {
16
- "import": "./dist/generate-codeowners.js",
17
- "types": "./types/generate-codeowners.d.ts"
18
- },
19
- "./dist/generate-codeowners.js"
20
- ],
21
- "generateLabelerYml": [
22
- {
23
- "import": "./dist/generate-labeler-yml.js",
24
- "types": "./types/generate-labeler-yml.d.ts"
25
- },
26
- "./dist/generate-labeler-yml.js"
27
- ]
28
- },
29
- "main": "dist/main.js",
30
- "types": "types/types.d.ts",
31
- "bin": "dist/cli.js",
6
+ "bin": "dist/run-cli.js",
32
7
  "files": [
33
8
  "dist/",
34
- "types/",
35
9
  "package.json",
36
10
  "README.md",
37
11
  "LICENSE"
38
12
  ],
39
- "scripts": {
40
- "build": "rm -rf dist && node --no-warnings --loader ts-node/esm tools/get-version.ts > src/version.ts && tsc && cp -f src/*.json dist/.",
41
- "check": "npm run format && npm run build && npm run depcruise -- --no-progress && npm test",
42
- "depcruise": "depcruise src tools",
43
- "depcruise:graph": "depcruise src --include-only '^(src)' --output-type dot | dot -T svg | depcruise-wrap-stream-in-html > dependency-graph.html",
44
- "depcruise:graph:dev": "depcruise src --prefix vscode://file/$(pwd)/ --output-type dot | dot -T svg | depcruise-wrap-stream-in-html | browser",
45
- "depcruise:graph:diff:dev": "depcruise src --prefix vscode://file/$(pwd)/ --output-type dot --reaches \"$(watskeburt $SHA -T regex)\"| dot -T svg | depcruise-wrap-stream-in-html | browser",
46
- "depcruise:graph:diff:mermaid": "depcruise src tools --output-type mermaid --output-to - --reaches \"$(watskeburt $SHA -T regex)\"",
47
- "depcruise:html": "depcruise src tools --output-type err-html --output-to dependency-violation-report.html",
48
- "format": "prettier --log-level warn --write \"**/*.{md,ts,json,yml}\"",
49
- "prepare": "husky install",
50
- "scm:stage": "git add .",
51
- "test": "c8 node --no-warnings --loader 'ts-node/esm' --test-reporter ./tools/dot-with-summary.reporter.js --test src/*.test.ts",
52
- "update-dependencies": "npm run upem:update && npm run upem:install && npm run check",
53
- "upem-outdated": "npm outdated --json --long | upem --dry-run",
54
- "upem:install": "npm install",
55
- "upem:update": "npm outdated --json --long | upem | pbcopy && pbpaste",
56
- "version": "npm run build && npm run scm:stage"
57
- },
58
13
  "keywords": [
59
14
  "CODEOWNERS"
60
15
  ],
@@ -63,28 +18,16 @@
63
18
  "homepage": "https://github.com/sverweij/virtual-code-owners",
64
19
  "repository": {
65
20
  "type": "git",
66
- "url": "git+https://github.com/sverweij/virtual-code-owners"
21
+ "url": "git+https://github.com/sverweij/virtual-code-owners.git"
67
22
  },
68
23
  "bugs": {
69
24
  "url": "https://github.com/sverweij/virtual-code-owners/issues"
70
25
  },
71
- "devDependencies": {
72
- "@types/node": "20.5.8",
73
- "c8": "8.0.1",
74
- "dependency-cruiser": "13.1.5",
75
- "husky": "8.0.3",
76
- "lint-staged": "14.0.1",
77
- "prettier": "3.0.3",
78
- "ts-node": "10.9.1",
79
- "typescript": "5.2.2",
80
- "upem": "8.0.0",
81
- "watskeburt": "1.0.1"
82
- },
83
26
  "dependencies": {
84
27
  "ajv": "8.12.0",
85
- "yaml": "2.3.2"
28
+ "yaml": "2.3.3"
86
29
  },
87
30
  "engines": {
88
- "node": "^16.19.0||^18.11.0||>=20.0.0"
31
+ "node": "^18.11.0||>=20.0.0"
89
32
  }
90
- }
33
+ }
@@ -1,6 +0,0 @@
1
- import type { ITeamMap, IVirtualCodeOwnersCST } from "./types.js";
2
- export default function generateCodeOwners(
3
- pVirtualCodeOwners: IVirtualCodeOwnersCST,
4
- pTeamMap: ITeamMap,
5
- pGeneratedWarning?: string,
6
- ): string;
@@ -1,6 +0,0 @@
1
- import type { ITeamMap, IVirtualCodeOwnersCST } from "./types.js";
2
- export default function generateLabelerYml(
3
- pCodeOwners: IVirtualCodeOwnersCST,
4
- pTeamMap: ITeamMap,
5
- pGeneratedWarning?: string,
6
- ): string;
@@ -1,18 +0,0 @@
1
- import type { IAnomaly, ITeamMap, IVirtualCodeOwnersCST } from "./types.js";
2
- /**
3
- * parses (virtual) codeowners as a string into a virtual codeowners CST
4
- * which can be used to do further rocessing on (e.g. generate codeowners,
5
- * validate etc.)
6
- *
7
- * @param pVirtualCodeOwnersAsString CODEOWNERS or VIRTUAL-CODE-OWNERS.txt file to parse
8
- * @param pTeamMap a virtual team map ()
9
- * @returns a virtual code owners CST
10
- */
11
- export declare function parse(
12
- pVirtualCodeOwnersAsString: string,
13
- pTeamMap?: ITeamMap,
14
- ): IVirtualCodeOwnersCST;
15
-
16
- export declare function getAnomalies(
17
- pVirtualCodeOwners: IVirtualCodeOwnersCST,
18
- ): IAnomaly[];
package/types/types.d.ts DELETED
@@ -1,45 +0,0 @@
1
- export interface ITeamMap {
2
- [teamName: string]: string[];
3
- }
4
-
5
- export type IVirtualCodeOwnersCST = IVirtualCodeOwnerLine[];
6
- export type IVirtualCodeOwnerLine = IBoringCSTLine | IInterestingCSTLine;
7
- export interface IBoringCSTLine {
8
- type: "comment" | "ignorable-comment" | "empty" | "unknown";
9
- line: number;
10
- raw: string;
11
- }
12
- export interface IInterestingCSTLine {
13
- type: "rule";
14
- line: number;
15
- filesPattern: string;
16
- spaces: string;
17
- users: IUser[];
18
- inlineComment: string;
19
- raw: string;
20
- }
21
- export type UserType =
22
- | "virtual-team-name"
23
- | "e-mail-address"
24
- | "other-user-or-team"
25
- | "invalid";
26
- export type IUser = {
27
- type: UserType;
28
- userNumberWithinLine: number;
29
- bareName: string;
30
- raw: string;
31
- };
32
-
33
- export type IAnomaly = ILineAnomaly | IUserAnomaly;
34
- export interface ILineAnomaly {
35
- type: "invalid-line";
36
- line: number;
37
- raw: string;
38
- }
39
- export interface IUserAnomaly {
40
- type: "invalid-user";
41
- line: number;
42
- userNumberWithinLine: number;
43
- bareName: string;
44
- raw: string;
45
- }