UNPKG

renovate

Version:

Automated dependency updates. Flexible so you don't need to be.

123 lines 5.62 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.codeOwnersForPr = codeOwnersForPr; const tslib_1 = require("tslib"); const is_1 = tslib_1.__importDefault(require("@sindresorhus/is")); const ignore_1 = tslib_1.__importDefault(require("ignore")); const logger_1 = require("../../../../logger"); const platform_1 = require("../../../../modules/platform"); const fs_1 = require("../../../../util/fs"); const git_1 = require("../../../../util/git"); const regex_1 = require("../../../../util/regex"); function extractOwnersFromLine(line) { const [pattern, ...usernames] = line.split((0, regex_1.regEx)(/\s+/)); const matchPattern = (0, ignore_1.default)().add(pattern); return { usernames, pattern, score: pattern.length, match: (path) => matchPattern.ignores(path), }; } function matchFileToOwners(file, rules) { const usernames = new Map(); for (const rule of rules) { if (!rule.match(file)) { continue; } for (const user of rule.usernames) { usernames.set(user, rule.score); } } return { file, userScoreMap: usernames }; } function getOwnerList(filesWithOwners) { const userFileMap = new Map(); for (const fileName of filesWithOwners) { for (const [username, score] of fileName.userScoreMap.entries()) { // Get / create user file score const fileMap = userFileMap.get(username) ?? new Map(); if (!userFileMap.has(username)) { userFileMap.set(username, fileMap); } // Add file to user fileMap.set(fileName.file, (fileMap.get(fileName.file) ?? 0) + score); } } return Array.from(userFileMap.entries()).map(([key, value]) => ({ username: key, fileScoreMap: value, })); } function parseCodeOwnersContent(codeOwnersFile) { return (codeOwnersFile .split(regex_1.newlineRegex) // Remove comments .map((line) => line.split('#')[0]) // Remove empty lines .map((line) => line.trim()) .filter(is_1.default.nonEmptyString)); } async function codeOwnersForPr(pr) { logger_1.logger.debug('Searching for CODEOWNERS file'); try { // Find CODEOWNERS file const codeOwnersFile = (await (0, fs_1.readLocalFile)('CODEOWNERS', 'utf8')) ?? (await (0, fs_1.readLocalFile)('.bitbucket/CODEOWNERS', 'utf8')) ?? (await (0, fs_1.readLocalFile)('.github/CODEOWNERS', 'utf8')) ?? (await (0, fs_1.readLocalFile)('.gitlab/CODEOWNERS', 'utf8')) ?? (await (0, fs_1.readLocalFile)('docs/CODEOWNERS', 'utf8')); if (!codeOwnersFile) { logger_1.logger.debug('No CODEOWNERS file found'); return []; } logger_1.logger.debug(`Found CODEOWNERS file: ${codeOwnersFile}`); // Get list of modified files in PR // if the commit sha is known, we can directly compare against the parent, otherwise use the branch const prFiles = pr.sha ? await (0, git_1.getBranchFilesFromCommit)(pr.sha) : await (0, git_1.getBranchFiles)(pr.sourceBranch); if (!prFiles?.length) { logger_1.logger.debug('PR includes no files'); return []; } // Convert CODEOWNERS file into list of matching rules const cleanedLines = parseCodeOwnersContent(codeOwnersFile); const fileOwnerRules = platform_1.platform.extractRulesFromCodeOwnersLines?.(cleanedLines) ?? cleanedLines.map(extractOwnersFromLine); logger_1.logger.debug({ prFiles, fileOwnerRules }, 'PR files and rules to match for CODEOWNERS'); // Apply rules & get list of owners for each prFile const emptyRules = fileOwnerRules.filter((rule) => rule.usernames.length === 0); const fileOwners = // Map through all prFiles and match said file(s) with all the rules prFiles .map((file) => matchFileToOwners(file, fileOwnerRules)) // Match file again but this time only with emptyRules, to ensure that files which have no owner set remain owner-less .map((matchedFile) => { const matchEmpty = emptyRules.find((rule) => rule.match(matchedFile.file)); if (matchEmpty) { return { ...matchedFile, userScoreMap: new Map() }; } return matchedFile; }); logger_1.logger.debug(`CODEOWNERS matched the following files: ${fileOwners .map((f) => f.file) .join(', ')}`); // Get list of all matched users and the files they own (reverse keys of fileOwners) const usersWithOwnedFiles = getOwnerList(fileOwners); // Calculate a match score for each user. This allows sorting of the final user array in a way that guarantees that users matched with more precise patterns are first and users matched with less precise patterns are last (wildcards) const userScore = usersWithOwnedFiles .map((userMatch) => ({ user: userMatch.username, score: Array.from(userMatch.fileScoreMap.values()).reduce((acc, score) => acc + score, 0), })) .sort((a, b) => b.score - a.score); logger_1.logger.debug(`CODEOWNERS matched the following users: ${JSON.stringify(userScore)}`); return userScore.map((u) => u.user); } catch (err) { logger_1.logger.warn({ err, pr }, 'Failed to determine CODEOWNERS for PR.'); return []; } } //# sourceMappingURL=code-owners.js.map