mirror of
https://github.com/dorny/paths-filter.git
synced 2026-06-29 09:11:40 +00:00
Compare commits
7 Commits
0f7976ff52
...
a2f17f45cf
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a2f17f45cf | ||
|
|
b82ff81ffb | ||
|
|
fbd0ab8f3e | ||
|
|
efb1da7ce8 | ||
|
|
d8f7b061b2 | ||
|
|
addbc147a9 | ||
|
|
8ec3b44912 |
@ -18,6 +18,13 @@ jobs:
|
||||
- run: |
|
||||
npm install
|
||||
npm run all
|
||||
- name: Check dist is up to date
|
||||
run: |
|
||||
if [ -n "$(git diff --name-only dist/)" ]; then
|
||||
echo "::error::dist/index.js is out of date. Run 'npm run all' and commit the result."
|
||||
git diff --stat dist/
|
||||
exit 1
|
||||
fi
|
||||
|
||||
test-inline:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
113
README.md
113
README.md
@ -27,6 +27,11 @@ don't allow this because they don't work on a level of individual jobs or steps.
|
||||
- The `base` input parameter must not be the same as the branch that triggered the workflow
|
||||
- Changes are detected against the merge-base with the configured base branch or the default branch
|
||||
- Uses git commands to detect changes - repository must be already [checked out](https://github.com/actions/checkout)
|
||||
- **[Merge queue](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/configuring-pull-request-merges/managing-a-merge-queue):**
|
||||
- Workflow triggered by **[merge_group](https://docs.github.com/en/actions/reference/events-that-trigger-workflows#merge_group)**
|
||||
- The `base` and `ref` input parameters default to commit hashes from the event
|
||||
unless explicitly specified.
|
||||
- Uses git commands to detect changes - repository must be already [checked out](https://github.com/actions/checkout)
|
||||
- **Master, Release, or other long-lived branches:**
|
||||
- Workflow triggered by **[push](https://docs.github.com/en/actions/reference/events-that-trigger-workflows#push)** event
|
||||
when `base` input parameter is the same as the branch that triggered the workflow:
|
||||
@ -46,7 +51,7 @@ don't allow this because they don't work on a level of individual jobs or steps.
|
||||
## Example
|
||||
|
||||
```yaml
|
||||
- uses: dorny/paths-filter@v3
|
||||
- uses: dorny/paths-filter@v4
|
||||
id: changes
|
||||
with:
|
||||
filters: |
|
||||
@ -72,7 +77,7 @@ For more scenarios see [examples](#examples) section.
|
||||
|
||||
## What's New
|
||||
|
||||
- New major release `v3` after update to Node 20 [Breaking change]
|
||||
- New major release `v4` after update to Node 24 [Breaking change]
|
||||
- Add `ref` input parameter
|
||||
- Add `list-files: csv` format
|
||||
- Configure matrix job to run for each folder with changes using `changes` output
|
||||
@ -84,7 +89,7 @@ For more information, see [CHANGELOG](https://github.com/dorny/paths-filter/blob
|
||||
## Usage
|
||||
|
||||
```yaml
|
||||
- uses: dorny/paths-filter@v3
|
||||
- uses: dorny/paths-filter@v4
|
||||
with:
|
||||
# Defines filters applied to detected changed files.
|
||||
# Each filter has a name and a list of rules.
|
||||
@ -104,6 +109,8 @@ For more information, see [CHANGELOG](https://github.com/dorny/paths-filter/blob
|
||||
# Branch, tag, or commit SHA against which the changes will be detected.
|
||||
# If it references the same branch it was pushed to,
|
||||
# changes are detected against the most recent commit before the push.
|
||||
# If it is empty and action is triggered by merge_group event,
|
||||
# the base commit in the event will be used.
|
||||
# Otherwise, it uses git merge-base to find the best common ancestor between
|
||||
# current branch (HEAD) and base.
|
||||
# When merge-base is found, it's used for change detection - only changes
|
||||
@ -117,6 +124,8 @@ For more information, see [CHANGELOG](https://github.com/dorny/paths-filter/blob
|
||||
# Git reference (e.g. branch name) from which the changes will be detected.
|
||||
# Useful when workflow can be triggered only on the default branch (e.g. repository_dispatch event)
|
||||
# but you want to get changes on a different branch.
|
||||
# If this is empty and action is triggered by merge_group event,
|
||||
# the head commit in the event will be used.
|
||||
# This option is ignored if action is triggered by pull_request event.
|
||||
# default: ${{ github.ref }}
|
||||
ref:
|
||||
@ -142,6 +151,13 @@ For more information, see [CHANGELOG](https://github.com/dorny/paths-filter/blob
|
||||
# Default: none
|
||||
list-files: ''
|
||||
|
||||
# Enables writing the lists of matching files to a corresponding file.
|
||||
# If set, the action will create the specified file with the list of matching files.
|
||||
# The file will be written in the format specified by the `list-files` option and named
|
||||
# after the filter. The path to the file will be relative to the working directory and
|
||||
# exported as an output variable named `<filter-name>_files_path`.
|
||||
write-to-files: ''
|
||||
|
||||
# Relative path under $GITHUB_WORKSPACE where the repository was checked out.
|
||||
working-directory: ''
|
||||
|
||||
@ -154,14 +170,14 @@ For more information, see [CHANGELOG](https://github.com/dorny/paths-filter/blob
|
||||
# Default: ${{ github.token }}
|
||||
token: ''
|
||||
|
||||
# Optional parameter to override the default behavior of file matching algorithm.
|
||||
# Optional parameter to override the default behavior of file matching algorithm.
|
||||
# By default files that match at least one pattern defined by the filters will be included.
|
||||
# This parameter allows to override the "at least one pattern" behavior to make it so that
|
||||
# all of the patterns have to match or otherwise the file is excluded.
|
||||
# An example scenario where this is useful if you would like to match all
|
||||
# .ts files in a sub-directory but not .md files.
|
||||
# The filters below will match markdown files despite the exclusion syntax UNLESS
|
||||
# you specify 'every' as the predicate-quantifier parameter. When you do that,
|
||||
# all of the patterns have to match or otherwise the file is excluded.
|
||||
# An example scenario where this is useful if you would like to match all
|
||||
# .ts files in a sub-directory but not .md files.
|
||||
# The filters below will match markdown files despite the exclusion syntax UNLESS
|
||||
# you specify 'every' as the predicate-quantifier parameter. When you do that,
|
||||
# it will only match the .ts files in the subdirectory as expected.
|
||||
#
|
||||
# backend:
|
||||
@ -192,8 +208,8 @@ jobs:
|
||||
tests:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dorny/paths-filter@v3
|
||||
- uses: actions/checkout@v6
|
||||
- uses: dorny/paths-filter@v4
|
||||
id: filter
|
||||
with:
|
||||
filters: |
|
||||
@ -237,7 +253,7 @@ jobs:
|
||||
frontend: ${{ steps.filter.outputs.frontend }}
|
||||
steps:
|
||||
# For pull requests it's not necessary to checkout the code
|
||||
- uses: dorny/paths-filter@v3
|
||||
- uses: dorny/paths-filter@v4
|
||||
id: filter
|
||||
with:
|
||||
filters: |
|
||||
@ -252,7 +268,7 @@ jobs:
|
||||
if: ${{ needs.changes.outputs.backend == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- ...
|
||||
|
||||
# JOB to build and test frontend code
|
||||
@ -261,7 +277,7 @@ jobs:
|
||||
if: ${{ needs.changes.outputs.frontend == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- ...
|
||||
```
|
||||
|
||||
@ -283,7 +299,7 @@ jobs:
|
||||
packages: ${{ steps.filter.outputs.changes }}
|
||||
steps:
|
||||
# For pull requests it's not necessary to checkout the code
|
||||
- uses: dorny/paths-filter@v3
|
||||
- uses: dorny/paths-filter@v4
|
||||
id: filter
|
||||
with:
|
||||
filters: |
|
||||
@ -300,7 +316,7 @@ jobs:
|
||||
package: ${{ fromJSON(needs.changes.outputs.packages) }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- ...
|
||||
```
|
||||
|
||||
@ -317,6 +333,12 @@ on:
|
||||
branches: # PRs to the following branches will trigger the workflow
|
||||
- master
|
||||
- develop
|
||||
# Optionally you can use the action in the merge queue
|
||||
# if your repository enables the feature.
|
||||
merge_group:
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
@ -324,8 +346,8 @@ jobs:
|
||||
permissions:
|
||||
pull-requests: read
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dorny/paths-filter@v3
|
||||
- uses: actions/checkout@v6
|
||||
- uses: dorny/paths-filter@v4
|
||||
id: filter
|
||||
with:
|
||||
filters: ... # Configure your filters
|
||||
@ -345,12 +367,12 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
# This may save additional git fetch roundtrip if
|
||||
# merge-base is found within latest 20 commits
|
||||
fetch-depth: 20
|
||||
- uses: dorny/paths-filter@v3
|
||||
- uses: dorny/paths-filter@v4
|
||||
id: filter
|
||||
with:
|
||||
base: develop # Change detection against merge-base with this branch
|
||||
@ -373,8 +395,8 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dorny/paths-filter@v3
|
||||
- uses: actions/checkout@v6
|
||||
- uses: dorny/paths-filter@v4
|
||||
id: filter
|
||||
with:
|
||||
# Use context to get the branch where commits were pushed.
|
||||
@ -401,14 +423,14 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
# Some action that modifies files tracked by git (e.g. code linter)
|
||||
- uses: johndoe/some-action@v1
|
||||
|
||||
# Filter to detect which files were modified
|
||||
# Changes could be, for example, automatically committed
|
||||
- uses: dorny/paths-filter@v3
|
||||
- uses: dorny/paths-filter@v4
|
||||
id: filter
|
||||
with:
|
||||
base: HEAD
|
||||
@ -423,7 +445,7 @@ jobs:
|
||||
<summary>Define filter rules in own file</summary>
|
||||
|
||||
```yaml
|
||||
- uses: dorny/paths-filter@v3
|
||||
- uses: dorny/paths-filter@v4
|
||||
id: filter
|
||||
with:
|
||||
# Path to file where filters are defined
|
||||
@ -436,7 +458,7 @@ jobs:
|
||||
<summary>Use YAML anchors to reuse path expression(s) inside another rule</summary>
|
||||
|
||||
```yaml
|
||||
- uses: dorny/paths-filter@v3
|
||||
- uses: dorny/paths-filter@v4
|
||||
id: filter
|
||||
with:
|
||||
# &shared is YAML anchor,
|
||||
@ -457,7 +479,7 @@ jobs:
|
||||
<summary>Consider if file was added, modified or deleted</summary>
|
||||
|
||||
```yaml
|
||||
- uses: dorny/paths-filter@v3
|
||||
- uses: dorny/paths-filter@v4
|
||||
id: filter
|
||||
with:
|
||||
# Changed file can be 'added', 'modified', or 'deleted'.
|
||||
@ -483,7 +505,7 @@ jobs:
|
||||
<summary>Detect changes in folder only for some file extensions</summary>
|
||||
|
||||
```yaml
|
||||
- uses: dorny/paths-filter@v3
|
||||
- uses: dorny/paths-filter@v4
|
||||
id: filter
|
||||
with:
|
||||
# This makes it so that all the patterns have to match a file for it to be
|
||||
@ -505,13 +527,44 @@ jobs:
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Handle large change sets (2000+ files)</summary>
|
||||
|
||||
```yaml
|
||||
- uses: dorny/paths-filter@v3
|
||||
id: changed
|
||||
with:
|
||||
# Enable writing the files matching each filter to the disk in addition to the output '<filter_name>_files'.
|
||||
# The path for each filter's file is output in the format '<filter_name>_files_path'.
|
||||
write-to-files: true
|
||||
list-files: json
|
||||
filters: |
|
||||
content:
|
||||
- 'content/**'
|
||||
|
||||
|
||||
- name: List changed directories relative to the base directory
|
||||
shell: bash
|
||||
env:
|
||||
BASE_DIR: ${{ inputs.base-directory }}
|
||||
CHANGED_CONTENT_FILES_PATH: ${{ steps.changed.outputs.content_files_path }}
|
||||
run: |
|
||||
CHANGED_CONTENT_DIRECTORIES=$(cat "${CHANGED_CONTENT_FILES_PATH}" | xargs -n1 realpath -m --relative-to=${BASE_DIR} | cut -f1 -d / | sort -u)
|
||||
for d in $CHANGED_CONTENT_DIRECTORIES
|
||||
do
|
||||
echo "Content directory change detected: ${d}"
|
||||
done
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### Custom processing of changed files
|
||||
|
||||
<details>
|
||||
<summary>Passing list of modified files as command line args in Linux shell</summary>
|
||||
|
||||
```yaml
|
||||
- uses: dorny/paths-filter@v3
|
||||
- uses: dorny/paths-filter@v4
|
||||
id: filter
|
||||
with:
|
||||
# Enable listing of files matching each filter.
|
||||
@ -537,7 +590,7 @@ jobs:
|
||||
<summary>Passing list of modified files as JSON array to another action</summary>
|
||||
|
||||
```yaml
|
||||
- uses: dorny/paths-filter@v3
|
||||
- uses: dorny/paths-filter@v4
|
||||
id: filter
|
||||
with:
|
||||
# Enable listing of files matching each filter.
|
||||
|
||||
@ -36,6 +36,13 @@ inputs:
|
||||
Backslash escapes every potentially unsafe character.
|
||||
required: false
|
||||
default: none
|
||||
write-to-files:
|
||||
description: |
|
||||
Enables writing the lists of matching files to a corresponding file in addition to the output '<filter-name>_files'.
|
||||
If set, the action will create the specified file with the list of matching files.
|
||||
The file will be written in the format specified by the `list-files` option and named
|
||||
after the filter. The path to the file will be output as a variable named `<filter-name>_files_path`.
|
||||
required: false
|
||||
initial-fetch-depth:
|
||||
description: |
|
||||
How many commits are initially fetched from base branch.
|
||||
|
||||
90
dist/index.js
vendored
90
dist/index.js
vendored
@ -552,15 +552,20 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||
const fs = __importStar(__nccwpck_require__(7147));
|
||||
const core = __importStar(__nccwpck_require__(2186));
|
||||
const github = __importStar(__nccwpck_require__(5438));
|
||||
const path_1 = __importDefault(__nccwpck_require__(1017));
|
||||
const filter_1 = __nccwpck_require__(3707);
|
||||
const file_1 = __nccwpck_require__(4014);
|
||||
const git = __importStar(__nccwpck_require__(3374));
|
||||
const shell_escape_1 = __nccwpck_require__(4613);
|
||||
const csv_escape_1 = __nccwpck_require__(7402);
|
||||
const fs_1 = __nccwpck_require__(7147);
|
||||
async function run() {
|
||||
try {
|
||||
const workingDirectory = core.getInput('working-directory', { required: false });
|
||||
@ -573,6 +578,7 @@ async function run() {
|
||||
const filtersInput = core.getInput('filters', { required: true });
|
||||
const filtersYaml = isPathInput(filtersInput) ? getConfigFileContent(filtersInput) : filtersInput;
|
||||
const listFiles = core.getInput('list-files', { required: false }).toLowerCase() || 'none';
|
||||
const writeToFiles = core.getInput('write-to-files', { required: false }).toLowerCase() === 'true';
|
||||
const initialFetchDepth = parseInt(core.getInput('initial-fetch-depth', { required: false })) || 10;
|
||||
const predicateQuantifier = core.getInput('predicate-quantifier', { required: false }) || filter_1.PredicateQuantifier.SOME;
|
||||
if (!isExportFormat(listFiles)) {
|
||||
@ -589,7 +595,7 @@ async function run() {
|
||||
const files = await getChangedFiles(token, base, ref, initialFetchDepth);
|
||||
core.info(`Detected ${files.length} changed files`);
|
||||
const results = filter.match(files);
|
||||
exportResults(results, listFiles);
|
||||
exportResults(results, listFiles, writeToFiles);
|
||||
}
|
||||
catch (error) {
|
||||
core.setFailed(getErrorMessage(error));
|
||||
@ -617,33 +623,49 @@ async function getChangedFiles(token, base, ref, initialFetchDepth) {
|
||||
}
|
||||
return await git.getChangesOnHead();
|
||||
}
|
||||
const prEvents = ['pull_request', 'pull_request_review', 'pull_request_review_comment', 'pull_request_target'];
|
||||
if (prEvents.includes(github.context.eventName)) {
|
||||
if (ref) {
|
||||
core.warning(`'ref' input parameter is ignored when 'base' is set to HEAD`);
|
||||
switch (github.context.eventName) {
|
||||
// To keep backward compatibility, commits in GitHub pull request event
|
||||
// take precedence over manual inputs.
|
||||
case 'pull_request':
|
||||
case 'pull_request_review':
|
||||
case 'pull_request_review_comment':
|
||||
case 'pull_request_target': {
|
||||
if (ref) {
|
||||
core.warning(`'ref' input parameter is ignored when action is triggered by pull request event`);
|
||||
}
|
||||
if (base) {
|
||||
core.warning(`'base' input parameter is ignored when action is triggered by pull request event`);
|
||||
}
|
||||
const pr = github.context.payload.pull_request;
|
||||
if (token) {
|
||||
return await getChangedFilesFromApi(token, pr);
|
||||
}
|
||||
if (github.context.eventName === 'pull_request_target') {
|
||||
// pull_request_target is executed in context of base branch and GITHUB_SHA points to last commit in base branch
|
||||
// Therefore it's not possible to look at changes in last commit
|
||||
// At the same time we don't want to fetch any code from forked repository
|
||||
throw new Error(`'token' input parameter is required if action is triggered by 'pull_request_target' event`);
|
||||
}
|
||||
core.info('Github token is not available - changes will be detected using git diff');
|
||||
const baseSha = (_a = github.context.payload.pull_request) === null || _a === void 0 ? void 0 : _a.base.sha;
|
||||
const defaultBranch = (_b = github.context.payload.repository) === null || _b === void 0 ? void 0 : _b.default_branch;
|
||||
const currentRef = await git.getCurrentRef();
|
||||
return await git.getChanges(base || baseSha || defaultBranch, currentRef);
|
||||
}
|
||||
if (base) {
|
||||
core.warning(`'base' input parameter is ignored when action is triggered by pull request event`);
|
||||
// To keep backward compatibility, manual inputs take precedence over
|
||||
// commits in GitHub merge queue event.
|
||||
case 'merge_group': {
|
||||
const mergeGroup = github.context.payload;
|
||||
if (!base) {
|
||||
base = mergeGroup.merge_group.base_sha;
|
||||
}
|
||||
if (!ref) {
|
||||
ref = mergeGroup.merge_group.head_sha;
|
||||
}
|
||||
break;
|
||||
}
|
||||
const pr = github.context.payload.pull_request;
|
||||
if (token) {
|
||||
return await getChangedFilesFromApi(token, pr);
|
||||
}
|
||||
if (github.context.eventName === 'pull_request_target') {
|
||||
// pull_request_target is executed in context of base branch and GITHUB_SHA points to last commit in base branch
|
||||
// Therefor it's not possible to look at changes in last commit
|
||||
// At the same time we don't want to fetch any code from forked repository
|
||||
throw new Error(`'token' input parameter is required if action is triggered by 'pull_request_target' event`);
|
||||
}
|
||||
core.info('Github token is not available - changes will be detected using git diff');
|
||||
const baseSha = (_a = github.context.payload.pull_request) === null || _a === void 0 ? void 0 : _a.base.sha;
|
||||
const defaultBranch = (_b = github.context.payload.repository) === null || _b === void 0 ? void 0 : _b.default_branch;
|
||||
const currentRef = await git.getCurrentRef();
|
||||
return await git.getChanges(base || baseSha || defaultBranch, currentRef);
|
||||
}
|
||||
else {
|
||||
return getChangedFilesFromGit(base, ref, initialFetchDepth);
|
||||
}
|
||||
return getChangedFilesFromGit(base, ref, initialFetchDepth);
|
||||
}
|
||||
async function getChangedFilesFromGit(base, head, initialFetchDepth) {
|
||||
var _a;
|
||||
@ -742,13 +764,14 @@ async function getChangedFilesFromApi(token, pullRequest) {
|
||||
core.endGroup();
|
||||
}
|
||||
}
|
||||
function exportResults(results, format) {
|
||||
function exportResults(results, format, writeToFiles) {
|
||||
const tempDir = (0, fs_1.mkdtempSync)(path_1.default.join(process.cwd(), 'paths-filter-'));
|
||||
core.info('Results:');
|
||||
const changes = [];
|
||||
for (const [key, files] of Object.entries(results)) {
|
||||
const value = files.length > 0;
|
||||
core.startGroup(`Filter ${key} = ${value}`);
|
||||
if (files.length > 0) {
|
||||
const match = files.length > 0;
|
||||
core.startGroup(`Filter ${key} = ${match}`);
|
||||
if (match) {
|
||||
changes.push(key);
|
||||
core.info('Matching files:');
|
||||
for (const file of files) {
|
||||
@ -758,11 +781,18 @@ function exportResults(results, format) {
|
||||
else {
|
||||
core.info('Matching files: none');
|
||||
}
|
||||
core.setOutput(key, value);
|
||||
core.setOutput(key, match);
|
||||
core.setOutput(`${key}_count`, files.length);
|
||||
if (format !== 'none') {
|
||||
const filesValue = serializeExport(files, format);
|
||||
core.setOutput(`${key}_files`, filesValue);
|
||||
if (writeToFiles) {
|
||||
const ext = format === 'json' ? 'json' : 'txt';
|
||||
const filePath = path_1.default.join(tempDir, `${key}-files.${ext}`);
|
||||
fs.writeFileSync(filePath, filesValue);
|
||||
core.info(`Matching files list for filter '${key}' written to '${filePath}'`);
|
||||
core.setOutput(`${key}_files_path`, filePath);
|
||||
}
|
||||
}
|
||||
core.endGroup();
|
||||
}
|
||||
|
||||
94
src/main.ts
94
src/main.ts
@ -1,8 +1,9 @@
|
||||
import * as fs from 'fs'
|
||||
import * as core from '@actions/core'
|
||||
import * as github from '@actions/github'
|
||||
import path from 'path'
|
||||
import {GetResponseDataTypeFromEndpointMethod} from '@octokit/types'
|
||||
import {PushEvent, PullRequestEvent} from '@octokit/webhooks-types'
|
||||
import {MergeGroupEvent, PullRequest, PushEvent} from '@octokit/webhooks-types'
|
||||
|
||||
import {
|
||||
isPredicateQuantifier,
|
||||
@ -16,6 +17,7 @@ import {File, ChangeStatus} from './file'
|
||||
import * as git from './git'
|
||||
import {backslashEscape, shellEscape} from './list-format/shell-escape'
|
||||
import {csvEscape} from './list-format/csv-escape'
|
||||
import {mkdtempSync} from 'fs'
|
||||
|
||||
type ExportFormat = 'none' | 'csv' | 'json' | 'shell' | 'escape'
|
||||
|
||||
@ -32,6 +34,7 @@ async function run(): Promise<void> {
|
||||
const filtersInput = core.getInput('filters', {required: true})
|
||||
const filtersYaml = isPathInput(filtersInput) ? getConfigFileContent(filtersInput) : filtersInput
|
||||
const listFiles = core.getInput('list-files', {required: false}).toLowerCase() || 'none'
|
||||
const writeToFiles = core.getInput('write-to-files', {required: false}).toLowerCase() === 'true'
|
||||
const initialFetchDepth = parseInt(core.getInput('initial-fetch-depth', {required: false})) || 10
|
||||
const predicateQuantifier = core.getInput('predicate-quantifier', {required: false}) || PredicateQuantifier.SOME
|
||||
|
||||
@ -52,7 +55,7 @@ async function run(): Promise<void> {
|
||||
const files = await getChangedFiles(token, base, ref, initialFetchDepth)
|
||||
core.info(`Detected ${files.length} changed files`)
|
||||
const results = filter.match(files)
|
||||
exportResults(results, listFiles)
|
||||
exportResults(results, listFiles, writeToFiles)
|
||||
} catch (error) {
|
||||
core.setFailed(getErrorMessage(error))
|
||||
}
|
||||
@ -84,32 +87,50 @@ async function getChangedFiles(token: string, base: string, ref: string, initial
|
||||
return await git.getChangesOnHead()
|
||||
}
|
||||
|
||||
const prEvents = ['pull_request', 'pull_request_review', 'pull_request_review_comment', 'pull_request_target']
|
||||
if (prEvents.includes(github.context.eventName)) {
|
||||
if (ref) {
|
||||
core.warning(`'ref' input parameter is ignored when 'base' is set to HEAD`)
|
||||
switch (github.context.eventName) {
|
||||
// To keep backward compatibility, commits in GitHub pull request event
|
||||
// take precedence over manual inputs.
|
||||
case 'pull_request':
|
||||
case 'pull_request_review':
|
||||
case 'pull_request_review_comment':
|
||||
case 'pull_request_target': {
|
||||
if (ref) {
|
||||
core.warning(`'ref' input parameter is ignored when action is triggered by pull request event`)
|
||||
}
|
||||
if (base) {
|
||||
core.warning(`'base' input parameter is ignored when action is triggered by pull request event`)
|
||||
}
|
||||
const pr = github.context.payload.pull_request as PullRequest
|
||||
if (token) {
|
||||
return await getChangedFilesFromApi(token, pr)
|
||||
}
|
||||
if (github.context.eventName === 'pull_request_target') {
|
||||
// pull_request_target is executed in context of base branch and GITHUB_SHA points to last commit in base branch
|
||||
// Therefore it's not possible to look at changes in last commit
|
||||
// At the same time we don't want to fetch any code from forked repository
|
||||
throw new Error(`'token' input parameter is required if action is triggered by 'pull_request_target' event`)
|
||||
}
|
||||
core.info('Github token is not available - changes will be detected using git diff')
|
||||
const baseSha = github.context.payload.pull_request?.base.sha
|
||||
const defaultBranch = github.context.payload.repository?.default_branch
|
||||
const currentRef = await git.getCurrentRef()
|
||||
return await git.getChanges(base || baseSha || defaultBranch, currentRef)
|
||||
}
|
||||
if (base) {
|
||||
core.warning(`'base' input parameter is ignored when action is triggered by pull request event`)
|
||||
// To keep backward compatibility, manual inputs take precedence over
|
||||
// commits in GitHub merge queue event.
|
||||
case 'merge_group': {
|
||||
const mergeGroup = github.context.payload as MergeGroupEvent
|
||||
if (!base) {
|
||||
base = mergeGroup.merge_group.base_sha
|
||||
}
|
||||
if (!ref) {
|
||||
ref = mergeGroup.merge_group.head_sha
|
||||
}
|
||||
break
|
||||
}
|
||||
const pr = github.context.payload.pull_request as PullRequestEvent
|
||||
if (token) {
|
||||
return await getChangedFilesFromApi(token, pr)
|
||||
}
|
||||
if (github.context.eventName === 'pull_request_target') {
|
||||
// pull_request_target is executed in context of base branch and GITHUB_SHA points to last commit in base branch
|
||||
// Therefor it's not possible to look at changes in last commit
|
||||
// At the same time we don't want to fetch any code from forked repository
|
||||
throw new Error(`'token' input parameter is required if action is triggered by 'pull_request_target' event`)
|
||||
}
|
||||
core.info('Github token is not available - changes will be detected using git diff')
|
||||
const baseSha = github.context.payload.pull_request?.base.sha
|
||||
const defaultBranch = github.context.payload.repository?.default_branch
|
||||
const currentRef = await git.getCurrentRef()
|
||||
return await git.getChanges(base || baseSha || defaultBranch, currentRef)
|
||||
} else {
|
||||
return getChangedFilesFromGit(base, ref, initialFetchDepth)
|
||||
}
|
||||
|
||||
return getChangedFilesFromGit(base, ref, initialFetchDepth)
|
||||
}
|
||||
|
||||
async function getChangedFilesFromGit(base: string, head: string, initialFetchDepth: number): Promise<File[]> {
|
||||
@ -175,7 +196,7 @@ async function getChangedFilesFromGit(base: string, head: string, initialFetchDe
|
||||
}
|
||||
|
||||
// Uses github REST api to get list of files changed in PR
|
||||
async function getChangedFilesFromApi(token: string, pullRequest: PullRequestEvent): Promise<File[]> {
|
||||
async function getChangedFilesFromApi(token: string, pullRequest: PullRequest): Promise<File[]> {
|
||||
core.startGroup(`Fetching list of changed files for PR#${pullRequest.number} from Github API`)
|
||||
try {
|
||||
const client = github.getOctokit(token)
|
||||
@ -228,13 +249,15 @@ async function getChangedFilesFromApi(token: string, pullRequest: PullRequestEve
|
||||
}
|
||||
}
|
||||
|
||||
function exportResults(results: FilterResults, format: ExportFormat): void {
|
||||
function exportResults(results: FilterResults, format: ExportFormat, writeToFiles: boolean): void {
|
||||
const tempDir = mkdtempSync(path.join(process.cwd(), 'paths-filter-'))
|
||||
|
||||
core.info('Results:')
|
||||
const changes = []
|
||||
for (const [key, files] of Object.entries(results)) {
|
||||
const value = files.length > 0
|
||||
core.startGroup(`Filter ${key} = ${value}`)
|
||||
if (files.length > 0) {
|
||||
const match = files.length > 0
|
||||
core.startGroup(`Filter ${key} = ${match}`)
|
||||
if (match) {
|
||||
changes.push(key)
|
||||
core.info('Matching files:')
|
||||
for (const file of files) {
|
||||
@ -244,12 +267,21 @@ function exportResults(results: FilterResults, format: ExportFormat): void {
|
||||
core.info('Matching files: none')
|
||||
}
|
||||
|
||||
core.setOutput(key, value)
|
||||
core.setOutput(key, match)
|
||||
core.setOutput(`${key}_count`, files.length)
|
||||
if (format !== 'none') {
|
||||
const filesValue = serializeExport(files, format)
|
||||
core.setOutput(`${key}_files`, filesValue)
|
||||
|
||||
if (writeToFiles) {
|
||||
const ext = format === 'json' ? 'json' : 'txt'
|
||||
const filePath = path.join(tempDir, `${key}-files.${ext}`)
|
||||
fs.writeFileSync(filePath, filesValue)
|
||||
core.info(`Matching files list for filter '${key}' written to '${filePath}'`)
|
||||
core.setOutput(`${key}_files_path`, filePath)
|
||||
}
|
||||
}
|
||||
|
||||
core.endGroup()
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user