microsoft/TypeAgent

Public

mirrored from https://github.com/microsoft/TypeAgentAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
1adc19e53b9dfd93f84ff36cd1ee7fdfdefe1548

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

ts/tools/scripts/repo-policy-check.mjs

207lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4import { execSync } from "node:child_process";
5import fs from "node:fs";
6import path from "node:path";
7import { rules as copyrightHeadersRules } from "./policyChecks/copyrightHeaders.mjs";
8import { rules as npmPackageRules } from "./policyChecks/npmPackage.mjs";
9import chalk from "chalk";
10
11/********************************************************
12 * Rules
13 ********************************************************/
14
15const rules = [...copyrightHeadersRules, ...npmPackageRules];
16
17/********************************************************
18 * Main
19 ********************************************************/
20
21function getCheckFiles() {
22 const files = execSync("git ls-tree --full-tree -r --name-only HEAD", {
23 encoding: "utf-8",
24 });
25 return files.trim().split("\n");
26}
27
28class Repofile {
29 constructor(repo, name) {
30 this.name = name;
31 this.repo = repo;
32 }
33 get fullpath() {
34 return path.join(this.repo, this.name);
35 }
36 get content() {
37 if (this._json !== undefined) {
38 return `${JSON.stringify(this._json, undefined, 2)}\n`;
39 }
40 if (this._content === undefined) {
41 this._content = fs.readFileSync(this.fullpath, "utf-8");
42 this._original = this._content;
43 }
44 return this._content;
45 }
46 get json() {
47 if (this._json === undefined) {
48 this._json = JSON.parse(this.content);
49 this._content = undefined;
50 }
51 return this._json;
52 }
53 set content(content) {
54 this._content = content;
55 this._json = undefined;
56 }
57 set json(json) {
58 this._json = json;
59 this._content = undefined;
60 }
61 save() {
62 if (this._original !== undefined) {
63 const content = this.content;
64 if (content !== this._original) {
65 fs.writeFileSync(this.fullpath, content);
66 this.original = content;
67 return true;
68 }
69 }
70 return false;
71 }
72}
73
74function loadConfig(repo) {
75 const configPath = path.join(repo, ".repolicy.json");
76 if (!fs.existsSync(configPath)) {
77 return {};
78 }
79 return JSON.parse(fs.readFileSync(configPath, "utf-8"));
80}
81
82function checkFile(file, config) {
83 const exclude = config.exclude;
84 if (exclude === undefined) {
85 return true;
86 }
87 return !exclude.some((prefix) => file.startsWith(prefix));
88}
89
90const colors = [
91 chalk.cyanBright,
92 chalk.yellowBright,
93 chalk.greenBright,
94 chalk.blueBright,
95 chalk.magentaBright,
96 chalk.redBright,
97 chalk.green,
98 chalk.yellow,
99 chalk.blue,
100 chalk.magenta,
101 chalk.cyan,
102];
103
104function main() {
105 let check = 0;
106 let fixed = 0;
107 let failed = 0;
108 let failedFiles = 0;
109
110 const files = getCheckFiles();
111
112 const repo = execSync("git rev-parse --show-toplevel", {
113 encoding: "utf-8",
114 }).trim();
115 const config = loadConfig(repo);
116 const fix = process.argv.includes("--fix");
117 const verbose = process.argv.includes("--verbose");
118 for (const file of files) {
119 if (!checkFile(file, config)) {
120 continue;
121 }
122 const rulesChecked = [];
123 let coloredFile = chalk.red(`${file}:`);
124 const repoFile = new Repofile(repo, file);
125 let failedFile = false;
126 let ruleIndex = 0;
127 for (const rule of rules) {
128 if (!rule.match.test(file)) {
129 continue;
130 }
131 check++;
132 const ruleColor = colors[ruleIndex++ % colors.length];
133 const coloredRule = ruleColor(` ${rule.name}: `);
134 rulesChecked.push(coloredRule);
135 const result = rule.check(repoFile, fix);
136 if (result === true) {
137 continue;
138 }
139 if (coloredFile !== undefined) {
140 console.log(coloredFile);
141 coloredFile = undefined;
142 }
143 if (fix && result === false) {
144 console.log(`${coloredRule}Fixed`);
145 fixed++;
146 continue;
147 }
148 if (Array.isArray(result)) {
149 for (const message of result) {
150 console.error(`${coloredRule}${message}`);
151 }
152 } else {
153 console.error(`${coloredRule}${result}`);
154 }
155 failed++;
156 failedFile = true;
157 }
158 if (failedFile) {
159 failedFiles++;
160 }
161 if (verbose) {
162 if (rulesChecked.length === 0) {
163 if (coloredFile !== undefined) {
164 console.log(coloredFile);
165 coloredFile = undefined;
166 }
167 console.log(` No rules matched`);
168 } else {
169 if (coloredFile !== undefined) {
170 console.log(chalk.green(`${file}:`));
171 coloredFile = undefined;
172 }
173 console.log(
174 rulesChecked.map((name) => `${name}Passed`).join("\n"),
175 );
176 }
177 }
178 if (fix) {
179 if (repoFile.save()) {
180 if (coloredFile !== undefined) {
181 console.log(coloredFile);
182 coloredFile = undefined;
183 }
184 console.log(` Saved changes`);
185 }
186 }
187 if (coloredFile === undefined) {
188 console.log();
189 }
190 }
191 if (failed > 0) {
192 console.error(
193 chalk.red(
194 `Failed ${failed}/${check} checks. ${failedFiles}/${files.length} files. ${fixed !== 0 ? `Fixed ${fixed}` : ""}`,
195 ),
196 );
197 process.exit(1);
198 }
199
200 console.log(
201 chalk.green(
202 `Passed ${check} checks. ${files.length} files. ${fixed !== 0 ? `Fixed ${fixed}` : ""}`,
203 ),
204 );
205}
206
207main();
208