Files
labeler/src/changedFiles.ts

361 lines
8.7 KiB
TypeScript

import * as core from '@actions/core';
import * as github from '@actions/github';
import {Minimatch} from 'minimatch';
import {printPattern, isObject, kebabToCamel} from './utils';
export interface ChangedFilesMatchConfig {
changedFiles?: ChangedFilesGlobPatternsConfig[];
}
interface ChangedFilesGlobPatternsConfig {
anyGlobToAnyFile?: string[];
anyGlobToAllFiles?: string[];
allGlobsToAnyFile?: string[];
allGlobsToAllFiles?: string[];
}
type ClientType = ReturnType<typeof github.getOctokit>;
const ALLOWED_FILES_CONFIG_KEYS = [
'any-glob-to-any-file',
'any-glob-to-all-files',
'all-globs-to-any-file',
'all-globs-to-all-files'
];
export async function getChangedFiles(
client: ClientType,
prNumber: number
): Promise<string[]> {
const listFilesOptions = client.rest.pulls.listFiles.endpoint.merge({
owner: github.context.repo.owner,
repo: github.context.repo.repo,
pull_number: prNumber
});
const listFilesResponse = await client.paginate(listFilesOptions);
const changedFiles = listFilesResponse.map((f: any) => f.filename);
core.debug('found changed files:');
for (const file of changedFiles) {
core.debug(' ' + file);
}
return changedFiles;
}
export function toChangedFilesMatchConfig(
config: any
): ChangedFilesMatchConfig {
if (!config['changed-files'] || !config['changed-files'].length) {
return {};
}
const changedFilesConfigs = Array.isArray(config['changed-files'])
? config['changed-files']
: [config['changed-files']];
const validChangedFilesConfigs: ChangedFilesGlobPatternsConfig[] = [];
changedFilesConfigs.forEach(changedFilesConfig => {
if (!isObject(changedFilesConfig)) {
throw new Error(
`The "changed-files" section must have a valid config structure. Please read the action documentation for more information`
);
}
const changedFilesConfigKeys = Object.keys(changedFilesConfig);
const invalidKeys = changedFilesConfigKeys.filter(
key => !ALLOWED_FILES_CONFIG_KEYS.includes(key)
);
if (invalidKeys.length) {
throw new Error(
`Unknown config options were under "changed-files": ${invalidKeys.join(
', '
)}`
);
}
changedFilesConfigKeys.forEach(key => {
validChangedFilesConfigs.push({
[kebabToCamel(key)]: Array.isArray(changedFilesConfig[key])
? changedFilesConfig[key]
: [changedFilesConfig[key]]
});
});
});
return {
changedFiles: validChangedFilesConfigs
};
}
export function checkAnyChangedFiles(
changedFiles: string[],
globPatternsConfigs: ChangedFilesGlobPatternsConfig[],
dot: boolean
): boolean {
core.debug(` checking "changed-files" patterns`);
for (const globPatternsConfig of globPatternsConfigs) {
if (globPatternsConfig.anyGlobToAnyFile) {
if (
checkIfAnyGlobMatchesAnyFile(
changedFiles,
globPatternsConfig.anyGlobToAnyFile,
dot
)
) {
core.debug(` "changed-files" matched`);
return true;
}
}
if (globPatternsConfig.anyGlobToAllFiles) {
if (
checkIfAnyGlobMatchesAllFiles(
changedFiles,
globPatternsConfig.anyGlobToAllFiles,
dot
)
) {
core.debug(` "changed-files" matched`);
return true;
}
}
if (globPatternsConfig.allGlobsToAnyFile) {
if (
checkIfAllGlobsMatchAnyFile(
changedFiles,
globPatternsConfig.allGlobsToAnyFile,
dot
)
) {
core.debug(` "changed-files" matched`);
return true;
}
}
if (globPatternsConfig.allGlobsToAllFiles) {
if (
checkIfAllGlobsMatchAllFiles(
changedFiles,
globPatternsConfig.allGlobsToAllFiles,
dot
)
) {
core.debug(` "changed-files" matched`);
return true;
}
}
}
core.debug(` "changed-files" did not match`);
return false;
}
export function checkAllChangedFiles(
changedFiles: string[],
globPatternsConfigs: ChangedFilesGlobPatternsConfig[],
dot: boolean
): boolean {
core.debug(` checking "changed-files" patterns`);
for (const globPatternsConfig of globPatternsConfigs) {
if (globPatternsConfig.anyGlobToAnyFile) {
if (
!checkIfAnyGlobMatchesAnyFile(
changedFiles,
globPatternsConfig.anyGlobToAnyFile,
dot
)
) {
core.debug(` "changed-files" did not match`);
return false;
}
}
if (globPatternsConfig.anyGlobToAllFiles) {
if (
!checkIfAnyGlobMatchesAllFiles(
changedFiles,
globPatternsConfig.anyGlobToAllFiles,
dot
)
) {
core.debug(` "changed-files" did not match`);
return false;
}
}
if (globPatternsConfig.allGlobsToAnyFile) {
if (
!checkIfAllGlobsMatchAnyFile(
changedFiles,
globPatternsConfig.allGlobsToAnyFile,
dot
)
) {
core.debug(` "changed-files" did not match`);
return false;
}
}
if (globPatternsConfig.allGlobsToAllFiles) {
if (
!checkIfAllGlobsMatchAllFiles(
changedFiles,
globPatternsConfig.allGlobsToAllFiles,
dot
)
) {
core.debug(` "changed-files" did not match`);
return false;
}
}
}
core.debug(` "changed-files" patterns matched`);
return true;
}
export function checkIfAnyGlobMatchesAnyFile(
changedFiles: string[],
globs: string[],
dot: boolean
): boolean {
core.debug(` checking "any-glob-to-any-file" config patterns`);
const matchers = globs.map(g => new Minimatch(g, {dot}));
for (const matcher of matchers) {
const matchedFile = changedFiles.find(changedFile => {
core.debug(
` checking "${printPattern(
matcher
)}" pattern against ${changedFile}`
);
return matcher.match(changedFile);
});
if (matchedFile) {
core.debug(
` "${printPattern(matcher)}" pattern matched ${matchedFile}`
);
return true;
}
}
core.debug(` none of the patterns matched any of the files`);
return false;
}
export function checkIfAllGlobsMatchAnyFile(
changedFiles: string[],
globs: string[],
dot: boolean
): boolean {
core.debug(` checking "all-globs-to-any-file" config patterns`);
const matchers = globs.map(g => new Minimatch(g, {dot}));
for (const changedFile of changedFiles) {
const mismatchedGlob = matchers.find(matcher => {
core.debug(
` checking "${printPattern(
matcher
)}" pattern against ${changedFile}`
);
return !matcher.match(changedFile);
});
if (mismatchedGlob) {
core.debug(
` "${printPattern(
mismatchedGlob
)}" pattern did not match ${changedFile}`
);
continue;
}
core.debug(` all patterns matched ${changedFile}`);
return true;
}
core.debug(` none of the files matched all patterns`);
return false;
}
export function checkIfAnyGlobMatchesAllFiles(
changedFiles: string[],
globs: string[],
dot: boolean
): boolean {
core.debug(` checking "any-glob-to-all-files" config patterns`);
const matchers = globs.map(g => new Minimatch(g, {dot}));
for (const matcher of matchers) {
const mismatchedFile = changedFiles.find(changedFile => {
core.debug(
` checking "${printPattern(
matcher
)}" pattern against ${changedFile}`
);
return !matcher.match(changedFile);
});
if (mismatchedFile) {
core.debug(
` "${printPattern(
matcher
)}" pattern did not match ${mismatchedFile}`
);
continue;
}
core.debug(` "${printPattern(matcher)}" pattern matched all files`);
return true;
}
core.debug(` none of the patterns matched all files`);
return false;
}
export function checkIfAllGlobsMatchAllFiles(
changedFiles: string[],
globs: string[],
dot: boolean
): boolean {
core.debug(` checking "all-globs-to-all-files" config patterns`);
const matchers = globs.map(g => new Minimatch(g, {dot}));
for (const changedFile of changedFiles) {
const mismatchedGlob = matchers.find(matcher => {
core.debug(
` checking "${printPattern(
matcher
)}" pattern against ${changedFile}`
);
return !matcher.match(changedFile);
});
if (mismatchedGlob) {
core.debug(
` "${printPattern(
mismatchedGlob
)}" pattern did not match ${changedFile}`
);
return false;
}
}
core.debug(` all patterns matched all files`);
return true;
}