mirror of
https://github.com/grafana/grafana.git
synced 2026-02-03 20:49:50 -05:00
* feat(script): generate a source file x teams manifest from CODEOWNERS * feat(script): unit tests + coverage report only for files owned by team * feat(script): calculate CODEOWNERS metadata * refactor(script): export a pure codeowners manifest generation function * refactor(script): export a pure test coverage by team function * refactor(script): generate raw JSONL codeowners data from Node.js script * feat(script): put codeowners manifest all together in one script * refactor(scripts): group consistently with NPM script name * refactor(scripts): deduplicate constants for file paths etc. * refactor(scripts): make console output cute 💅✨ * refactor(tests): make coverage by "owner" directory more human readable * refactor(scripts): use consistent naming "codeowner" instead of "team" * chore(codeowners): mark DataViz as owners of scripts for now * chore(todo): leave a note where coverage metrics should be emitted later * fix(gitignore): ignore root codeowners-manifest directory not scripts/* * refactor(script): rename manifest to generate for clarity * docs(readme): add a brief README describing new scrips * chore(linter): ignore temporary files in prettier, fix whitespace format * refactor(script): simplify Jest config by using team files list directly * refactor(script): simplify script, partition sourceFiles and testFiles * refactor(script): simplify and parallelize manifest write operations * fix(script): handle errors for JSONL line reader * refactor(script): use Map instead of POJOs * fix(script): handle errors when streaming raw JSONL output * fix(script): add error handling, and use promise API for metadata check * fix(reporter): suppress duplicate Jest CLI coverage report output * refactor(script): simplify with fs promises API for consistency * fix(script): error handling for cp spawn-ed process * refactor(script): use Promise API for mkdir + exists * refactor(script): use fs Promise API * refactor(script): use fs Promise API * fix(script): same allow list for sourceFilter and all Jest config rules Co-authored-by: Paul Marbach <paul.marbach@grafana.com> * fix(script): bust cache when new files are created also --------- Co-authored-by: Paul Marbach <paul.marbach@grafana.com>
117 lines
3.7 KiB
JavaScript
117 lines
3.7 KiB
JavaScript
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
const baseConfig = require('./jest.config.js');
|
|
|
|
const CODEOWNERS_MANIFEST_FILENAMES_BY_TEAM_PATH = 'codeowners-manifest/filenames-by-team.json';
|
|
|
|
const teamName = process.env.TEAM_NAME;
|
|
if (!teamName) {
|
|
console.error('ERROR: TEAM_NAME environment variable is required');
|
|
process.exit(1);
|
|
}
|
|
|
|
const outputDir = `./coverage/by-team/${createOwnerDirectory(teamName)}`;
|
|
|
|
const codeownersFilePath = path.join(__dirname, CODEOWNERS_MANIFEST_FILENAMES_BY_TEAM_PATH);
|
|
|
|
if (!fs.existsSync(codeownersFilePath)) {
|
|
console.error(`Codeowners file not found at ${codeownersFilePath} ...`);
|
|
console.error('Please run: yarn codeowners-manifest first to generate the mapping file');
|
|
process.exit(1);
|
|
}
|
|
|
|
const codeownersData = JSON.parse(fs.readFileSync(codeownersFilePath, 'utf8'));
|
|
const teamFiles = codeownersData[teamName] || [];
|
|
|
|
if (teamFiles.length === 0) {
|
|
console.error(`ERROR: No files found for team "${teamName}"`);
|
|
console.error('Available teams:', Object.keys(codeownersData).join(', '));
|
|
process.exit(1);
|
|
}
|
|
|
|
const sourceFiles = teamFiles.filter((file) => {
|
|
const ext = path.extname(file);
|
|
return (
|
|
['.ts', '.tsx', '.js', '.jsx'].includes(ext) &&
|
|
!file.includes('.test.') &&
|
|
!file.includes('.spec.') &&
|
|
!file.includes('.story.') &&
|
|
!file.includes('.gen.ts') &&
|
|
!file.includes('.d.ts') &&
|
|
!file.endsWith('/types.ts')
|
|
);
|
|
});
|
|
|
|
const testFiles = teamFiles.filter((file) => {
|
|
const ext = path.extname(file);
|
|
return ['.ts', '.tsx', '.js', '.jsx'].includes(ext) && (file.includes('.test.') || file.includes('.spec.'));
|
|
});
|
|
|
|
if (testFiles.length === 0) {
|
|
console.log(`No test files found for team ${teamName}`);
|
|
process.exit(0);
|
|
}
|
|
|
|
console.log(
|
|
`🧪 Collecting coverage for ${sourceFiles.length} testable files and running ${testFiles.length} test files of ${teamFiles.length} files owned by ${teamName}.`
|
|
);
|
|
|
|
module.exports = {
|
|
...baseConfig,
|
|
|
|
collectCoverage: true,
|
|
collectCoverageFrom: sourceFiles.map((file) => `<rootDir>/${file}`),
|
|
coverageReporters: ['none'],
|
|
coverageDirectory: '/tmp/jest-coverage-ignore',
|
|
|
|
coverageProvider: 'v8',
|
|
reporters: [
|
|
'default',
|
|
[
|
|
'jest-monocart-coverage',
|
|
{
|
|
name: `Coverage Report - ${teamName} owned files`,
|
|
outputDir: outputDir,
|
|
reports: ['console-summary', 'v8', 'json', 'lcov'],
|
|
sourceFilter: (coveredFile) => sourceFiles.includes(coveredFile),
|
|
all: {
|
|
dir: ['./packages', './public'],
|
|
filter: (filePath) => {
|
|
const relativePath = filePath.replace(process.cwd() + '/', '');
|
|
return sourceFiles.includes(relativePath);
|
|
},
|
|
},
|
|
cleanCache: true,
|
|
onEnd: (coverageResults) => {
|
|
console.log(`📄 Coverage report saved to file://${path.resolve(outputDir)}/index.html`);
|
|
// TODO: Emit coverage metrics https://github.com/grafana/grafana/issues/111208
|
|
},
|
|
},
|
|
],
|
|
],
|
|
|
|
testRegex: undefined,
|
|
|
|
testMatch: testFiles.map((file) => `<rootDir>/${file}`),
|
|
};
|
|
|
|
/**
|
|
* Create a filesystem-safe directory structure for different owner types
|
|
* @param {string} owner - CODEOWNERS owner (username, team, or email)
|
|
* @returns {string} Directory path relative to coverage/by-team/
|
|
*/
|
|
function createOwnerDirectory(owner) {
|
|
if (owner.includes('@') && owner.includes('/')) {
|
|
// Example: @grafana/dataviz-squad
|
|
const [org, team] = owner.substring(1).split('/');
|
|
return `teams/${org}/${team}`;
|
|
} else if (owner.startsWith('@')) {
|
|
// Example: @jesdavpet
|
|
return `users/${owner.substring(1)}`;
|
|
} else {
|
|
// Example: user@domain.tld
|
|
const [user, domain] = owner.split('@');
|
|
return `emails/${user}-at-${domain}`;
|
|
}
|
|
}
|