scripts: add script to print code ownership

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2024-02-05 13:21:15 +01:00
parent c1c0cb047b
commit 278d9959c1
3 changed files with 160 additions and 5 deletions
+1
View File
@@ -92,6 +92,7 @@
"prettier": "^2.2.1",
"semver": "^7.5.3",
"shx": "^0.3.2",
"sloc": "^0.3.1",
"ts-node": "^10.4.0",
"typescript": "~5.1.0"
},
+139
View File
@@ -0,0 +1,139 @@
#!/usr/bin/env node
/*
* Copyright 2021 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const fs = require('fs-extra');
const globby = require('globby');
const sloc = require('sloc');
const codeownersUtils = require('codeowners-utils');
const { resolve: resolvePath } = require('path');
async function loadOwners(rootDir) {
const codeowners = await codeownersUtils.loadOwners(rootDir);
return function getOwners(path) {
const { owners } = codeownersUtils.matchFile(path, codeowners);
if (
owners.includes('@backstage/maintainers') &&
owners.includes('@backstage/reviewers')
) {
return owners.filter(owner => owner !== '@backstage/reviewers');
}
return owners;
};
}
function createLocCounter(rootDir) {
return async path => {
const content = await fs.readFile(resolvePath(rootDir, path), 'utf-8');
const stats = sloc(content, 'ts');
return stats.source;
};
}
async function printOwnerDirectories(allFiles, getOwners, getLoc, onlyOwner) {
const countByPath = new Map();
let total = 0;
let totalShare = 0;
for (const file of allFiles) {
const owners = getOwners(file);
const loc = await getLoc(file);
if (owners.includes(onlyOwner)) {
const share = loc / owners.length;
total += loc;
totalShare += share;
const path = file.split('/').slice(0, 2).join('/');
if ((await fs.stat(path)).isDirectory()) {
countByPath.set(path, (countByPath.get(path) || 0) + share);
}
}
}
const sortedPaths = Array.from(countByPath)
.map(([path, loc]) => ({ path, loc: Math.round(loc) }))
.sort((a, b) => b.loc - a.loc);
const maxPathLen = Math.max(...sortedPaths.map(({ path }) => path.length));
for (const { path, loc } of sortedPaths) {
console.log(`${path.padEnd(maxPathLen)} ${loc}`);
}
console.log();
console.log('Total share:', Math.round(totalShare));
console.log('Total lines of code:', total);
}
async function printAllOwners(allFiles, getOwners, getLoc) {
const countByOwners = new Map();
let total = 0;
for (const file of allFiles) {
const owners = getOwners(file);
const loc = await getLoc(file);
total += loc;
const share = loc / owners.length;
for (const owner of owners) {
countByOwners.set(owner, (countByOwners.get(owner) || 0) + share);
}
}
const sortedOwners = Array.from(countByOwners)
.map(([owner, loc]) => ({ owner, loc: Math.round(loc) }))
.sort((a, b) => b.loc - a.loc);
const maxOwnerLen = Math.max(
...sortedOwners.map(({ owner }) => owner.length),
);
for (const { owner, loc } of sortedOwners) {
console.log(`${owner.padEnd(maxOwnerLen)} ${loc}`);
}
console.log();
console.log('Total lines of code:', total);
}
async function main(onlyOwner) {
const rootDir = resolvePath(__dirname, '..');
const allFiles = await globby(
[
'**/*.{js,jsx,ts,tsx,mjs,cjs}',
'!**/*.{generated,test}.*',
'!**/{__fixtures__,fixtures}',
],
{
cwd: rootDir,
gitignore: true,
followSymbolicLinks: false,
},
);
const getOwners = await loadOwners(rootDir);
const getLoc = createLocCounter(rootDir);
if (onlyOwner) {
await printOwnerDirectories(allFiles, getOwners, getLoc, onlyOwner);
} else {
await printAllOwners(allFiles, getOwners, getLoc);
}
}
main(...process.argv.slice(2)).catch(err => {
console.error(err.stack);
process.exit(1);
});
+20 -5
View File
@@ -22644,12 +22644,12 @@ __metadata:
languageName: node
linkType: hard
"cli-table@npm:^0.3.1":
version: 0.3.6
resolution: "cli-table@npm:0.3.6"
"cli-table@npm:^0.3.1, cli-table@npm:^0.3.11":
version: 0.3.11
resolution: "cli-table@npm:0.3.11"
dependencies:
colors: 1.0.3
checksum: b0cd08578c810240920438cc2b3ffb4b4f5106b29f3362707f1d8cfc0c0440ad2afb70b96e30ce37f72f0ffe1e844ae7341dde4df17d51ad345eb186a5903af2
checksum: 59fb61f992ac9bc8610ed98c72bf7f5d396c5afb42926b6747b46b0f8bb98a0dfa097998e77542ac334c1eb7c18dbf4f104d5783493273c5ec4c34084aa7c663
languageName: node
linkType: hard
@@ -39193,7 +39193,7 @@ __metadata:
languageName: node
linkType: hard
"readdirp@npm:~3.6.0":
"readdirp@npm:^3.3.0, readdirp@npm:~3.6.0":
version: 3.6.0
resolution: "readdirp@npm:3.6.0"
dependencies:
@@ -40117,6 +40117,7 @@ __metadata:
prettier: ^2.2.1
semver: ^7.5.3
shx: ^0.3.2
sloc: ^0.3.1
ts-node: ^10.4.0
typescript: ~5.1.0
languageName: unknown
@@ -40900,6 +40901,20 @@ __metadata:
languageName: node
linkType: hard
"sloc@npm:^0.3.1":
version: 0.3.1
resolution: "sloc@npm:0.3.1"
dependencies:
async: ^3.2.4
cli-table: ^0.3.11
commander: ^11.0.0
readdirp: ^3.3.0
bin:
sloc: bin/sloc
checksum: 3075150912b4fbb90167c446b155fcfffa20351245d7ae2a7139b2c3ba5dd9fd01fe97fc11a917d5f6a66778c3a9ab4c6ceff0bcd234338f38942fb7238ab927
languageName: node
linkType: hard
"slugify@npm:^1.6.4":
version: 1.6.6
resolution: "slugify@npm:1.6.6"