Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update get-changed-files action #26

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions common/src/common-utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { setOutput } from '@actions/core';
import { getExecOutput } from '@actions/exec'
import { stringifyForShell } from './serialization-utils';

export async function execCommand(command: string): Promise<string> {
const { stdout, stderr, exitCode } = await getExecOutput(command)
Expand All @@ -9,3 +11,9 @@ export async function execCommand(command: string): Promise<string> {

return stdout.trim()
}

export function setOutputs(values: Record<string, unknown>): void {
for (const [key, value] of Object.entries(values)) {
setOutput(key, stringifyForShell(value));
}
}
1 change: 0 additions & 1 deletion common/src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@

export const inputs = {
GH_TOKEN: 'gh-token',
OUTPUT: 'output',
PATHS: 'paths',
} as const;

Expand Down
21 changes: 20 additions & 1 deletion common/src/path-utils.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { filterPaths } from "./path-utils";
import { filterPaths, normalizePatterns } from "./path-utils";

describe('path utils', () => {

Expand All @@ -9,6 +9,11 @@ describe('path utils', () => {
patterns: [],
expected: []
},
{
paths: ['a.ts', 'a.js', 'b.md'],
patterns: [],
expected: ['a.ts', 'a.js', 'b.md']
},
{
paths: ['a.ts', 'a.js', 'b.md'],
patterns: ['b*.*'],
Expand Down Expand Up @@ -89,4 +94,18 @@ describe('path utils', () => {
expect(filterPaths(paths, patterns)).toEqual(expected);
});
});

describe('normalizePatterns', () => {
test.each([
{ patterns: undefined, expected: undefined },
{ patterns: [ ], expected: undefined },
{ patterns: [ undefined ], expected: undefined },
{ patterns: [ '' ], expected: undefined },
{ patterns: [ '', undefined ], expected: undefined },
{ patterns: ['a'], expected: ['a'] },
{ patterns: ['', 'a', undefined ], expected: ['a'] },
])('basic cases [%#]', ({ patterns, expected }) => {
expect(normalizePatterns(patterns)).toEqual(expected);
});
});
});
26 changes: 19 additions & 7 deletions common/src/path-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,23 @@ function filterPathsImpl(
paths: string[],
patterns: string[],
): string[] {
return paths.filter(path => {
return patterns.reduce((prevResult, pattern) => {
return pattern.startsWith(NEGATION)
? prevResult && !match(path, pattern.substring(1))
: prevResult || match(path, pattern);
}, false);
});
const normalizedPatterns = normalizePatterns(patterns);
return normalizedPatterns === undefined
? paths
: paths.filter(path => testPath(path, normalizedPatterns));
}

export function normalizePatterns(patterns?: (string | undefined)[]): string[] | undefined {
if(!patterns) return undefined;

const notEmptyPatterns = patterns.filter(p => p != undefined && p.length > 0);
return (notEmptyPatterns.length > 0 ? notEmptyPatterns : undefined) as ReturnType<typeof normalizePatterns>;
}

export function testPath(path: string, patterns: string[]): boolean {
return patterns.reduce((prevResult, pattern) => {
return pattern.startsWith(NEGATION)
? prevResult && !match(path, pattern.substring(1))
: prevResult || match(path, pattern);
}, false);
}
18 changes: 12 additions & 6 deletions common/src/pr-utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@

import * as core from '@actions/core'
import { context, getOctokit } from '@actions/github'
import * as core from '@actions/core';
import { context, getOctokit } from '@actions/github';
import { components } from '@octokit/openapi-types';
import { execCommand } from './common-utils';

export async function getPrRevisionRange(): Promise<{
Expand Down Expand Up @@ -42,14 +43,19 @@ function normalizeCommit(commit: string) {
return commit === '0000000000000000000000000000000000000000' ? 'HEAD^' : commit;
}

export async function getChangedFiles(token: string): Promise<string[]> {
interface ChangedFile {
path: string;
status: components['schemas']['diff-entry']['status'];
}

export async function getChangedFiles(token: string): Promise<ChangedFile[]> {
return getChangedFilesImpl(token).then((files) => {
core.info(`${files.length} changed files: ${JSON.stringify(files, undefined, 2)}`)
return files;
});
}

async function getChangedFilesImpl(token: string): Promise<string[]> {
async function getChangedFilesImpl(token: string): Promise<ChangedFile[]> {
try {
const octokit = getOctokit(token);

Expand All @@ -58,13 +64,13 @@ async function getChangedFilesImpl(token: string): Promise<string[]> {
return [];
}

const files = await octokit.paginate(octokit.rest.pulls.listFiles, {
const entries = await octokit.paginate(octokit.rest.pulls.listFiles, {
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.payload.pull_request.number,
});

return files.map(file => file.filename);
return entries.map(({ filename, status }) => ({ path: filename, status }));
} catch (error) {
core.setFailed(`Getting changed files failed with error: ${error}`);
return [];
Expand Down
23 changes: 23 additions & 0 deletions common/src/serialization-utils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { stringifyForShell } from "./serialization-utils";

describe('action utils', () => {

describe(stringifyForShell.name, () => {
test.each([
{ value: 123, expected: '123' },
{ value: 'abc', expected: 'abc' },
])('scalar cases [%#]', ({ value, expected }) => {
expect(stringifyForShell(value)).toEqual(expected);
});
});

describe(stringifyForShell.name, () => {
test.each([
{ value: [123, 456], expected: '123 456' },
{ value: ['abc', 'def'], expected: '\'abc\' \'def\'' },
])('array values [%#]', ({ value, expected }) => {
expect(stringifyForShell(value)).toEqual(expected);
});
});

});
35 changes: 35 additions & 0 deletions common/src/serialization-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
function stringifyArrayItem(item: unknown): string {
switch (typeof item) {
case 'number':
return item.toString();

case 'string':
return `'${item}'`;

default:
return JSON.stringify(item);
}
}

export function stringifyForShell(value: unknown): string {

switch (typeof value) {

case 'string':
return value;

case 'object':
if (Array.isArray(value)) {
return value.map(stringifyArrayItem).join(' ');
}

if (value === null) {
return '';
}

return value.toString();

default:
return JSON.stringify(value);
}
}
15 changes: 8 additions & 7 deletions get-changed-files/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,16 @@ inputs:
description: Semicolon separated globs
required: false

output:
description: Output file path
required: true


runs:
using: node16
main: dist/index.js

outputs:
result:
description: A file with the list of files in JSON format
count:
description: The count of all files changed in the PR
files:
description: Space-separated list of all files changed in the PR
json:
description: |
Full output in JSON format.
'Schema: { files: { path: string; status: string; } []; count: number; }'
101 changes: 82 additions & 19 deletions get-changed-files/dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
});
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.execCommand = void 0;
exports.setOutputs = exports.execCommand = void 0;
const core_1 = __nccwpck_require__(5316);
const exec_1 = __nccwpck_require__(110);
const serialization_utils_1 = __nccwpck_require__(9091);
function execCommand(command) {
return __awaiter(this, void 0, void 0, function* () {
const { stdout, stderr, exitCode } = yield (0, exec_1.getExecOutput)(command);
Expand All @@ -28,6 +30,12 @@ function execCommand(command) {
});
}
exports.execCommand = execCommand;
function setOutputs(values) {
for (const [key, value] of Object.entries(values)) {
(0, core_1.setOutput)(key, (0, serialization_utils_1.stringifyForShell)(value));
}
}
exports.setOutputs = setOutputs;


/***/ }),
Expand All @@ -41,7 +49,6 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.outputs = exports.inputs = void 0;
exports.inputs = {
GH_TOKEN: 'gh-token',
OUTPUT: 'output',
PATHS: 'paths',
};
exports.outputs = {
Expand Down Expand Up @@ -126,7 +133,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
return result;
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.filterPaths = void 0;
exports.testPath = exports.normalizePatterns = exports.filterPaths = void 0;
const core = __importStar(__nccwpck_require__(5316));
const minimatch_1 = __nccwpck_require__(148);
const NEGATION = '!';
Expand All @@ -144,14 +151,26 @@ function filterPaths(paths, patterns) {
}
exports.filterPaths = filterPaths;
function filterPathsImpl(paths, patterns) {
return paths.filter(path => {
return patterns.reduce((prevResult, pattern) => {
return pattern.startsWith(NEGATION)
? prevResult && !match(path, pattern.substring(1))
: prevResult || match(path, pattern);
}, false);
});
const normalizedPatterns = normalizePatterns(patterns);
return normalizedPatterns === undefined
? paths
: paths.filter(path => testPath(path, normalizedPatterns));
}
function normalizePatterns(patterns) {
if (!patterns)
return undefined;
const notEmptyPatterns = patterns.filter(p => p != undefined && p.length > 0);
return (notEmptyPatterns.length > 0 ? notEmptyPatterns : undefined);
}
exports.normalizePatterns = normalizePatterns;
function testPath(path, patterns) {
return patterns.reduce((prevResult, pattern) => {
return pattern.startsWith(NEGATION)
? prevResult && !match(path, pattern.substring(1))
: prevResult || match(path, pattern);
}, false);
}
exports.testPath = testPath;


/***/ }),
Expand Down Expand Up @@ -249,12 +268,12 @@ function getChangedFilesImpl(token) {
core.setFailed('Getting changed files only works on pull request events.');
return [];
}
const files = yield octokit.paginate(octokit.rest.pulls.listFiles, {
const entries = yield octokit.paginate(octokit.rest.pulls.listFiles, {
owner: github_1.context.repo.owner,
repo: github_1.context.repo.repo,
pull_number: github_1.context.payload.pull_request.number,
});
return files.map(file => file.filename);
return entries.map(({ filename, status }) => ({ path: filename, status }));
}
catch (error) {
core.setFailed(`Getting changed files failed with error: ${error}`);
Expand All @@ -264,6 +283,44 @@ function getChangedFilesImpl(token) {
}


/***/ }),

/***/ 9091:
/***/ ((__unused_webpack_module, exports) => {

"use strict";

Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.stringifyForShell = void 0;
function stringifyArrayItem(item) {
switch (typeof item) {
case 'number':
return item.toString();
case 'string':
return `'${item}'`;
default:
return JSON.stringify(item);
}
}
function stringifyForShell(value) {
switch (typeof value) {
case 'string':
return value;
case 'object':
if (Array.isArray(value)) {
return value.map(stringifyArrayItem).join(' ');
}
if (value === null) {
return '';
}
return value.toString();
default:
return JSON.stringify(value);
}
}
exports.stringifyForShell = stringifyForShell;


/***/ }),

/***/ 2013:
Expand Down Expand Up @@ -514,20 +571,26 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
});
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
const fs = __importStar(__nccwpck_require__(7147));
const core = __importStar(__nccwpck_require__(5316));
const common_1 = __nccwpck_require__(9145);
function run() {
return __awaiter(this, void 0, void 0, function* () {
try {
const pathPatterns = core.getInput(common_1.inputs.PATHS).split(';');
const patterns = (0, common_1.normalizePatterns)(core.getInput(common_1.inputs.PATHS).split(';'));
const token = core.getInput(common_1.inputs.GH_TOKEN, { required: true });
const output = core.getInput(common_1.inputs.OUTPUT, { required: true });
console.log('patterns: ' + JSON.stringify(pathPatterns, undefined, 2));
console.log('patterns: ' + JSON.stringify(patterns, undefined, 2));
const changedFiles = yield (0, common_1.getChangedFiles)(token);
const filteredFiles = (0, common_1.filterPaths)(changedFiles, pathPatterns);
(0, common_1.ensureDir)(output);
fs.writeFileSync(output, JSON.stringify(filteredFiles.map(filename => ({ filename })), undefined, 2));
const filteredFiles = patterns === undefined
? changedFiles
: changedFiles.filter(({ path }) => (0, common_1.testPath)(path, patterns));
(0, common_1.setOutputs)({
json: JSON.stringify({
files: filteredFiles,
count: filteredFiles.length,
}),
files: filteredFiles.map(e => e.path),
count: filteredFiles.length,
});
}
catch (error) {
if (error instanceof Error) {
Expand Down
Loading