feat(test): calculate coverage summaries in codeowner jest config
This commit is contained in:
+26
-21
@@ -3,8 +3,10 @@ const open = require('open').default;
|
||||
const path = require('path');
|
||||
|
||||
const baseConfig = require('./jest.config.js');
|
||||
const { createOwnerDirectory, createOwnerFilenameSlug } = require('./scripts/codeowners-manifest/utils.js');
|
||||
|
||||
const CODEOWNERS_MANIFEST_FILENAMES_BY_TEAM_PATH = 'codeowners-manifest/filenames-by-team.json';
|
||||
const COVERAGE_SUMMARY_OUTPUT_PATH = './coverage-summary.json';
|
||||
|
||||
const codeownerName = process.env.CODEOWNER_NAME;
|
||||
if (!codeownerName) {
|
||||
@@ -100,6 +102,8 @@ module.exports = {
|
||||
openCoverageReport(reportURL);
|
||||
}
|
||||
|
||||
writeCoverageSummaryArtifact(coverageResults);
|
||||
|
||||
// TODO: Emit coverage metrics https://github.com/grafana/grafana/issues/111208
|
||||
},
|
||||
},
|
||||
@@ -111,30 +115,31 @@ module.exports = {
|
||||
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}`;
|
||||
function writeCoverageSummaryArtifact(coverageResults) {
|
||||
if (!coverageResults || !coverageResults.summary) {
|
||||
return;
|
||||
}
|
||||
|
||||
const summary = {
|
||||
team: codeownerName,
|
||||
commit: process.env.GITHUB_SHA || 'unknown',
|
||||
timestamp: new Date().toISOString(),
|
||||
summary: {
|
||||
lines: { pct: coverageResults.summary.lines.pct },
|
||||
statements: { pct: coverageResults.summary.statements.pct },
|
||||
functions: { pct: coverageResults.summary.functions.pct },
|
||||
branches: { pct: coverageResults.summary.branches.pct },
|
||||
},
|
||||
};
|
||||
|
||||
try {
|
||||
fs.writeFileSync(COVERAGE_SUMMARY_OUTPUT_PATH, JSON.stringify(summary, null, 2));
|
||||
console.log(`📊 Coverage summary written to ${COVERAGE_SUMMARY_OUTPUT_PATH}`);
|
||||
} catch (err) {
|
||||
console.error(`Failed to write coverage summary: ${err}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the given file URL in the default browser safely, without shell injection risk.
|
||||
* @param {string} reportURL
|
||||
*/
|
||||
async function openCoverageReport(reportURL) {
|
||||
try {
|
||||
await open(reportURL);
|
||||
|
||||
@@ -4,9 +4,29 @@ const { CODEOWNERS_JSON_PATH: CODEOWNERS_MANIFEST_CODEOWNERS_PATH } = require('.
|
||||
|
||||
let _codeownersCache = null;
|
||||
|
||||
/**
|
||||
* Creates a filesystem-safe slug for different CODEOWNERS owner types
|
||||
* @param {string} owner - CODEOWNERS owner (username, team, or email)
|
||||
* @param {string} delimiter - Delimiter to use between parts (default: '/')
|
||||
* @returns {string} Slugified owner string with type prefix to avoid collisions
|
||||
*/
|
||||
function createOwnerSlug(owner, delimiter = '/') {
|
||||
if (owner.includes('@') && owner.includes('/')) {
|
||||
const [org, team] = owner.substring(1).split('/');
|
||||
return ['team', org, team].join(delimiter);
|
||||
} else if (owner.startsWith('@')) {
|
||||
return ['user', owner.substring(1)].join(delimiter);
|
||||
} else {
|
||||
const [user, domain] = owner.split('@');
|
||||
const sanitizedUser = user.replace(/[+.]/g, delimiter);
|
||||
const sanitizedDomain = domain.replace(/\./g, delimiter);
|
||||
return ['email', `${sanitizedUser}-at-${sanitizedDomain}`].join(delimiter);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
/**
|
||||
* import the contents of the codeowners manifest JSON file, with caching
|
||||
* Imports the contents of the codeowners manifest JSON file, with caching
|
||||
* @param {boolean} clearCache - if true, clear the cached data and reload the codeowners manifest
|
||||
* @returns {Promise<Array<string>>} - list of codeowners which own at least one file in the project
|
||||
*/
|
||||
@@ -31,4 +51,28 @@ module.exports = {
|
||||
|
||||
return _codeownersCache;
|
||||
},
|
||||
|
||||
/**
|
||||
* 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/
|
||||
*
|
||||
* @example
|
||||
* createOwnerDirectory('@grafana/dataviz-squad') => 'teams/grafana/dataviz-squad'
|
||||
*/
|
||||
createOwnerDirectory(owner) {
|
||||
return createOwnerSlug(owner, '/');
|
||||
},
|
||||
|
||||
/**
|
||||
* Create a filename-safe slug for artifacts and filenames
|
||||
* @param {string} owner - CODEOWNERS owner (username, team, or email)
|
||||
* @returns {string} Filename-safe slug
|
||||
*
|
||||
* @example
|
||||
* createOwnerFilenameSlug('@grafana/dataviz-squad') => 'teams-grafana-dataviz-squad'
|
||||
*/
|
||||
createOwnerFilenameSlug(owner) {
|
||||
return createOwnerSlug(owner, '-');
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user