Skip to content

Commit 01127bf

Browse files
authored
Add pr-filter (#21)
1 parent bf51981 commit 01127bf

16 files changed

Lines changed: 40038 additions & 71 deletions

File tree

common/package.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"name": "common",
3+
"version": "0.1.0",
4+
"author": "Developer Express Inc.",
5+
"license": "MIT",
6+
"engines": {
7+
"node": ">=16.0.0"
8+
},
9+
"main": "lib/index.js",
10+
"scripts": {
11+
"clean": "rimraf lib",
12+
"build": "tsc",
13+
"test": "jest --config ../jest.config.js"
14+
},
15+
"devDependencies": {
16+
"minimatch": "^10.0.1"
17+
}
18+
}

common/src/common-utils.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { getExecOutput } from '@actions/exec'
2+
3+
export async function execCommand(command: string): Promise<string> {
4+
const { stdout, stderr, exitCode } = await getExecOutput(command)
5+
6+
if (exitCode !== 0) {
7+
throw new Error(`Command "${command}" has been failed with error: ${stderr}`)
8+
}
9+
10+
return stdout.trim()
11+
}

common/src/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export * from './common-utils';
2+
export * from './path-utils';
3+
export * from './pr-utils';

common/src/path-utils.test.ts

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import { filterPaths } from "./path-utils";
2+
3+
describe('path utils', () => {
4+
5+
describe('filterPaths', () => {
6+
test.each([
7+
{
8+
paths: [],
9+
patterns: [],
10+
expected: []
11+
},
12+
{
13+
paths: ['a.ts', 'a.js', 'b.md'],
14+
patterns: ['b*.*'],
15+
expected: ['b.md']
16+
},
17+
{
18+
paths: ['a.ts', 'b.md'],
19+
patterns: ['**'],
20+
expected: ['a.ts', 'b.md']
21+
},
22+
{
23+
paths: ['a.ts', 'a.js', 'x/a.ts', 'x/a.js', 'x/y/a.ts', 'x/z/a.js'],
24+
patterns: ['*.ts'],
25+
expected: ['a.ts']
26+
},
27+
{
28+
paths: ['a.ts', 'a.js', 'x/a.ts', 'x/a.js', 'x/y/a.ts', 'x/z/a.js'],
29+
patterns: ['**/*.ts'],
30+
expected: ['a.ts', 'x/a.ts', 'x/y/a.ts']
31+
},
32+
{
33+
paths: ['a.ts', 'a.js', 'x/a.ts', 'x/a.js', 'x/y/a.ts', 'x/z/a.js'],
34+
patterns: ['**/y/*.*'],
35+
expected: ['x/y/a.ts']
36+
},
37+
])('basic cases [%#]', ({ paths, patterns, expected }) => {
38+
expect(filterPaths(paths, patterns)).toEqual(expected);
39+
});
40+
});
41+
42+
describe('filterPaths', () => {
43+
test.each([
44+
// #region cases with negation
45+
{
46+
paths: ['a.ts', 'a.js', 'b.md'],
47+
patterns: ['**', '!*.md'],
48+
expected: ['a.ts', 'a.js']
49+
},
50+
{
51+
paths: ['a.ts', 'a.js', 'b.md'],
52+
patterns: ['**', '!b*'],
53+
expected: ['a.ts', 'a.js']
54+
},
55+
{
56+
paths: ['a.ts', 'a.js', 'x/a.ts', 'x/a.js', 'x/y/a.ts', 'x/z/a.js'],
57+
patterns: ['**', '!*.js'],
58+
expected: ['a.ts', 'x/a.ts', 'x/a.js', 'x/y/a.ts', 'x/z/a.js']
59+
},
60+
{
61+
paths: ['a.ts', 'a.js', 'x/a.ts', 'x/a.js', 'x/y/a.ts', 'x/z/a.js'],
62+
patterns: ['**', '!**/*.js'],
63+
expected: ['a.ts', 'x/a.ts', 'x/y/a.ts']
64+
},
65+
{
66+
paths: ['a.ts', 'a.js', 'x/a.ts', 'x/a.js', 'x/y/a.ts', 'x/z/a.js'],
67+
patterns: ['**', '!**/{y,z}/*.*'],
68+
expected: ['a.ts', 'a.js', 'x/a.ts', 'x/a.js',]
69+
},
70+
// #endregion
71+
])('negation cases [%#]', ({ paths, patterns, expected }) => {
72+
expect(filterPaths(paths, patterns)).toEqual(expected);
73+
});
74+
});
75+
76+
describe('filterPaths', () => {
77+
test.each([
78+
{ // starting with dot
79+
paths: ['.a/b.ts'],
80+
patterns: ['**'],
81+
expected: ['.a/b.ts']
82+
},
83+
{ // backslash
84+
paths: ['a\\b\\c.ts'],
85+
patterns: ['**/b/*.*'],
86+
expected: ['a\\b\\c.ts']
87+
},
88+
])('specific cases [%#]', ({ paths, patterns, expected }) => {
89+
expect(filterPaths(paths, patterns)).toEqual(expected);
90+
});
91+
});
92+
});

common/src/path-utils.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { MinimatchOptions, minimatch } from "minimatch";
2+
3+
const NEGATION = '!';
4+
const matchOptions: MinimatchOptions = {
5+
dot: true,
6+
}
7+
8+
function match(path: string, pattern: string): boolean {
9+
return minimatch(path.replace(/\\/g, '/'), pattern, matchOptions);
10+
}
11+
12+
export function filterPaths(
13+
paths: string[],
14+
patterns: string[],
15+
): string[] {
16+
return paths.filter(path => {
17+
return patterns.reduce((prevResult, pattern) => {
18+
return pattern.startsWith(NEGATION)
19+
? prevResult && !match(path, pattern.substring(1))
20+
: prevResult || match(path, pattern);
21+
}, false);
22+
});
23+
}

common/src/pr-utils.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
2+
import * as core from '@actions/core'
3+
import { context } from '@actions/github'
4+
import { execCommand } from './common-utils';
5+
6+
export async function getPrRevisionRange(): Promise<{
7+
head: string;
8+
base: string;
9+
}> {
10+
return getRange().then((r) => {
11+
core.info(`Base commit: ${r.base}`);
12+
core.info(`Head commit: ${r.head}`);
13+
return r;
14+
});
15+
}
16+
17+
function normalizeCommit(commit: string) {
18+
return commit === '0000000000000000000000000000000000000000' ? 'HEAD^' : commit;
19+
}
20+
21+
export async function getRange(): Promise<{
22+
head: string;
23+
base: string;
24+
}> {
25+
switch (context.eventName) {
26+
case 'pull_request':
27+
const baseBranch = context.payload.pull_request?.base?.ref;
28+
await execCommand(`git fetch origin`);
29+
30+
return {
31+
base: await execCommand(`git rev-parse origin/${baseBranch}`),
32+
head: context.payload.pull_request?.head?.sha,
33+
};
34+
35+
case 'push':
36+
37+
return {
38+
base: normalizeCommit(context.payload.before),
39+
head: context.payload.after,
40+
};
41+
default:
42+
throw new Error(`This action only supports pull requests and pushes, ${context.eventName} events are not supported.`);
43+
}
44+
}

common/tsconfig.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"extends": "../tsconfig.base.json",
3+
"compilerOptions": {
4+
"rootDir": "./src",
5+
"outDir": "./lib",
6+
"declaration": true
7+
}
8+
}

package-lock.json

Lines changed: 51 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@
3838
"typescript": "^4.9.5"
3939
},
4040
"workspaces": [
41+
"common",
42+
"pr-filter",
4143
"send-teams-notification",
4244
"verify-version-change"
4345
]

pr-filter/action.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
name: pr-filter
2+
description: Checks if PR meets certian criteria
3+
4+
inputs:
5+
paths:
6+
description: Semicolon separated globs
7+
required: false
8+
9+
runs:
10+
using: node16
11+
main: dist/index.js
12+
13+
outputs:
14+
result:
15+
description: true if all criteria met, otherwise false

0 commit comments

Comments
 (0)