virtual-code-owners 6.0.0 → 6.1.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
@@ -71,7 +71,7 @@ This is where `virtual-code-owners` comes in.
71
71
 
72
72
  ### VIRTUAL-CODEOWNERS.txt
73
73
 
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.
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
75
  The only difference between VIRTUAL-CODEOWNERS.txt and a CODEOWNERS file is that
76
76
  the _teams_ the former uses might not exist yet, except in a `virtual-teams.yml`.
77
77
  This enables you to write a _much_ easier to maintain list of code owners.
@@ -189,10 +189,10 @@ file that will check that for you.
189
189
  - ~~Currently only works for _user names_ to identify team members - not for e-mail
190
190
  addresses.~~
191
191
  Works with both user names and e-mail addresses
192
- - _virtual-code-owners_ assumes the VIRTUAL-CODEOWNERS.txt is a valid CODEOWNERS
193
- file and the virtual-teams.yml is a valid yaml file with teams names as keys
194
- and team members as arrays under these. It will likely throw errors when this
195
- assumption is not met, but the error-messages might be cryptic.
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.
196
196
 
197
197
  ### Why the `.txt` extension?
198
198
 
@@ -236,3 +236,13 @@ npx virtual-code-owners --emitLabeler
236
236
 
237
237
  If you have an alternate file location for the `labeler.yml` you can specify that
238
238
  with virtual-code-owner's `--labelerLocation` parameter.
239
+
240
+ ### Can I just run virtual-code-owners to validate VIRTUAL-CODEOWNERS.txt & virtual-teams.yml?
241
+
242
+ So _without_ generating any output?
243
+
244
+ Yes. Use the `--dryRun` command line option:
245
+
246
+ ```
247
+ npx virtual-code-owners --dryRun
248
+ ```
@@ -20,7 +20,7 @@ export default function generateCodeOwners(pVirtualCodeOwners, pTeamMap, pGenera
20
20
  function generateLine(pCSTLine, pTeamMap) {
21
21
  if (pCSTLine.type === "rule") {
22
22
  const lUserNames = uniq(pCSTLine.users.flatMap((pUser) => expandTeamToUserNames(pUser, pTeamMap)))
23
- .sort()
23
+ .sort(compareUserNames)
24
24
  .join(" ");
25
25
  return pCSTLine.filesPattern + pCSTLine.spaces + lUserNames;
26
26
  }
@@ -41,6 +41,9 @@ function userNameToCodeOwner(pUserName) {
41
41
  }
42
42
  return `@${pUserName}`;
43
43
  }
44
+ function compareUserNames(pLeftName, pRightName) {
45
+ return pLeftName.toLowerCase() > pRightName.toLowerCase() ? 1 : -1;
46
+ }
44
47
  function uniq(pUserNames) {
45
48
  return Array.from(new Set(pUserNames));
46
49
  }
package/dist/main.js CHANGED
@@ -25,6 +25,8 @@ Options:
25
25
  (default: false)
26
26
  --labelerLocation [file-name] The location of the labeler.yml file
27
27
  (default: ".github/labeler.yml")
28
+ --dryRun Just validate inputs, don't generate
29
+ outputs (default: false)
28
30
  -h, --help display help for command`;
29
31
  export function cli(pArguments = process.argv.slice(2), pOutStream = process.stdout, pErrorStream = process.stderr) {
30
32
  try {
@@ -72,6 +74,10 @@ function getOptions(pArguments) {
72
74
  type: "string",
73
75
  default: ".github/labeler.yml",
74
76
  },
77
+ dryRun: {
78
+ type: "boolean",
79
+ default: false,
80
+ },
75
81
  help: { type: "boolean", short: "h", default: false },
76
82
  version: { type: "boolean", short: "V", default: false },
77
83
  },
@@ -84,14 +90,18 @@ function main(pOptions, pErrorStream) {
84
90
  const lTeamMap = readTeamMap(pOptions.virtualTeams);
85
91
  const lVirtualCodeOwners = readVirtualCodeOwners(pOptions.virtualCodeOwners, lTeamMap);
86
92
  const lCodeOwnersContent = generateCodeOwners(lVirtualCodeOwners, lTeamMap);
87
- writeFileSync(pOptions.codeOwners, lCodeOwnersContent, {
88
- encoding: "utf-8",
89
- });
90
- if (pOptions.emitLabeler) {
91
- const lLabelerContent = generateLabelerYml(lVirtualCodeOwners, lTeamMap);
92
- writeFileSync(pOptions.labelerLocation, lLabelerContent, {
93
+ if (!pOptions.dryRun) {
94
+ writeFileSync(pOptions.codeOwners, lCodeOwnersContent, {
93
95
  encoding: "utf-8",
94
96
  });
97
+ }
98
+ if (pOptions.emitLabeler) {
99
+ const lLabelerContent = generateLabelerYml(lVirtualCodeOwners, lTeamMap);
100
+ if (!pOptions.dryRun) {
101
+ writeFileSync(pOptions.labelerLocation, lLabelerContent, {
102
+ encoding: "utf-8",
103
+ });
104
+ }
95
105
  pErrorStream.write(`${EOL}Wrote '${pOptions.codeOwners}' AND '${pOptions.labelerLocation}'${EOL}${EOL}`);
96
106
  }
97
107
  else {
@@ -8,10 +8,10 @@ export default function readTeamMap(pVirtualTeamsFileName) {
8
8
  encoding: "utf-8",
9
9
  });
10
10
  const lTeamMap = parseYaml(lVirtualTeamsAsAString);
11
- validateTeamMap(lTeamMap, pVirtualTeamsFileName);
11
+ assertTeamMapValid(lTeamMap, pVirtualTeamsFileName);
12
12
  return lTeamMap;
13
13
  }
14
- function validateTeamMap(pTeamMap, pVirtualTeamsFileName) {
14
+ function assertTeamMapValid(pTeamMap, pVirtualTeamsFileName) {
15
15
  const ajv = new Ajv({
16
16
  allErrors: true,
17
17
  verbose: true,
@@ -1,6 +1,6 @@
1
1
  import { readFileSync } from "node:fs";
2
2
  import { EOL } from "node:os";
3
- import { getAnomalies, parse as parseVirtualCodeOwners } from "./parse.js";
3
+ import { getAnomalies, parse as parseVirtualCodeOwners, } from "./parse-virtual-code-owners.js";
4
4
  export default function readVirtualCodeOwners(pVirtualCodeOwnersFileName, pTeamMap) {
5
5
  const lVirtualCodeOwnersAsAString = readFileSync(pVirtualCodeOwnersFileName, {
6
6
  encoding: "utf-8",
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const VERSION = "6.0.0";
1
+ export const VERSION = "6.1.1";
@@ -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/4.1.0",
5
+ "$id": "org.js.virtual-code-owners/6.0.0",
6
6
  "type": "object",
7
7
  "additionalProperties": {
8
8
  "type": "array",
package/package.json CHANGED
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "virtual-code-owners",
3
- "version": "6.0.0",
4
- "description": "Merges a VIRTUAL-CODEOWNERS.txt and a virtual-teams.yml into CODEOWNERS",
3
+ "version": "6.1.1",
4
+ "description": "Makes your CODEOWNERS file liveable again",
5
5
  "type": "module",
6
6
  "exports": {
7
7
  "parse": [
8
8
  {
9
- "import": "./dist/parse.js",
10
- "types": "./types/parse.d.ts"
9
+ "import": "./dist/parse-virtual-code-owners.js",
10
+ "types": "./types/parse-virtual-code-owners.d.ts"
11
11
  },
12
- "./dist/parse.js"
12
+ "./dist/parse-virtual-code-owners.js"
13
13
  ],
14
14
  "generateCodeOwners": [
15
15
  {
@@ -30,7 +30,8 @@
30
30
  "types": "types/types.d.ts",
31
31
  "bin": "dist/cli.js",
32
32
  "files": [
33
- "dist",
33
+ "dist/",
34
+ "types/",
34
35
  "package.json",
35
36
  "README.md",
36
37
  "LICENSE"
@@ -44,7 +45,7 @@
44
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",
45
46
  "depcruise:graph:diff:mermaid": "depcruise src tools --output-type mermaid --output-to - --reaches \"$(watskeburt $SHA -T regex)\"",
46
47
  "depcruise:html": "depcruise src tools --output-type err-html --output-to dependency-violation-report.html",
47
- "format": "prettier --loglevel warn --write \"**/*.{md,ts,json,yml}\"",
48
+ "format": "prettier --log-level warn --write \"**/*.{md,ts,json,yml}\"",
48
49
  "prepare": "husky install",
49
50
  "scm:stage": "git add .",
50
51
  "test": "NODE_OPTIONS=--no-warnings c8 mocha",
@@ -69,17 +70,17 @@
69
70
  },
70
71
  "devDependencies": {
71
72
  "@types/mocha": "10.0.1",
72
- "@types/node": "20.3.1",
73
+ "@types/node": "20.4.1",
73
74
  "c8": "8.0.0",
74
- "dependency-cruiser": "13.0.3",
75
+ "dependency-cruiser": "13.1.0",
75
76
  "husky": "8.0.3",
76
- "lint-staged": "13.2.2",
77
+ "lint-staged": "13.2.3",
77
78
  "mocha": "10.2.0",
78
- "prettier": "2.8.8",
79
+ "prettier": "3.0.0",
79
80
  "ts-node": "10.9.1",
80
- "typescript": "5.1.3",
81
+ "typescript": "5.1.6",
81
82
  "upem": "8.0.0",
82
- "watskeburt": "0.11.3"
83
+ "watskeburt": "0.11.6"
83
84
  },
84
85
  "dependencies": {
85
86
  "ajv": "8.12.0",
@@ -0,0 +1,6 @@
1
+ import type { ITeamMap, IVirtualCodeOwnersCST } from "./types.js";
2
+ export default function generateCodeOwners(
3
+ pVirtualCodeOwners: IVirtualCodeOwnersCST,
4
+ pTeamMap: ITeamMap,
5
+ pGeneratedWarning?: string,
6
+ ): string;
@@ -0,0 +1,6 @@
1
+ import type { ITeamMap, IVirtualCodeOwnersCST } from "./types.js";
2
+ export default function generateLabelerYml(
3
+ pCodeOwners: IVirtualCodeOwnersCST,
4
+ pTeamMap: ITeamMap,
5
+ pGeneratedWarning?: string,
6
+ ): string;
@@ -0,0 +1,18 @@
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[];
@@ -0,0 +1,44 @@
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
+ raw: string;
19
+ }
20
+ export type UserType =
21
+ | "virtual-team-name"
22
+ | "e-mail-address"
23
+ | "other-user-or-team"
24
+ | "invalid";
25
+ export type IUser = {
26
+ type: UserType;
27
+ userNumberWithinLine: number;
28
+ bareName: string;
29
+ raw: string;
30
+ };
31
+
32
+ export type IAnomaly = ILineAnomaly | IUserAnomaly;
33
+ export interface ILineAnomaly {
34
+ type: "invalid-line";
35
+ line: number;
36
+ raw: string;
37
+ }
38
+ export interface IUserAnomaly {
39
+ type: "invalid-user";
40
+ line: number;
41
+ userNumberWithinLine: number;
42
+ bareName: string;
43
+ raw: string;
44
+ }
File without changes