The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
grafana/scripts/cli/generateBettererIssues.ts

148 lines
4.0 KiB

import { betterer, BettererFileIssues } from '@betterer/betterer';
import Codeowners from 'codeowners';
import { readFile, writeFile } from 'fs/promises';
import { template } from 'lodash';
import path from 'path';
import { hideBin } from 'yargs/helpers';
import yargs from 'yargs/yargs';
const argv = yargs(hideBin(process.argv))
.option('template', {
demandOption: true,
alias: 't',
describe: 'Path to a template to use for each issue. See source bettererIssueTemplate.md for an example',
type: 'string',
default: './scripts/cli/bettererIssueTemplate.md',
})
.option('output', {
demandOption: true,
alias: 'o',
describe: 'Path to directory to save issues to',
type: 'string',
})
.option('test', {
demandOption: true,
alias: 'b',
describe: 'Name of the betterer test to produce the report for',
type: 'string',
})
.option('test-message', {
alias: 'm',
describe: 'Filter issues containing this message',
type: 'string',
})
.option('single-owner', {
type: 'boolean',
alias: 's',
describe: 'Only use first owner for files with multiple owners',
default: false,
})
.usage('Usage: yarn betterer:issues -t [path] -o [path] -b [string]')
.version(false)
.help('help').argv;
interface FileDetails {
fileName: string;
issueCount: number;
issues: BettererFileIssues;
}
// really dumb and simple pluralize function. not meant to be exhaustive
function plural(word: string, count: number) {
if (count === 0 || count > 1) {
return word + 's';
}
return word;
}
async function main() {
const args = await argv;
const templatePath = path.resolve(args.template);
const outputPath = path.resolve(args.output);
const templateString = (await readFile(templatePath)).toString();
const owners = new Codeowners();
const results = await betterer.results();
const filesByOwner: Record<string, FileDetails[]> = {};
for (const testResults of results.resultSummaries) {
if (testResults.name !== args.test) {
continue;
}
if (typeof testResults.details === 'string') {
continue;
}
for (const _fileName in testResults.details) {
const fileName = _fileName.replace(process.cwd() + '/', '');
const _details = testResults.details[_fileName];
let ownersForFile = owners.getOwner(fileName);
if (args.singleOwner) {
ownersForFile = [ownersForFile[0]];
}
const filterByMessage = args.testMessage?.length ? args.testMessage.toLowerCase() : undefined;
const filteredDetails = filterByMessage
? _details.filter((v) => v.message.toLowerCase().includes(filterByMessage))
: _details;
const numberOfIssues = filteredDetails.length;
if (numberOfIssues === 0) {
continue;
}
for (const owner of ownersForFile) {
if (!filesByOwner[owner]) {
filesByOwner[owner] = [];
}
filesByOwner[owner].push({
fileName,
issueCount: numberOfIssues,
issues: filteredDetails,
});
}
}
}
const contexts = Object.entries(filesByOwner).map(([owner, files]) => {
const fileCount = files.length;
const totalIssueCount = files.reduce((acc, v) => acc + v.issueCount, 0);
return {
owner,
files,
fileCount,
totalIssueCount,
issueFilter: args.test,
issueMessageFilter: args.testMessage,
};
});
const compiledTemplate = template(templateString, { imports: { plural } });
for (const context of contexts) {
const fileSafeOwner = context.owner.replace(/[^a-z0-9-]/gi, '_');
const fileSafeTestName = args.test.replace(/[^a-z0-9-]/gi, '_');
const outputFilePath = path.join(outputPath, `${fileSafeTestName}_${fileSafeOwner}.txt`);
const printed = compiledTemplate(context);
await writeFile(outputFilePath, printed);
const indented = printed
.split('\n')
.map((v) => `\t${v}`)
.join('\n');
console.log(`Printed issue for owner`, context.owner, 'to', outputFilePath);
console.log(indented);
}
}
main().catch(console.error);