From 75cbfb4be9b7729029618ca576138535c9d7ff72 Mon Sep 17 00:00:00 2001 From: Michal Dorner Date: Fri, 16 Oct 2020 12:28:12 +0200 Subject: [PATCH] Support workflows triggered by any event (#44) * Allow change detection on all event types * mention commit SHA in docs for base parameter * improve logging * update CHANGELOG.md --- CHANGELOG.md | 3 +++ README.md | 31 +++++++++++++++++++------------ __tests__/git.test.ts | 6 ++++++ dist/index.js | 38 +++++++++++++++++++++----------------- src/git.ts | 4 ++++ src/main.ts | 32 ++++++++++++++++++-------------- 6 files changed, 71 insertions(+), 43 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a0db14a..769bf11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog +## v2.5.0 +- [Support workflows triggered by any event](https://github.com/dorny/paths-filter/pull/44) + ## v2.4.2 - [Fixed compatibility with older (<2.23) versions of git](https://github.com/dorny/paths-filter/pull/42) diff --git a/README.md b/README.md index fe21d3f..d074bf7 100644 --- a/README.md +++ b/README.md @@ -9,18 +9,27 @@ doesn't allow this because they doesn't work on a level of individual jobs or st ## Supported workflows: -- Pull requests: - - Action triggered by **[pull_request](https://docs.github.com/en/actions/reference/events-that-trigger-workflows#pull_request)** +- **Pull requests:** + - Workflow triggered by **[pull_request](https://docs.github.com/en/actions/reference/events-that-trigger-workflows#pull_request)** or **[pull_request_target](https://docs.github.com/en/actions/reference/events-that-trigger-workflows#pull_request_target)** event - Changes are detected against the pull request base branch - Uses Github REST API to fetch list of modified files -- Feature branches: - - Action triggered by **[push](https://docs.github.com/en/actions/reference/events-that-trigger-workflows#push)** event - - Changes are detected against the merge-base with configured base branch +- **Feature branches:** + - Workflow triggered by **[push](https://docs.github.com/en/actions/reference/events-that-trigger-workflows#push)** + or any other **[event](https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows)** + - The `base` input parameter must not be the same as the branch that triggered the workflow + - Changes are detected against the merge-base with configured base branch or default branch - Uses git commands to detect changes - repository must be already [checked out](https://github.com/actions/checkout) -- Master, Release or other long-lived branches: - - Action triggered by **[push](https://docs.github.com/en/actions/reference/events-that-trigger-workflows#push)** event - - Changes are detected against the most recent commit on the same branch before the push +- **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 same as the branch that triggered the workflow: + - Changes are detected against the most recent commit on the same branch before the push + - Workflow triggered by any other **[event](https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows)** + when `base` input parameter is commit SHA: + - Changes are detected against the provided `base` commit + - Workflow triggered by any other **[event](https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows)** + when `base` input parameter is same as the branch that triggered the workflow: + - Changes are detected from last commit - Uses git commands to detect changes - repository must be already [checked out](https://github.com/actions/checkout) ## Example @@ -49,6 +58,7 @@ For more scenarios see [examples](#examples) section. # What's New +- Support workflows triggered by any event - Fixed compatibility with older (<2.23) versions of git - Support for tag pushes and tags as a base reference - Fixes for various edge cases when event payload is incomplete @@ -57,9 +67,6 @@ For more scenarios see [examples](#examples) section. - Detects only changes introduced by feature branch. Later modifications on base branch are ignored. - Filter by type of file change: - Optionally consider if file was added, modified or deleted -- Custom processing of changed files: - - Optionally export paths of all files matching the filter - - Output can be space-delimited or in JSON format For more information see [CHANGELOG](https://github.com/actions/checkout/blob/main/CHANGELOG.md) @@ -85,7 +92,7 @@ For more information see [CHANGELOG](https://github.com/actions/checkout/blob/ma # Filters syntax is documented by example - see examples section. filters: '' - # Branch or tag against which the changes will be detected. + # Branch, tag or commit SHA against which the changes will be detected. # If it references same branch it was pushed to, # changes are detected against the most recent commit before the push. # Otherwise it uses git merge-base to find best common ancestor between diff --git a/__tests__/git.test.ts b/__tests__/git.test.ts index 5f36ec1..9645221 100644 --- a/__tests__/git.test.ts +++ b/__tests__/git.test.ts @@ -26,4 +26,10 @@ describe('git utility function tests (those not invoking git)', () => { expect(git.getShortName('tags/v1')).toBe('tags/v1') expect(git.getShortName('v1')).toBe('v1') }) + + test('isGitSha(ref) returns true only for 40 characters of a-z and 0-9', () => { + expect(git.isGitSha('8b399ed1681b9efd6b1e048ca1c5cba47edf3855')).toBeTruthy() + expect(git.isGitSha('This_is_very_long_name_for_a_branch_1111')).toBeFalsy() + expect(git.isGitSha('master')).toBeFalsy() + }) }) diff --git a/dist/index.js b/dist/index.js index 390807a..dc22a60 100644 --- a/dist/index.js +++ b/dist/index.js @@ -3811,7 +3811,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); -exports.getShortName = exports.getCurrentRef = exports.listAllFilesAsAdded = exports.parseGitDiffOutput = exports.getChangesSinceMergeBase = exports.getChanges = exports.getChangesInLastCommit = exports.NULL_SHA = void 0; +exports.isGitSha = exports.getShortName = exports.getCurrentRef = exports.listAllFilesAsAdded = exports.parseGitDiffOutput = exports.getChangesSinceMergeBase = exports.getChanges = exports.getChangesInLastCommit = exports.NULL_SHA = void 0; const exec_1 = __importDefault(__webpack_require__(807)); const core = __importStar(__webpack_require__(470)); const file_1 = __webpack_require__(258); @@ -3960,6 +3960,10 @@ function getShortName(ref) { return ref; } exports.getShortName = getShortName; +function isGitSha(ref) { + return /^[a-z0-9]{40}$/.test(ref); +} +exports.isGitSha = isGitSha; async function hasCommit(ref) { core.startGroup(`Checking if commit for ${ref} is locally available`); try { @@ -4679,34 +4683,34 @@ async function getChangedFiles(token, base, initialFetchDepth) { core.info('Github token is not available - changes will be detected from PRs merge commit'); return await git.getChangesInLastCommit(); } - else if (github.context.eventName === 'push') { - return getChangedFilesFromPush(base, initialFetchDepth); - } else { - throw new Error('This action can be triggered only by pull_request, pull_request_target or push event'); + return getChangedFilesFromGit(base, initialFetchDepth); } } -async function getChangedFilesFromPush(base, initialFetchDepth) { +async function getChangedFilesFromGit(base, initialFetchDepth) { var _a; - const push = github.context.payload; - const defaultRef = (_a = push.repository) === null || _a === void 0 ? void 0 : _a.default_branch; - const pushRef = git.getShortName(push.ref) || + const defaultRef = (_a = github.context.payload.repository) === null || _a === void 0 ? void 0 : _a.default_branch; + const beforeSha = github.context.eventName === 'push' ? github.context.payload.before : null; + const pushRef = git.getShortName(github.context.ref) || (core.warning(`'ref' field is missing in PUSH event payload - using current branch, tag or commit SHA`), await git.getCurrentRef()); const baseRef = git.getShortName(base) || defaultRef; if (!baseRef) { throw new Error("This action requires 'base' input to be configured or 'repository.default_branch' to be set in the event payload"); } - // If base references same branch it was pushed to, - // we will do comparison against the previously pushed commit - if (baseRef === pushRef) { - if (!push.before) { + const isBaseRefSha = git.isGitSha(baseRef); + const isBaseSameAsPush = baseRef === pushRef; + // If base is commit SHA will do comparison against the referenced commit + // Or If base references same branch it was pushed to, we will do comparison against the previously pushed commit + if (isBaseRefSha || isBaseSameAsPush) { + if (!isBaseRefSha && !beforeSha) { core.warning(`'before' field is missing in PUSH event payload - changes will be detected from last commit`); return await git.getChangesInLastCommit(); } + const baseSha = isBaseRefSha ? baseRef : beforeSha; // If there is no previously pushed commit, // we will do comparison against the default branch or return all as added - if (push.before === git.NULL_SHA) { + if (baseSha === git.NULL_SHA) { if (defaultRef && baseRef !== defaultRef) { core.info(`First push of a branch detected - changes will be detected against the default branch ${defaultRef}`); return await git.getChangesSinceMergeBase(defaultRef, initialFetchDepth); @@ -4716,8 +4720,8 @@ async function getChangedFilesFromPush(base, initialFetchDepth) { return await git.listAllFilesAsAdded(); } } - core.info(`Changes will be detected against the last previously pushed commit on same branch (${pushRef})`); - return await git.getChanges(push.before); + core.info(`Changes will be detected against commit (${baseSha})`); + return await git.getChanges(baseSha); } // Changes introduced by current branch against the base branch core.info(`Changes will be detected against the branch ${baseRef}`); @@ -4813,7 +4817,7 @@ module.exports = require("https"); /***/ 215: /***/ (function(module) { -module.exports = {"_args":[["@octokit/rest@16.43.1","C:\\Users\\dha\\Workspace\\dorny\\paths-filter"]],"_from":"@octokit/rest@16.43.1","_id":"@octokit/rest@16.43.1","_inBundle":false,"_integrity":"sha512-gfFKwRT/wFxq5qlNjnW2dh+qh74XgTQ2B179UX5K1HYCluioWj8Ndbgqw2PVqa1NnVJkGHp2ovMpVn/DImlmkw==","_location":"/@octokit/rest","_phantomChildren":{"@types/node":"14.0.5","deprecation":"2.3.1","once":"1.4.0","os-name":"3.1.0"},"_requested":{"type":"version","registry":true,"raw":"@octokit/rest@16.43.1","name":"@octokit/rest","escapedName":"@octokit%2frest","scope":"@octokit","rawSpec":"16.43.1","saveSpec":null,"fetchSpec":"16.43.1"},"_requiredBy":["/@actions/github"],"_resolved":"https://registry.npmjs.org/@octokit/rest/-/rest-16.43.1.tgz","_spec":"16.43.1","_where":"C:\\Users\\dha\\Workspace\\dorny\\paths-filter","author":{"name":"Gregor Martynus","url":"https://github.com/gr2m"},"bugs":{"url":"https://github.com/octokit/rest.js/issues"},"bundlesize":[{"path":"./dist/octokit-rest.min.js.gz","maxSize":"33 kB"}],"contributors":[{"name":"Mike de Boer","email":"info@mikedeboer.nl"},{"name":"Fabian Jakobs","email":"fabian@c9.io"},{"name":"Joe Gallo","email":"joe@brassafrax.com"},{"name":"Gregor Martynus","url":"https://github.com/gr2m"}],"dependencies":{"@octokit/auth-token":"^2.4.0","@octokit/plugin-paginate-rest":"^1.1.1","@octokit/plugin-request-log":"^1.0.0","@octokit/plugin-rest-endpoint-methods":"2.4.0","@octokit/request":"^5.2.0","@octokit/request-error":"^1.0.2","atob-lite":"^2.0.0","before-after-hook":"^2.0.0","btoa-lite":"^1.0.0","deprecation":"^2.0.0","lodash.get":"^4.4.2","lodash.set":"^4.3.2","lodash.uniq":"^4.5.0","octokit-pagination-methods":"^1.1.0","once":"^1.4.0","universal-user-agent":"^4.0.0"},"description":"GitHub REST API client for Node.js","devDependencies":{"@gimenete/type-writer":"^0.1.3","@octokit/auth":"^1.1.1","@octokit/fixtures-server":"^5.0.6","@octokit/graphql":"^4.2.0","@types/node":"^13.1.0","bundlesize":"^0.18.0","chai":"^4.1.2","compression-webpack-plugin":"^3.1.0","cypress":"^3.0.0","glob":"^7.1.2","http-proxy-agent":"^4.0.0","lodash.camelcase":"^4.3.0","lodash.merge":"^4.6.1","lodash.upperfirst":"^4.3.1","lolex":"^5.1.2","mkdirp":"^1.0.0","mocha":"^7.0.1","mustache":"^4.0.0","nock":"^11.3.3","npm-run-all":"^4.1.2","nyc":"^15.0.0","prettier":"^1.14.2","proxy":"^1.0.0","semantic-release":"^17.0.0","sinon":"^8.0.0","sinon-chai":"^3.0.0","sort-keys":"^4.0.0","string-to-arraybuffer":"^1.0.0","string-to-jsdoc-comment":"^1.0.0","typescript":"^3.3.1","webpack":"^4.0.0","webpack-bundle-analyzer":"^3.0.0","webpack-cli":"^3.0.0"},"files":["index.js","index.d.ts","lib","plugins"],"homepage":"https://github.com/octokit/rest.js#readme","keywords":["octokit","github","rest","api-client"],"license":"MIT","name":"@octokit/rest","nyc":{"ignore":["test"]},"publishConfig":{"access":"public"},"release":{"publish":["@semantic-release/npm",{"path":"@semantic-release/github","assets":["dist/*","!dist/*.map.gz"]}]},"repository":{"type":"git","url":"git+https://github.com/octokit/rest.js.git"},"scripts":{"build":"npm-run-all build:*","build:browser":"npm-run-all build:browser:*","build:browser:development":"webpack --mode development --entry . --output-library=Octokit --output=./dist/octokit-rest.js --profile --json > dist/bundle-stats.json","build:browser:production":"webpack --mode production --entry . --plugin=compression-webpack-plugin --output-library=Octokit --output-path=./dist --output-filename=octokit-rest.min.js --devtool source-map","build:ts":"npm run -s update-endpoints:typescript","coverage":"nyc report --reporter=html && open coverage/index.html","generate-bundle-report":"webpack-bundle-analyzer dist/bundle-stats.json --mode=static --no-open --report dist/bundle-report.html","lint":"prettier --check '{lib,plugins,scripts,test}/**/*.{js,json,ts}' 'docs/*.{js,json}' 'docs/src/**/*' index.js README.md package.json","lint:fix":"prettier --write '{lib,plugins,scripts,test}/**/*.{js,json,ts}' 'docs/*.{js,json}' 'docs/src/**/*' index.js README.md package.json","postvalidate:ts":"tsc --noEmit --target es6 test/typescript-validate.ts","prebuild:browser":"mkdirp dist/","pretest":"npm run -s lint","prevalidate:ts":"npm run -s build:ts","start-fixtures-server":"octokit-fixtures-server","test":"nyc mocha test/mocha-node-setup.js \"test/*/**/*-test.js\"","test:browser":"cypress run --browser chrome","update-endpoints":"npm-run-all update-endpoints:*","update-endpoints:fetch-json":"node scripts/update-endpoints/fetch-json","update-endpoints:typescript":"node scripts/update-endpoints/typescript","validate:ts":"tsc --target es6 --noImplicitAny index.d.ts"},"types":"index.d.ts","version":"16.43.1"}; +module.exports = {"_args":[["@octokit/rest@16.43.1","C:\\Users\\Michal\\Workspace\\dorny\\pr-changed-files-filter"]],"_from":"@octokit/rest@16.43.1","_id":"@octokit/rest@16.43.1","_inBundle":false,"_integrity":"sha512-gfFKwRT/wFxq5qlNjnW2dh+qh74XgTQ2B179UX5K1HYCluioWj8Ndbgqw2PVqa1NnVJkGHp2ovMpVn/DImlmkw==","_location":"/@octokit/rest","_phantomChildren":{"@types/node":"14.0.5","deprecation":"2.3.1","once":"1.4.0","os-name":"3.1.0"},"_requested":{"type":"version","registry":true,"raw":"@octokit/rest@16.43.1","name":"@octokit/rest","escapedName":"@octokit%2frest","scope":"@octokit","rawSpec":"16.43.1","saveSpec":null,"fetchSpec":"16.43.1"},"_requiredBy":["/@actions/github"],"_resolved":"https://registry.npmjs.org/@octokit/rest/-/rest-16.43.1.tgz","_spec":"16.43.1","_where":"C:\\Users\\Michal\\Workspace\\dorny\\pr-changed-files-filter","author":{"name":"Gregor Martynus","url":"https://github.com/gr2m"},"bugs":{"url":"https://github.com/octokit/rest.js/issues"},"bundlesize":[{"path":"./dist/octokit-rest.min.js.gz","maxSize":"33 kB"}],"contributors":[{"name":"Mike de Boer","email":"info@mikedeboer.nl"},{"name":"Fabian Jakobs","email":"fabian@c9.io"},{"name":"Joe Gallo","email":"joe@brassafrax.com"},{"name":"Gregor Martynus","url":"https://github.com/gr2m"}],"dependencies":{"@octokit/auth-token":"^2.4.0","@octokit/plugin-paginate-rest":"^1.1.1","@octokit/plugin-request-log":"^1.0.0","@octokit/plugin-rest-endpoint-methods":"2.4.0","@octokit/request":"^5.2.0","@octokit/request-error":"^1.0.2","atob-lite":"^2.0.0","before-after-hook":"^2.0.0","btoa-lite":"^1.0.0","deprecation":"^2.0.0","lodash.get":"^4.4.2","lodash.set":"^4.3.2","lodash.uniq":"^4.5.0","octokit-pagination-methods":"^1.1.0","once":"^1.4.0","universal-user-agent":"^4.0.0"},"description":"GitHub REST API client for Node.js","devDependencies":{"@gimenete/type-writer":"^0.1.3","@octokit/auth":"^1.1.1","@octokit/fixtures-server":"^5.0.6","@octokit/graphql":"^4.2.0","@types/node":"^13.1.0","bundlesize":"^0.18.0","chai":"^4.1.2","compression-webpack-plugin":"^3.1.0","cypress":"^3.0.0","glob":"^7.1.2","http-proxy-agent":"^4.0.0","lodash.camelcase":"^4.3.0","lodash.merge":"^4.6.1","lodash.upperfirst":"^4.3.1","lolex":"^5.1.2","mkdirp":"^1.0.0","mocha":"^7.0.1","mustache":"^4.0.0","nock":"^11.3.3","npm-run-all":"^4.1.2","nyc":"^15.0.0","prettier":"^1.14.2","proxy":"^1.0.0","semantic-release":"^17.0.0","sinon":"^8.0.0","sinon-chai":"^3.0.0","sort-keys":"^4.0.0","string-to-arraybuffer":"^1.0.0","string-to-jsdoc-comment":"^1.0.0","typescript":"^3.3.1","webpack":"^4.0.0","webpack-bundle-analyzer":"^3.0.0","webpack-cli":"^3.0.0"},"files":["index.js","index.d.ts","lib","plugins"],"homepage":"https://github.com/octokit/rest.js#readme","keywords":["octokit","github","rest","api-client"],"license":"MIT","name":"@octokit/rest","nyc":{"ignore":["test"]},"publishConfig":{"access":"public"},"release":{"publish":["@semantic-release/npm",{"path":"@semantic-release/github","assets":["dist/*","!dist/*.map.gz"]}]},"repository":{"type":"git","url":"git+https://github.com/octokit/rest.js.git"},"scripts":{"build":"npm-run-all build:*","build:browser":"npm-run-all build:browser:*","build:browser:development":"webpack --mode development --entry . --output-library=Octokit --output=./dist/octokit-rest.js --profile --json > dist/bundle-stats.json","build:browser:production":"webpack --mode production --entry . --plugin=compression-webpack-plugin --output-library=Octokit --output-path=./dist --output-filename=octokit-rest.min.js --devtool source-map","build:ts":"npm run -s update-endpoints:typescript","coverage":"nyc report --reporter=html && open coverage/index.html","generate-bundle-report":"webpack-bundle-analyzer dist/bundle-stats.json --mode=static --no-open --report dist/bundle-report.html","lint":"prettier --check '{lib,plugins,scripts,test}/**/*.{js,json,ts}' 'docs/*.{js,json}' 'docs/src/**/*' index.js README.md package.json","lint:fix":"prettier --write '{lib,plugins,scripts,test}/**/*.{js,json,ts}' 'docs/*.{js,json}' 'docs/src/**/*' index.js README.md package.json","postvalidate:ts":"tsc --noEmit --target es6 test/typescript-validate.ts","prebuild:browser":"mkdirp dist/","pretest":"npm run -s lint","prevalidate:ts":"npm run -s build:ts","start-fixtures-server":"octokit-fixtures-server","test":"nyc mocha test/mocha-node-setup.js \"test/*/**/*-test.js\"","test:browser":"cypress run --browser chrome","update-endpoints":"npm-run-all update-endpoints:*","update-endpoints:fetch-json":"node scripts/update-endpoints/fetch-json","update-endpoints:typescript":"node scripts/update-endpoints/typescript","validate:ts":"tsc --target es6 --noImplicitAny index.d.ts"},"types":"index.d.ts","version":"16.43.1"}; /***/ }), diff --git a/src/git.ts b/src/git.ts index ac4debc..6a0ac91 100644 --- a/src/git.ts +++ b/src/git.ts @@ -153,6 +153,10 @@ export function getShortName(ref: string): string { return ref } +export function isGitSha(ref: string): boolean { + return /^[a-z0-9]{40}$/.test(ref) +} + async function hasCommit(ref: string): Promise { core.startGroup(`Checking if commit for ${ref} is locally available`) try { diff --git a/src/main.ts b/src/main.ts index 12408cd..92cad59 100644 --- a/src/main.ts +++ b/src/main.ts @@ -62,19 +62,19 @@ async function getChangedFiles(token: string, base: string, initialFetchDepth: n } core.info('Github token is not available - changes will be detected from PRs merge commit') return await git.getChangesInLastCommit() - } else if (github.context.eventName === 'push') { - return getChangedFilesFromPush(base, initialFetchDepth) } else { - throw new Error('This action can be triggered only by pull_request, pull_request_target or push event') + return getChangedFilesFromGit(base, initialFetchDepth) } } -async function getChangedFilesFromPush(base: string, initialFetchDepth: number): Promise { - const push = github.context.payload as Webhooks.WebhookPayloadPush - const defaultRef = push.repository?.default_branch +async function getChangedFilesFromGit(base: string, initialFetchDepth: number): Promise { + const defaultRef = github.context.payload.repository?.default_branch + + const beforeSha = + github.context.eventName === 'push' ? (github.context.payload as Webhooks.WebhookPayloadPush).before : null const pushRef = - git.getShortName(push.ref) || + git.getShortName(github.context.ref) || (core.warning(`'ref' field is missing in PUSH event payload - using current branch, tag or commit SHA`), await git.getCurrentRef()) @@ -85,17 +85,21 @@ async function getChangedFilesFromPush(base: string, initialFetchDepth: number): ) } - // If base references same branch it was pushed to, - // we will do comparison against the previously pushed commit - if (baseRef === pushRef) { - if (!push.before) { + const isBaseRefSha = git.isGitSha(baseRef) + const isBaseSameAsPush = baseRef === pushRef + + // If base is commit SHA will do comparison against the referenced commit + // Or If base references same branch it was pushed to, we will do comparison against the previously pushed commit + if (isBaseRefSha || isBaseSameAsPush) { + if (!isBaseRefSha && !beforeSha) { core.warning(`'before' field is missing in PUSH event payload - changes will be detected from last commit`) return await git.getChangesInLastCommit() } + const baseSha = isBaseRefSha ? baseRef : beforeSha // If there is no previously pushed commit, // we will do comparison against the default branch or return all as added - if (push.before === git.NULL_SHA) { + if (baseSha === git.NULL_SHA) { if (defaultRef && baseRef !== defaultRef) { core.info(`First push of a branch detected - changes will be detected against the default branch ${defaultRef}`) return await git.getChangesSinceMergeBase(defaultRef, initialFetchDepth) @@ -105,8 +109,8 @@ async function getChangedFilesFromPush(base: string, initialFetchDepth: number): } } - core.info(`Changes will be detected against the last previously pushed commit on same branch (${pushRef})`) - return await git.getChanges(push.before) + core.info(`Changes will be detected against commit (${baseSha})`) + return await git.getChanges(baseSha) } // Changes introduced by current branch against the base branch