6 Commits

Author SHA1 Message Date
Michal Dorner
83deb9f037 Improve change detection for feature branches (#16)
* Detect changes against configured base branch

* Update README and action.yml

* Add job.outputs example

* Update CHANGELOG
2020-06-24 21:53:31 +02:00
Michal Dorner
7d201829e2 Support reusable paths blocks via yaml anchors (#13)
* Add support for nested arrays of path expressions

* Remove pull_request trigger type options

Default value is fine: opened, synchronize, reopened

* Add CHANGELOG

* Update README
2020-06-19 23:39:06 +02:00
Michal Dorner
4eb15bc267 Update README
Add links to pull_request / push events that trigger workflows
2020-06-15 22:15:38 +02:00
Michal Dorner
9ef7936e79 Fix URL in README 2020-06-15 21:57:00 +02:00
Michal Dorner
affb29871a Support push event (#10)
* Support triggering from push event
* Add self-test to build workflow
* Update action metadata
2020-06-15 21:49:10 +02:00
Tonye Jack
910e8b1235 Update README.md (#11)
* Update README.md

* Update version
2020-06-14 23:04:35 +02:00
15 changed files with 564 additions and 162 deletions

View File

@@ -25,7 +25,7 @@
"@typescript-eslint/generic-type-naming": ["error", "^[A-Z][A-Za-z]*$"],
"@typescript-eslint/no-array-constructor": "error",
"@typescript-eslint/no-empty-interface": "error",
"@typescript-eslint/no-explicit-any": "error",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-extraneous-class": "error",
"@typescript-eslint/no-for-in-array": "error",
"@typescript-eslint/no-inferrable-types": "error",
@@ -42,7 +42,7 @@
"@typescript-eslint/prefer-includes": "error",
"@typescript-eslint/prefer-interface": "error",
"@typescript-eslint/prefer-string-starts-ends-with": "error",
"@typescript-eslint/promise-function-async": "error",
"@typescript-eslint/promise-function-async": ["error", { "allowAny": true }],
"@typescript-eslint/require-array-sort-compare": "error",
"@typescript-eslint/restrict-plus-operands": "error",
"semi": "off",

View File

@@ -12,3 +12,15 @@ jobs:
- run: |
npm install
npm run all
self-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: ./
id: filter
with:
filters: '.github/filters.yml'
- name: filter-test
if: steps.filter.outputs.any != 'true' || steps.filter.outputs.error == 'true'
run: exit 1

View File

@@ -1,9 +1,6 @@
name: "Pull Request Verification"
on:
pull_request:
types:
- opened
- synchronize
branches:
- master

21
CHANGELOG.md Normal file
View File

@@ -0,0 +1,21 @@
# Changelog
## v2.2.0
- [Improve change detection for feature branches](https://github.com/dorny/paths-filter/pull/16)
## v2.1.0
- [Support reusable paths blocks with yaml anchors](https://github.com/dorny/paths-filter/pull/13)
## v2.0.0
- [Added support for workflows triggered by push events](https://github.com/dorny/paths-filter/pull/10)
- Action and repository renamed to paths-filter - original name doesn't make sense anymore
## v1.1.0
- [Allows filters to be specified in own .yml file](https://github.com/dorny/paths-filter/pull/8)
- [Adds alternative change detection using git fetch and git diff-index](https://github.com/dorny/paths-filter/pull/9)
## v1.0.1
Updated dependencies - fixes github security alert
## v1.0.0
First official release uploaded to marketplace.

View File

@@ -1,7 +1,7 @@
The MIT License (MIT)
Copyright (c) 2018 GitHub, Inc. and contributors
Copyright (c) 2020 Michal Dorner and contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -1,27 +1,34 @@
<p align="center">
<a href="https://github.com/dorny/pr-changed-files-filter/actions"><img alt="typescript-action status" src="https://github.com/dorny/pr-changed-files-filter/workflows/Build/badge.svg"></a>
<a href="https://github.com/dorny/paths-filter/actions"><img alt="paths-filter status" src="https://github.com/dorny/paths-filter/workflows/Build/badge.svg"></a>
</p>
**CAUTION**: This action can be only used in a workflow triggered by `pull_request` event.
# Paths filter
# Pull request changed files filter
With this [Github Action](https://github.com/features/actions) you can execute your workflow steps only if relevant files are modified.
This [Github Action](https://github.com/features/actions) enables conditional execution of workflow job steps considering which files are modified by a pull request.
It saves time and resources especially in monorepo setups, where you can run slow tasks (e.g. integration tests) only for changed components.
Github workflows built-in
[path filters](https://help.github.com/en/actions/referenceworkflow-syntax-for-github-actions#onpushpull_requestpaths)
It saves time and resources especially in monorepo setups, where you can run slow tasks (e.g. integration tests or deployments) only for changed components.
Github workflows built-in [path filters](https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#onpushpull_requestpaths)
doesn't allow this because they doesn't work on a level of individual jobs or steps.
Supported workflows:
- Action triggered by **[pull_request](https://help.github.com/en/actions/reference/events-that-trigger-workflows#pull-request-event-pull_request)** event:
- changes detected against the pull request base branch
- Action triggered by **[push](https://help.github.com/en/actions/reference/events-that-trigger-workflows#push-event-push)** event:
- changes detected against the most recent commit on the same branch before the push
- changes detected against the top of the configured *base* branch (e.g. master)
## Usage
The action accepts filter rules in the YAML format.
Filter rules are defined using YAML format.
Each filter rule is a list of [glob expressions](https://github.com/isaacs/minimatch).
Corresponding output variable will be created to indicate if there's a changed file matching any of the rule glob expressions.
Output variables can be later used in the `if` clause to conditionally run specific steps.
### Inputs
- **`token`**: GitHub Access Token - defaults to `${{ github.token }}`
- **`token`**: GitHub Access Token - defaults to `${{ github.token }}` so you don't have to explicitly provide it.
- **`base`**: Git reference (e.g. branch name) against which the changes will be detected. Defaults to repository default branch (e.g. master).
If it references same branch it was pushed to, changes are detected against the most recent commit before the push.
This option is ignored if action is triggered by *pull_request* event.
- **`filters`**: Path to the configuration file or directly embedded string in YAML format. Filter configuration is a dictionary, where keys specifies rule names and values are lists of file path patterns.
### Outputs
@@ -29,20 +36,20 @@ Output variables can be later used in the `if` clause to conditionally run speci
- `'true'` - if **any** of changed files matches any of rule patterns
- `'false'` - if **none** of changed files matches any of rule patterns
### Notes
- minimatch [dot](https://www.npmjs.com/package/minimatch#dot) option is set to true - therefore
globbing will match also paths where file or folder name starts with a dot.
- You can use YAML anchors to reuse path expression(s) inside another rule. See example in the tests.
- If changes are detected against the previous commit and there is none (i.e. first push of a new branch), all filter rules will report changed files.
- You can use `base: ${{ github.ref }}` to configure change detection against previous commit for every branch you create.
### Sample workflow
### Example
```yaml
name: Build verification
on:
push:
branches:
- master
pull_request:
types:
- opened
- synchronize
branches:
- master
jobs:
@@ -50,7 +57,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: dorny/pr-changed-files-filter@v1
- uses: dorny/paths-filter@v2.2.0
id: filter
with:
# inline YAML or path to separate file (e.g.: .github/filters.yaml)
@@ -76,20 +83,55 @@ jobs:
run: ...
```
If your workflow uses multiple jobs, you can put *paths-filter* into own job and use
[job outputs](https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjobs_idoutputs)
in other jobs [if](https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idif) statements:
```yml
on:
pull_request:
branches:
- master
jobs:
changes:
runs-on: ubuntu-latest
# Set job outputs to values from filter step
outputs:
backend: ${{ steps.filter.outputs.backend }}
frontend: ${{ steps.filter.outputs.frontend }}
steps:
# For pull requests it's not necessary to checkout the code
- uses: dorny/paths-filter@v2.2.0
id: filter
with:
# Filters stored in own yaml file
filters: '.github/filters.yml'
backend:
if: ${{ needs.changes.outputs.backend == 'true' }}
steps:
- ...
frontend:
if: ${{ needs.changes.outputs.frontend == 'true' }}
steps:
- ...
```
## How it works
1. Required inputs are checked (`filters`)
2. If token was provided, it's used to fetch list of changed files from Github API.
3. If token was not provided, base branch is fetched and changed files are detected using `git diff-index` command.
4. For each filter rule it checks if there is any matching file
5. Output variables are set
1. If action was triggered by pull request:
- If access token was provided it's used to fetch list of changed files from Github API.
- If access token was not provided, top of the base branch is fetched and changed files are detected using `git diff-index <SHA>` command.
2. If action was triggered by push event
- if *base* input parameter references same branch it was pushed to, most recent commit before the push is fetched
- If *base* input parameter references other branch, top of that branch is fetched
- changed files are detected using `git diff-index FETCH_HEAD` command.
3. For each filter rule it checks if there is any matching file
4. Output variables are set
## Difference from related projects:
## Difference from similar projects:
- [Has Changed Path](https://github.com/MarceloPrado/has-changed-path)
- detects changes from previous commit
- you have to configure `checkout` action to fetch some number of previous commits
- `git diff` is used for change detection
- outputs only single `true` / `false` value if any of provided paths contains changes
- [Changed Files Exporter](https://github.com/futuratrepadeira/changed-files)
- outputs lists with paths of created, updated and deleted files
@@ -97,4 +139,4 @@ jobs:
- [Changed File Filter](https://github.com/tony84727/changed-file-filter)
- allows change detection between any refs or commits
- fetches whole history of your git repository
- might have negative performance impact on big repositories (github by default fetches only single commit)
- might have negative performance impact on big repositories (github by default fetches only single commit)

View File

@@ -90,4 +90,18 @@ describe('matching tests', () => {
const match = filter.match(['.test/.test.js'])
expect(match.dot).toBeTruthy()
})
test('matches path based on rules included using YAML anchor', () => {
const yaml = `
shared: &shared
- common/**/*
- config/**/*
src:
- *shared
- src/**/*
`
let filter = new Filter(yaml)
const match = filter.match(['config/settings.yml'])
expect(match.src).toBeTruthy()
})
})

19
__tests__/git.test.ts Normal file
View File

@@ -0,0 +1,19 @@
import * as git from '../src/git'
describe('git utility function tests (those not invoking git)', () => {
test('Detects if ref references a tag', () => {
expect(git.isTagRef('refs/tags/v1.0')).toBeTruthy()
expect(git.isTagRef('refs/heads/master')).toBeFalsy()
expect(git.isTagRef('master')).toBeFalsy()
})
test('Trims "refs/" from ref', () => {
expect(git.trimRefs('refs/heads/master')).toBe('heads/master')
expect(git.trimRefs('heads/master')).toBe('heads/master')
expect(git.trimRefs('master')).toBe('master')
})
test('Trims "refs/" and "heads/" from ref', () => {
expect(git.trimRefsHeads('refs/heads/master')).toBe('master')
expect(git.trimRefsHeads('heads/master')).toBe('master')
expect(git.trimRefsHeads('master')).toBe('master')
})
})

View File

@@ -1,11 +1,17 @@
name: 'Pull request changed files filter'
description: 'Enables conditional execution of workflow job steps considering which files are modified by a pull request.'
name: 'Paths filter'
description: 'Execute your workflow steps only if relevant files are modified.'
author: 'Michal Dorner <dorner.michal@gmail.com>'
inputs:
token:
description: 'GitHub Access Token'
required: false
default: ${{ github.token }}
base:
description: |
Git reference (e.g. branch name) against which the changes will be detected. Defaults to repository default branch (e.g. master).
If it references same branch it was pushed to, changes are detected against the most recent commit before the push.
This option is ignored if action is triggered by pull_request event.
required: false
filters:
description: 'Path to the configuration file or YAML string with filters definition'
required: true

108
dist/index.js vendored
View File

@@ -3798,27 +3798,29 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getChangedFiles = exports.fetchBranch = void 0;
exports.trimRefsHeads = exports.trimRefs = exports.isTagRef = exports.getChangedFiles = exports.fetchCommit = exports.FETCH_HEAD = exports.NULL_SHA = void 0;
const exec_1 = __webpack_require__(986);
function fetchBranch(base) {
exports.NULL_SHA = '0000000000000000000000000000000000000000';
exports.FETCH_HEAD = 'FETCH_HEAD';
function fetchCommit(ref) {
return __awaiter(this, void 0, void 0, function* () {
const exitCode = yield exec_1.exec('git', ['fetch', '--depth=1', 'origin', base]);
const exitCode = yield exec_1.exec('git', ['fetch', '--depth=1', '--no-tags', 'origin', ref]);
if (exitCode !== 0) {
throw new Error(`Fetching branch ${base} failed, exiting`);
throw new Error(`Fetching ${ref} failed`);
}
});
}
exports.fetchBranch = fetchBranch;
function getChangedFiles(base) {
exports.fetchCommit = fetchCommit;
function getChangedFiles(ref) {
return __awaiter(this, void 0, void 0, function* () {
let output = '';
const exitCode = yield exec_1.exec('git', ['diff-index', '--name-only', base], {
const exitCode = yield exec_1.exec('git', ['diff-index', '--name-only', ref], {
listeners: {
stdout: (data) => (output += data.toString())
}
});
if (exitCode !== 0) {
throw new Error(`Couldn't determine changed files, exiting`);
throw new Error(`Couldn't determine changed files`);
}
return output
.split('\n')
@@ -3827,6 +3829,22 @@ function getChangedFiles(base) {
});
}
exports.getChangedFiles = getChangedFiles;
function isTagRef(ref) {
return ref.startsWith('refs/tags/');
}
exports.isTagRef = isTagRef;
function trimRefs(ref) {
return trimStart(ref, 'refs/');
}
exports.trimRefs = trimRefs;
function trimRefsHeads(ref) {
const trimRef = trimStart(ref, 'refs/');
return trimStart(trimRef, 'heads/');
}
exports.trimRefsHeads = trimRefsHeads;
function trimStart(ref, start) {
return ref.startsWith(start) ? ref.substr(start.length) : ref;
}
/***/ }),
@@ -4485,16 +4503,20 @@ function run() {
const token = core.getInput('token', { required: false });
const filtersInput = core.getInput('filters', { required: true });
const filtersYaml = isPathInput(filtersInput) ? getConfigFileContent(filtersInput) : filtersInput;
if (github.context.eventName !== 'pull_request') {
core.setFailed('This action can be triggered only by pull_request event');
return;
}
const pr = github.context.payload.pull_request;
const filter = new filter_1.default(filtersYaml);
const files = token ? yield getChangedFilesFromApi(token, pr) : yield getChangedFilesFromGit(pr);
const result = filter.match(files);
for (const key in result) {
core.setOutput(key, String(result[key]));
const files = yield getChangedFiles(token);
if (files === null) {
// Change detection was not possible
// Set all filter keys to true (i.e. changed)
for (const key in filter.rules) {
core.setOutput(key, String(true));
}
}
else {
const result = filter.match(files);
for (const key in result) {
core.setOutput(key, String(result[key]));
}
}
}
catch (error) {
@@ -4514,13 +4536,47 @@ function getConfigFileContent(configPath) {
}
return fs.readFileSync(configPath, { encoding: 'utf8' });
}
function getChangedFiles(token) {
return __awaiter(this, void 0, void 0, function* () {
if (github.context.eventName === 'pull_request') {
const pr = github.context.payload.pull_request;
return token ? yield getChangedFilesFromApi(token, pr) : yield getChangedFilesFromGit(pr.base.sha);
}
else if (github.context.eventName === 'push') {
return getChangedFilesFromPush();
}
else {
throw new Error('This action can be triggered only by pull_request or push event');
}
});
}
function getChangedFilesFromPush() {
return __awaiter(this, void 0, void 0, function* () {
const push = github.context.payload;
// No change detection for pushed tags
if (git.isTagRef(push.ref))
return null;
// Get base from input or use repo default branch.
// It it starts with 'refs/', it will be trimmed (git fetch refs/heads/<NAME> doesn't work)
const baseInput = git.trimRefs(core.getInput('base', { required: false }) || push.repository.default_branch);
// If base references same branch it was pushed to, we will do comparison against the previously pushed commit.
// Otherwise changes are detected against the base reference
const base = git.trimRefsHeads(baseInput) === git.trimRefsHeads(push.ref) ? push.before : baseInput;
// There is no previous commit for comparison
// e.g. change detection against previous commit of just pushed new branch
if (base === git.NULL_SHA)
return null;
return yield getChangedFilesFromGit(base);
});
}
// Fetch base branch and use `git diff` to determine changed files
function getChangedFilesFromGit(pullRequest) {
function getChangedFilesFromGit(ref) {
return __awaiter(this, void 0, void 0, function* () {
core.debug('Fetching base branch and using `git diff-index` to determine changed files');
const baseRef = pullRequest.base.ref;
yield git.fetchBranch(baseRef);
return yield git.getChangedFiles(pullRequest.base.sha);
yield git.fetchCommit(ref);
// FETCH_HEAD will always point to the just fetched commit
// No matter if ref is SHA, branch or tag name or full git ref
return yield git.getChangedFiles(git.FETCH_HEAD);
});
}
// Uses github REST api to get list of files changed in PR
@@ -4645,10 +4701,11 @@ class Filter {
dot: true
};
for (const name of Object.keys(doc)) {
const patterns = doc[name];
if (!Array.isArray(patterns)) {
const patternsNode = doc[name];
if (!Array.isArray(patternsNode)) {
this.throwInvalidFormatError();
}
const patterns = flat(patternsNode);
if (!patterns.every(x => typeof x === 'string')) {
this.throwInvalidFormatError();
}
@@ -4669,6 +4726,11 @@ class Filter {
}
}
exports.default = Filter;
// Creates a new array with all sub-array elements recursively concatenated
// In future could be replaced by Array.prototype.flat (supported on Node.js 11+)
function flat(arr) {
return arr.reduce((acc, val) => acc.concat(Array.isArray(val) ? flat(val) : val), []);
}
/***/ }),

325
package-lock.json generated
View File

@@ -1,5 +1,5 @@
{
"name": "pr-changed-files-filter",
"name": "paths-filter",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
@@ -3897,33 +3897,33 @@
}
},
"@typescript-eslint/experimental-utils": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-3.0.0.tgz",
"integrity": "sha512-BN0vmr9N79M9s2ctITtChRuP1+Dls0x/wlg0RXW1yQ7WJKPurg6X3Xirv61J2sjPif4F8SLsFMs5Nzte0WYoTQ==",
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-3.3.0.tgz",
"integrity": "sha512-d4pGIAbu/tYsrPrdHCQ5xfadJGvlkUxbeBB56nO/VGmEDi/sKmfa5fGty5t5veL1OyJBrUmSiRn1R1qfVDydrg==",
"dev": true,
"requires": {
"@types/json-schema": "^7.0.3",
"@typescript-eslint/typescript-estree": "3.0.0",
"@typescript-eslint/typescript-estree": "3.3.0",
"eslint-scope": "^5.0.0",
"eslint-utils": "^2.0.0"
}
},
"@typescript-eslint/parser": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-3.0.0.tgz",
"integrity": "sha512-8RRCA9KLxoFNO0mQlrLZA0reGPd/MsobxZS/yPFj+0/XgMdS8+mO8mF3BDj2ZYQj03rkayhSJtF1HAohQ3iylw==",
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-3.3.0.tgz",
"integrity": "sha512-a7S0Sqn/+RpOOWTcaLw6RD4obsharzxmgMfdK24l364VxuBODXjuJM7ImCkSXEN7oz52aiZbXSbc76+2EsE91w==",
"dev": true,
"requires": {
"@types/eslint-visitor-keys": "^1.0.0",
"@typescript-eslint/experimental-utils": "3.0.0",
"@typescript-eslint/typescript-estree": "3.0.0",
"@typescript-eslint/experimental-utils": "3.3.0",
"@typescript-eslint/typescript-estree": "3.3.0",
"eslint-visitor-keys": "^1.1.0"
}
},
"@typescript-eslint/typescript-estree": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-3.0.0.tgz",
"integrity": "sha512-nevQvHyNghsfLrrByzVIH4ZG3NROgJ8LZlfh3ddwPPH4CH7W4GAiSx5qu+xHuX5pWsq6q/eqMc1io840ZhAnUg==",
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-3.3.0.tgz",
"integrity": "sha512-3SqxylENltEvJsjjMSDCUx/edZNSC7wAqifUU1Ywp//0OWEZwMZJfecJud9XxJ/40rAKEbJMKBOQzeOjrLJFzQ==",
"dev": true,
"requires": {
"debug": "^4.1.1",
@@ -3956,9 +3956,9 @@
"dev": true
},
"acorn": {
"version": "6.4.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz",
"integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==",
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.3.1.tgz",
"integrity": "sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA==",
"dev": true
},
"acorn-globals": {
@@ -4003,6 +4003,12 @@
"uri-js": "^4.2.2"
}
},
"ansi-colors": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz",
"integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==",
"dev": true
},
"ansi-escapes": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz",
@@ -5419,6 +5425,15 @@
"once": "^1.4.0"
}
},
"enquirer": {
"version": "2.3.5",
"resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.5.tgz",
"integrity": "sha512-BNT1C08P9XD0vNg3J475yIUG+mVdp9T6towYFHUv897X0KoHBjB1shyrNmhmtHWKP17iSWgo7Gqh7BBuzLZMSA==",
"dev": true,
"requires": {
"ansi-colors": "^3.2.1"
}
},
"error-ex": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
@@ -5487,79 +5502,214 @@
}
},
"eslint": {
"version": "5.16.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-5.16.0.tgz",
"integrity": "sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg==",
"version": "7.3.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-7.3.0.tgz",
"integrity": "sha512-dJMVXwfU5PT1cj2Nv2VPPrKahKTGdX+5Dh0Q3YuKt+Y2UhdL2YbzsVaBMyG9HC0tBismlv/r1+eZqs6SMIV38Q==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.0.0",
"ajv": "^6.9.1",
"chalk": "^2.1.0",
"cross-spawn": "^6.0.5",
"ajv": "^6.10.0",
"chalk": "^4.0.0",
"cross-spawn": "^7.0.2",
"debug": "^4.0.1",
"doctrine": "^3.0.0",
"eslint-scope": "^4.0.3",
"eslint-utils": "^1.3.1",
"eslint-visitor-keys": "^1.0.0",
"espree": "^5.0.1",
"esquery": "^1.0.1",
"enquirer": "^2.3.5",
"eslint-scope": "^5.1.0",
"eslint-utils": "^2.0.0",
"eslint-visitor-keys": "^1.2.0",
"espree": "^7.1.0",
"esquery": "^1.2.0",
"esutils": "^2.0.2",
"file-entry-cache": "^5.0.1",
"functional-red-black-tree": "^1.0.1",
"glob": "^7.1.2",
"globals": "^11.7.0",
"glob-parent": "^5.0.0",
"globals": "^12.1.0",
"ignore": "^4.0.6",
"import-fresh": "^3.0.0",
"imurmurhash": "^0.1.4",
"inquirer": "^6.2.2",
"js-yaml": "^3.13.0",
"is-glob": "^4.0.0",
"js-yaml": "^3.13.1",
"json-stable-stringify-without-jsonify": "^1.0.1",
"levn": "^0.3.0",
"lodash": "^4.17.11",
"levn": "^0.4.1",
"lodash": "^4.17.14",
"minimatch": "^3.0.4",
"mkdirp": "^0.5.1",
"natural-compare": "^1.4.0",
"optionator": "^0.8.2",
"path-is-inside": "^1.0.2",
"optionator": "^0.9.1",
"progress": "^2.0.0",
"regexpp": "^2.0.1",
"semver": "^5.5.1",
"strip-ansi": "^4.0.0",
"strip-json-comments": "^2.0.1",
"regexpp": "^3.1.0",
"semver": "^7.2.1",
"strip-ansi": "^6.0.0",
"strip-json-comments": "^3.1.0",
"table": "^5.2.3",
"text-table": "^0.2.0"
"text-table": "^0.2.0",
"v8-compile-cache": "^2.0.3"
},
"dependencies": {
"eslint-scope": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz",
"integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==",
"ansi-styles": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
"integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
"dev": true,
"requires": {
"esrecurse": "^4.1.0",
"estraverse": "^4.1.1"
"@types/color-name": "^1.1.1",
"color-convert": "^2.0.1"
}
},
"eslint-utils": {
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz",
"integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==",
"chalk": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
"dev": true,
"requires": {
"eslint-visitor-keys": "^1.1.0"
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
"cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
"integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
"dev": true,
"requires": {
"path-key": "^3.1.0",
"shebang-command": "^2.0.0",
"which": "^2.0.1"
}
},
"eslint-visitor-keys": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
"integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
"dev": true
},
"globals": {
"version": "12.4.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz",
"integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==",
"dev": true,
"requires": {
"type-fest": "^0.8.1"
}
},
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true
},
"levn": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
"integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
"dev": true,
"requires": {
"prelude-ls": "^1.2.1",
"type-check": "~0.4.0"
}
},
"optionator": {
"version": "0.9.1",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
"integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==",
"dev": true,
"requires": {
"deep-is": "^0.1.3",
"fast-levenshtein": "^2.0.6",
"levn": "^0.4.1",
"prelude-ls": "^1.2.1",
"type-check": "^0.4.0",
"word-wrap": "^1.2.3"
}
},
"path-key": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
"dev": true
},
"prelude-ls": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
"integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
"dev": true
},
"regexpp": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz",
"integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==",
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz",
"integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==",
"dev": true
},
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
"version": "7.3.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz",
"integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==",
"dev": true
},
"shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
"dev": true,
"requires": {
"shebang-regex": "^3.0.0"
}
},
"shebang-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
"dev": true
},
"strip-ansi": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
"dev": true,
"requires": {
"ansi-regex": "^5.0.0"
}
},
"supports-color": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
"integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
"dev": true,
"requires": {
"has-flag": "^4.0.0"
}
},
"type-check": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
"integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
"dev": true,
"requires": {
"prelude-ls": "^1.2.1"
}
},
"which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
"dev": true,
"requires": {
"isexe": "^2.0.0"
}
}
}
},
@@ -5927,9 +6077,9 @@
"dev": true
},
"eslint-scope": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz",
"integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==",
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.0.tgz",
"integrity": "sha512-iiGRvtxWqgtx5m8EyQUJihBloE4EnYeGE/bz1wSPwJE6tZuJUtHlhqDM4Xj2ukE8Dyy1+HCZ4hE0fzIVMzb58w==",
"dev": true,
"requires": {
"esrecurse": "^4.1.0",
@@ -5937,9 +6087,9 @@
}
},
"eslint-utils": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.0.0.tgz",
"integrity": "sha512-0HCPuJv+7Wv1bACm8y5/ECVfYdfsAm9xmVb7saeFlxjPYALefjhbYoCkBjPdPzGH8wWyTpAez82Fh3VKYEZ8OA==",
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz",
"integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==",
"dev": true,
"requires": {
"eslint-visitor-keys": "^1.1.0"
@@ -5952,14 +6102,22 @@
"dev": true
},
"espree": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/espree/-/espree-5.0.1.tgz",
"integrity": "sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A==",
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/espree/-/espree-7.1.0.tgz",
"integrity": "sha512-dcorZSyfmm4WTuTnE5Y7MEN1DyoPYy1ZR783QW1FJoenn7RailyWFsq/UL6ZAAA7uXurN9FIpYyUs3OfiIW+Qw==",
"dev": true,
"requires": {
"acorn": "^6.0.7",
"acorn-jsx": "^5.0.0",
"eslint-visitor-keys": "^1.0.0"
"acorn": "^7.2.0",
"acorn-jsx": "^5.2.0",
"eslint-visitor-keys": "^1.2.0"
},
"dependencies": {
"eslint-visitor-keys": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
"integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
"dev": true
}
}
},
"esprima": {
@@ -6486,6 +6644,15 @@
"path-is-absolute": "^1.0.0"
}
},
"glob-parent": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz",
"integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==",
"dev": true,
"requires": {
"is-glob": "^4.0.1"
}
},
"globals": {
"version": "11.12.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
@@ -12781,12 +12948,6 @@
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
"dev": true
},
"path-is-inside": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
"integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=",
"dev": true
},
"path-key": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
@@ -13862,9 +14023,9 @@
"dev": true
},
"strip-json-comments": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.0.tgz",
"integrity": "sha512-e6/d0eBu7gHtdCqFt0xJr642LdToM5/cN4Qb9DbHjVx1CP5RyeM+zH7pbecEmDv/lBqb0QH+6Uqq75rxFPkM0w==",
"dev": true
},
"supports-color": {
@@ -14317,6 +14478,12 @@
"dev": true,
"optional": true
},
"v8-compile-cache": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz",
"integrity": "sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ==",
"dev": true
},
"v8-to-istanbul": {
"version": "4.1.4",
"resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-4.1.4.tgz",

View File

@@ -1,8 +1,8 @@
{
"name": "pr-changed-files-filter",
"name": "paths-filter",
"version": "1.0.0",
"private": true,
"description": "Enables conditional execution of workflow job steps considering which files are modified by a pull request.",
"description": "Execute your workflow steps only if relevant files are modified.",
"main": "lib/main.js",
"scripts": {
"build": "tsc",
@@ -36,9 +36,9 @@
"@types/js-yaml": "^3.12.4",
"@types/minimatch": "^3.0.3",
"@types/node": "^14.0.5",
"@typescript-eslint/parser": "^3.0.0",
"@typescript-eslint/parser": "^3.3.0",
"@zeit/ncc": "^0.22.3",
"eslint": "^5.16.0",
"eslint": "^7.3.0",
"eslint-plugin-github": "^2.0.0",
"eslint-plugin-jest": "^22.21.0",
"jest": "^26.0.1",

View File

@@ -15,10 +15,11 @@ export default class Filter {
}
for (const name of Object.keys(doc)) {
const patterns = doc[name] as string[]
if (!Array.isArray(patterns)) {
const patternsNode = doc[name]
if (!Array.isArray(patternsNode)) {
this.throwInvalidFormatError()
}
const patterns = flat(patternsNode) as string[]
if (!patterns.every(x => typeof x === 'string')) {
this.throwInvalidFormatError()
}
@@ -40,3 +41,9 @@ export default class Filter {
throw new Error('Invalid filter YAML format: Expected dictionary of string arrays')
}
}
// Creates a new array with all sub-array elements recursively concatenated
// In future could be replaced by Array.prototype.flat (supported on Node.js 11+)
function flat(arr: any[]): any[] {
return arr.reduce((acc, val) => acc.concat(Array.isArray(val) ? flat(val) : val), [])
}

View File

@@ -1,22 +1,25 @@
import {exec} from '@actions/exec'
export async function fetchBranch(base: string): Promise<void> {
const exitCode = await exec('git', ['fetch', '--depth=1', 'origin', base])
export const NULL_SHA = '0000000000000000000000000000000000000000'
export const FETCH_HEAD = 'FETCH_HEAD'
export async function fetchCommit(ref: string): Promise<void> {
const exitCode = await exec('git', ['fetch', '--depth=1', '--no-tags', 'origin', ref])
if (exitCode !== 0) {
throw new Error(`Fetching branch ${base} failed, exiting`)
throw new Error(`Fetching ${ref} failed`)
}
}
export async function getChangedFiles(base: string): Promise<string[]> {
export async function getChangedFiles(ref: string): Promise<string[]> {
let output = ''
const exitCode = await exec('git', ['diff-index', '--name-only', base], {
const exitCode = await exec('git', ['diff-index', '--name-only', ref], {
listeners: {
stdout: (data: Buffer) => (output += data.toString())
}
})
if (exitCode !== 0) {
throw new Error(`Couldn't determine changed files, exiting`)
throw new Error(`Couldn't determine changed files`)
}
return output
@@ -24,3 +27,20 @@ export async function getChangedFiles(base: string): Promise<string[]> {
.map(s => s.trim())
.filter(s => s.length > 0)
}
export function isTagRef(ref: string): boolean {
return ref.startsWith('refs/tags/')
}
export function trimRefs(ref: string): string {
return trimStart(ref, 'refs/')
}
export function trimRefsHeads(ref: string): string {
const trimRef = trimStart(ref, 'refs/')
return trimStart(trimRef, 'heads/')
}
function trimStart(ref: string, start: string): string {
return ref.startsWith(start) ? ref.substr(start.length) : ref
}

View File

@@ -12,18 +12,20 @@ async function run(): Promise<void> {
const filtersInput = core.getInput('filters', {required: true})
const filtersYaml = isPathInput(filtersInput) ? getConfigFileContent(filtersInput) : filtersInput
if (github.context.eventName !== 'pull_request') {
core.setFailed('This action can be triggered only by pull_request event')
return
}
const pr = github.context.payload.pull_request as Webhooks.WebhookPayloadPullRequestPullRequest
const filter = new Filter(filtersYaml)
const files = token ? await getChangedFilesFromApi(token, pr) : await getChangedFilesFromGit(pr)
const files = await getChangedFiles(token)
const result = filter.match(files)
for (const key in result) {
core.setOutput(key, String(result[key]))
if (files === null) {
// Change detection was not possible
// Set all filter keys to true (i.e. changed)
for (const key in filter.rules) {
core.setOutput(key, String(true))
}
} else {
const result = filter.match(files)
for (const key in result) {
core.setOutput(key, String(result[key]))
}
}
} catch (error) {
core.setFailed(error.message)
@@ -46,12 +48,45 @@ function getConfigFileContent(configPath: string): string {
return fs.readFileSync(configPath, {encoding: 'utf8'})
}
async function getChangedFiles(token: string): Promise<string[] | null> {
if (github.context.eventName === 'pull_request') {
const pr = github.context.payload.pull_request as Webhooks.WebhookPayloadPullRequestPullRequest
return token ? await getChangedFilesFromApi(token, pr) : await getChangedFilesFromGit(pr.base.sha)
} else if (github.context.eventName === 'push') {
return getChangedFilesFromPush()
} else {
throw new Error('This action can be triggered only by pull_request or push event')
}
}
async function getChangedFilesFromPush(): Promise<string[] | null> {
const push = github.context.payload as Webhooks.WebhookPayloadPush
// No change detection for pushed tags
if (git.isTagRef(push.ref)) return null
// Get base from input or use repo default branch.
// It it starts with 'refs/', it will be trimmed (git fetch refs/heads/<NAME> doesn't work)
const baseInput = git.trimRefs(core.getInput('base', {required: false}) || push.repository.default_branch)
// If base references same branch it was pushed to, we will do comparison against the previously pushed commit.
// Otherwise changes are detected against the base reference
const base = git.trimRefsHeads(baseInput) === git.trimRefsHeads(push.ref) ? push.before : baseInput
// There is no previous commit for comparison
// e.g. change detection against previous commit of just pushed new branch
if (base === git.NULL_SHA) return null
return await getChangedFilesFromGit(base)
}
// Fetch base branch and use `git diff` to determine changed files
async function getChangedFilesFromGit(pullRequest: Webhooks.WebhookPayloadPullRequestPullRequest): Promise<string[]> {
async function getChangedFilesFromGit(ref: string): Promise<string[]> {
core.debug('Fetching base branch and using `git diff-index` to determine changed files')
const baseRef = pullRequest.base.ref
await git.fetchBranch(baseRef)
return await git.getChangedFiles(pullRequest.base.sha)
await git.fetchCommit(ref)
// FETCH_HEAD will always point to the just fetched commit
// No matter if ref is SHA, branch or tag name or full git ref
return await git.getChangedFiles(git.FETCH_HEAD)
}
// Uses github REST api to get list of files changed in PR