2020-03-20 09:59:35 -04:00
|
|
|
/**
|
|
|
|
* @license
|
2020-05-19 15:08:49 -04:00
|
|
|
* Copyright Google LLC All Rights Reserved.
|
2020-03-20 09:59:35 -04:00
|
|
|
*
|
|
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
|
|
* found in the LICENSE file at https://angular.io/license
|
|
|
|
*/
|
2020-05-20 17:41:44 -04:00
|
|
|
|
|
|
|
import {error} from '../utils/console';
|
2020-04-16 14:12:25 -04:00
|
|
|
import {convertConditionToFunction} from './condition_evaluator';
|
2020-03-20 09:59:35 -04:00
|
|
|
import {PullApproveGroupConfig} from './parse-yaml';
|
|
|
|
|
|
|
|
/** A condition for a group. */
|
|
|
|
interface GroupCondition {
|
2020-04-16 14:12:25 -04:00
|
|
|
expression: string;
|
|
|
|
checkFn: (files: string[]) => boolean;
|
2020-03-20 09:59:35 -04:00
|
|
|
matchedFiles: Set<string>;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Result of testing files against the group. */
|
|
|
|
export interface PullApproveGroupResult {
|
|
|
|
groupName: string;
|
2020-04-16 14:12:25 -04:00
|
|
|
matchedConditions: GroupCondition[];
|
2020-03-20 09:59:35 -04:00
|
|
|
matchedCount: number;
|
2020-04-16 14:12:25 -04:00
|
|
|
unmatchedConditions: GroupCondition[];
|
2020-03-20 09:59:35 -04:00
|
|
|
unmatchedCount: number;
|
|
|
|
}
|
|
|
|
|
2020-04-16 14:12:25 -04:00
|
|
|
// Regular expression that matches conditions for the global approval.
|
|
|
|
const GLOBAL_APPROVAL_CONDITION_REGEX = /^"global-(docs-)?approvers" not in groups.approved$/;
|
2020-03-20 09:59:35 -04:00
|
|
|
|
2020-04-16 14:12:25 -04:00
|
|
|
// Name of the PullApprove group that serves as fallback. This group should never capture
|
|
|
|
// any conditions as it would always match specified files. This is not desired as we want
|
|
|
|
// to figure out as part of this tool, whether there actually are unmatched files.
|
|
|
|
const FALLBACK_GROUP_NAME = 'fallback';
|
2020-03-20 09:59:35 -04:00
|
|
|
|
|
|
|
/** A PullApprove group to be able to test files against. */
|
|
|
|
export class PullApproveGroup {
|
2020-04-16 14:12:25 -04:00
|
|
|
/** List of conditions for the group. */
|
|
|
|
conditions: GroupCondition[] = [];
|
|
|
|
|
|
|
|
constructor(public groupName: string, config: PullApproveGroupConfig) {
|
|
|
|
this._captureConditions(config);
|
|
|
|
}
|
2020-03-20 09:59:35 -04:00
|
|
|
|
2020-04-16 14:12:25 -04:00
|
|
|
private _captureConditions(config: PullApproveGroupConfig) {
|
|
|
|
if (config.conditions && this.groupName !== FALLBACK_GROUP_NAME) {
|
|
|
|
return config.conditions.forEach(condition => {
|
|
|
|
const expression = condition.trim();
|
2020-03-20 09:59:35 -04:00
|
|
|
|
2020-04-16 14:12:25 -04:00
|
|
|
if (expression.match(GLOBAL_APPROVAL_CONDITION_REGEX)) {
|
2020-03-30 11:44:30 -04:00
|
|
|
// Currently a noop as we don't take any action for global approval conditions.
|
2020-04-16 14:12:25 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
this.conditions.push({
|
|
|
|
expression,
|
|
|
|
checkFn: convertConditionToFunction(expression),
|
|
|
|
matchedFiles: new Set(),
|
|
|
|
});
|
|
|
|
} catch (e) {
|
2020-05-20 17:41:44 -04:00
|
|
|
error(`Could not parse condition in group: ${this.groupName}`);
|
|
|
|
error(` - ${expression}`);
|
|
|
|
error(`Error:`);
|
|
|
|
error(e.message);
|
|
|
|
error(e.stack);
|
2020-03-30 11:44:30 -04:00
|
|
|
process.exit(1);
|
2020-03-27 21:14:52 -04:00
|
|
|
}
|
2020-04-16 14:12:25 -04:00
|
|
|
});
|
2020-03-20 09:59:35 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Tests a provided file path to determine if it would be considered matched by
|
|
|
|
* the pull approve group's conditions.
|
|
|
|
*/
|
2020-04-16 14:12:25 -04:00
|
|
|
testFile(filePath: string): boolean {
|
|
|
|
return this.conditions.every(({matchedFiles, checkFn, expression}) => {
|
|
|
|
try {
|
|
|
|
const matchesFile = checkFn([filePath]);
|
|
|
|
if (matchesFile) {
|
|
|
|
matchedFiles.add(filePath);
|
2020-03-20 09:59:35 -04:00
|
|
|
}
|
2020-04-16 14:12:25 -04:00
|
|
|
return matchesFile;
|
|
|
|
} catch (e) {
|
|
|
|
const errMessage = `Condition could not be evaluated: \n\n` +
|
|
|
|
`From the [${this.groupName}] group:\n` +
|
|
|
|
` - ${expression}` +
|
|
|
|
`\n\n${e.message} ${e.stack}\n\n`;
|
2020-05-20 17:41:44 -04:00
|
|
|
error(errMessage);
|
2020-04-16 14:12:25 -04:00
|
|
|
process.exit(1);
|
2020-03-20 09:59:35 -04:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-04-16 14:12:25 -04:00
|
|
|
/** Retrieve the results for the Group, all matched and unmatched conditions. */
|
|
|
|
getResults(): PullApproveGroupResult {
|
|
|
|
const matchedConditions = this.conditions.filter(c => !!c.matchedFiles.size);
|
|
|
|
const unmatchedConditions = this.conditions.filter(c => !c.matchedFiles.size);
|
|
|
|
return {
|
|
|
|
matchedConditions,
|
|
|
|
matchedCount: matchedConditions.length,
|
|
|
|
unmatchedConditions,
|
|
|
|
unmatchedCount: unmatchedConditions.length,
|
|
|
|
groupName: this.groupName,
|
|
|
|
};
|
|
|
|
}
|
2020-03-20 09:59:35 -04:00
|
|
|
}
|