85a51734ec
Signed-off-by: Camila Belo <camilaibs@gmail.com>
184 lines
4.9 KiB
JavaScript
Executable File
184 lines
4.9 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
/*
|
|
* Copyright 2023 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 path = require('path');
|
|
const { execFile: execFileCb } = require('child_process');
|
|
const { promisify } = require('util');
|
|
|
|
const execFile = promisify(execFileCb);
|
|
|
|
const rootDirectory = path.resolve(__dirname, '..');
|
|
const pluginsDirectory = path.resolve(rootDirectory, 'plugins');
|
|
|
|
async function run(command, ...args) {
|
|
const { stdout, stderr } = await execFile(command, args, {
|
|
cwd: rootDirectory,
|
|
});
|
|
|
|
if (stderr) {
|
|
console.error(stderr);
|
|
}
|
|
|
|
return stdout.trim();
|
|
}
|
|
|
|
function findLatestValidCommit(commits, directoryPath) {
|
|
return commits.find(commit => {
|
|
const { author, message, files } = commit;
|
|
|
|
// exclude merge commits
|
|
if (message.startsWith('Merge pull request #')) {
|
|
return false;
|
|
}
|
|
|
|
// exclude commits authored by a bot
|
|
if (author.includes('[bot]')) {
|
|
return false;
|
|
}
|
|
|
|
// exclude core maintainers' commits
|
|
if (
|
|
[
|
|
'ben@blam.sh',
|
|
'freben@gmail.com',
|
|
'poldsberg@gmail.com',
|
|
'johan.haals@gmail.com',
|
|
].some(email => author.includes(email))
|
|
) {
|
|
return false;
|
|
}
|
|
|
|
// ignore multiple plugins changes
|
|
if (
|
|
files.some(file => {
|
|
const fileFullPath = path.resolve(rootDirectory, file);
|
|
if (!fileFullPath.startsWith(pluginsDirectory)) {
|
|
return false;
|
|
}
|
|
const pluginBasePath = directoryPath.replace(
|
|
/-(backend|common|react|node).*$/,
|
|
'',
|
|
);
|
|
return !fileFullPath.startsWith(pluginBasePath);
|
|
})
|
|
) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
});
|
|
}
|
|
|
|
async function getPluginDirectory(directoryName) {
|
|
const directoryPath = path.resolve(pluginsDirectory, directoryName);
|
|
const packageJson = await fs.readJson(
|
|
path.resolve(directoryPath, 'package.json'),
|
|
);
|
|
return { directoryName, directoryPath, packageJson };
|
|
}
|
|
|
|
function parseCommitsLog(log) {
|
|
const lines = log.split('\n');
|
|
return lines.reduce((commits, line) => {
|
|
if (!line) return commits;
|
|
if (line.includes(';')) {
|
|
const [author, message, date] = line.split(';');
|
|
return [...commits, { author, message, date, files: [] }];
|
|
}
|
|
const { files, ...commit } = commits.pop();
|
|
return [...commits, { ...commit, files: [...files, line] }];
|
|
}, []);
|
|
}
|
|
|
|
async function readDirectoryCommits(directoryName) {
|
|
const maxCount = 100;
|
|
const directoryPath = path.resolve(pluginsDirectory, directoryName);
|
|
|
|
const logOutput = await run(
|
|
'git',
|
|
'log',
|
|
'origin/master',
|
|
'--name-only',
|
|
`--max-count=${maxCount}`,
|
|
'--pretty=format:%an <%ae>;%s;%as',
|
|
'--',
|
|
// ignore changes on README and package.json files
|
|
path.posix.resolve(directoryPath, 'src'),
|
|
`:(exclude)${path.posix.resolve(directoryPath, 'src', '**', '*.test.*')}`,
|
|
);
|
|
|
|
return parseCommitsLog(logOutput);
|
|
}
|
|
|
|
async function getLatestDirectoryCommit({ directoryName, directoryPath }) {
|
|
console.log(`🔎 Reading data for ${directoryName}`);
|
|
|
|
const commits = await readDirectoryCommits(directoryName);
|
|
|
|
const commit = findLatestValidCommit(commits, directoryPath) ?? {
|
|
author: '-',
|
|
message: '-',
|
|
date: '-',
|
|
};
|
|
|
|
return { plugin: directoryName, ...commit };
|
|
}
|
|
|
|
function isValidDirectory({ packageJson }) {
|
|
const roles = [
|
|
'frontend-plugin',
|
|
'frontend-plugin-module',
|
|
'backend-plugin',
|
|
'backend-plugin-module',
|
|
];
|
|
return roles.includes(packageJson?.backstage?.role ?? '');
|
|
}
|
|
|
|
async function main() {
|
|
const directoryNames = fs
|
|
.readdirSync(pluginsDirectory, { withFileTypes: true })
|
|
.filter(dirent => dirent.isDirectory())
|
|
.map(dirent => dirent.name);
|
|
|
|
const directories = await Promise.all(directoryNames.map(getPluginDirectory));
|
|
|
|
const commits = await Promise.all(
|
|
directories.filter(isValidDirectory).map(getLatestDirectoryCommit),
|
|
);
|
|
|
|
const fileName = 'plugins-report.csv';
|
|
|
|
const fileContent = [
|
|
'Plugin;Author;Message;Date',
|
|
...commits.map(c => `${c.plugin};${c.author};${c.message};${c.date}`),
|
|
];
|
|
|
|
console.log(`📊 Generating plugins report...`);
|
|
|
|
fs.writeFile(fileName, fileContent.join('\n'), err => {
|
|
if (err) throw err;
|
|
});
|
|
|
|
console.log(`📄 Report generated at ${fileName}`);
|
|
}
|
|
|
|
main(process.argv.slice(2)).catch(error => {
|
|
console.error(error.stack || error);
|
|
process.exit(1);
|
|
});
|