diff --git a/.github/workflows/pull-request-verification.yml b/.github/workflows/pull-request-verification.yml index 71fd091..66e001c 100644 --- a/.github/workflows/pull-request-verification.yml +++ b/.github/workflows/pull-request-verification.yml @@ -71,6 +71,23 @@ jobs: if: steps.filter.outputs.any != 'true' || steps.filter.outputs.error == 'true' run: exit 1 + test-local-changes: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - run: echo "NEW FILE" > local + - run: git add local + - uses: ./ + id: filter + with: + base: HEAD + filters: | + local: + - local + - name: filter-test + if: steps.filter.outputs.local != 'true' + run: exit 1 + test-change-type: runs-on: ubuntu-latest steps: diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f6e799..e32fba0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog +## v2.6.0 +- [Support local changes](https://github.com/dorny/paths-filter/pull/53) + ## v2.5.3 - [Fixed mapping of removed/deleted change status from github API](https://github.com/dorny/paths-filter/pull/51) - [Fixed retrieval of all changes via Github API when there are 100+ changes](https://github.com/dorny/paths-filter/pull/50) diff --git a/README.md b/README.md index 554d327..893fc6b 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,10 @@ doesn't allow this because they doesn't work on a level of individual jobs or st 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) +- **Local changes** + - Workflow triggered by any event when `base` input parameter is set to `HEAD` + - Changes are detected against current HEAD + - Untracked files are ignored ## Example ```yaml @@ -62,6 +66,7 @@ For more scenarios see [examples](#examples) section. # What's New +- Support local changes - Fixed retrieval of all changes via Github API when there are 100+ changes - Paths expressions are now evaluated using [picomatch](https://github.com/micromatch/picomatch) library - Support workflows triggered by any event @@ -304,6 +309,35 @@ jobs: ``` +
+ Local changes: Detect staged and unstaged local changes + +```yaml +on: + push: + branches: # Push to following branches will trigger the workflow + - master + - develop + - release/** +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + # Some action which 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@v2 + id: filter + with: + base: HEAD + filters: ... # Configure your filters +``` +
+ ## Advanced options
diff --git a/dist/index.js b/dist/index.js index 76ddc47..ea54600 100644 --- a/dist/index.js +++ b/dist/index.js @@ -3811,11 +3811,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); -exports.isGitSha = 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.getChangesOnHead = exports.getChanges = exports.getChangesInLastCommit = exports.HEAD = exports.NULL_SHA = void 0; const exec_1 = __importDefault(__webpack_require__(807)); const core = __importStar(__webpack_require__(470)); const file_1 = __webpack_require__(258); exports.NULL_SHA = '0000000000000000000000000000000000000000'; +exports.HEAD = 'HEAD'; async function getChangesInLastCommit() { core.startGroup(`Change detection in last commit`); let output = ''; @@ -3850,6 +3851,20 @@ async function getChanges(ref) { return parseGitDiffOutput(output); } exports.getChanges = getChanges; +async function getChangesOnHead() { + // Get current changes - both staged and unstaged + core.startGroup(`Change detection on HEAD`); + let output = ''; + try { + output = (await exec_1.default('git', ['diff', '--no-renames', '--name-status', '-z', 'HEAD'])).stdout; + } + finally { + fixStdOutNullTermination(); + core.endGroup(); + } + return parseGitDiffOutput(output); +} +exports.getChangesOnHead = getChangesOnHead; async function getChangesSinceMergeBase(ref, initialFetchDepth) { if (!(await hasCommit(ref))) { // Fetch and add base branch @@ -4675,6 +4690,11 @@ function getConfigFileContent(configPath) { return fs.readFileSync(configPath, { encoding: 'utf8' }); } async function getChangedFiles(token, base, initialFetchDepth) { + // if base is 'HEAD' only local uncommitted changes will be detected + // This is the simplest case as we don't need to fetch more commits or evaluate current/before refs + if (base === git.HEAD) { + return await git.getChangesOnHead(); + } if (github.context.eventName === 'pull_request' || github.context.eventName === 'pull_request_target') { const pr = github.context.payload.pull_request; if (token) { @@ -4692,7 +4712,7 @@ async function getChangedFilesFromGit(base, initialFetchDepth) { 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`), + (core.warning(`'ref' field is missing in event payload - using current branch, tag or commit SHA`), await git.getCurrentRef()); const baseRef = git.getShortName(base) || defaultRef; if (!baseRef) { @@ -4700,11 +4720,11 @@ async function getChangedFilesFromGit(base, initialFetchDepth) { } 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 base is commit SHA we 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`); + core.warning(`'before' field is missing in event payload - changes will be detected from last commit`); return await git.getChangesInLastCommit(); } const baseSha = isBaseRefSha ? baseRef : beforeSha; diff --git a/src/git.ts b/src/git.ts index 6a0ac91..f34a5cc 100644 --- a/src/git.ts +++ b/src/git.ts @@ -3,6 +3,7 @@ import * as core from '@actions/core' import {File, ChangeStatus} from './file' export const NULL_SHA = '0000000000000000000000000000000000000000' +export const HEAD = 'HEAD' export async function getChangesInLastCommit(): Promise { core.startGroup(`Change detection in last commit`) @@ -39,6 +40,20 @@ export async function getChanges(ref: string): Promise { return parseGitDiffOutput(output) } +export async function getChangesOnHead(): Promise { + // Get current changes - both staged and unstaged + core.startGroup(`Change detection on HEAD`) + let output = '' + try { + output = (await exec('git', ['diff', '--no-renames', '--name-status', '-z', 'HEAD'])).stdout + } finally { + fixStdOutNullTermination() + core.endGroup() + } + + return parseGitDiffOutput(output) +} + export async function getChangesSinceMergeBase(ref: string, initialFetchDepth: number): Promise { if (!(await hasCommit(ref))) { // Fetch and add base branch diff --git a/src/main.ts b/src/main.ts index 56718f2..6464367 100644 --- a/src/main.ts +++ b/src/main.ts @@ -55,6 +55,12 @@ function getConfigFileContent(configPath: string): string { } async function getChangedFiles(token: string, base: string, initialFetchDepth: number): Promise { + // if base is 'HEAD' only local uncommitted changes will be detected + // This is the simplest case as we don't need to fetch more commits or evaluate current/before refs + if (base === git.HEAD) { + return await git.getChangesOnHead() + } + if (github.context.eventName === 'pull_request' || github.context.eventName === 'pull_request_target') { const pr = github.context.payload.pull_request as Webhooks.WebhookPayloadPullRequestPullRequest if (token) { @@ -75,7 +81,7 @@ async function getChangedFilesFromGit(base: string, initialFetchDepth: number): const pushRef = git.getShortName(github.context.ref) || - (core.warning(`'ref' field is missing in PUSH event payload - using current branch, tag or commit SHA`), + (core.warning(`'ref' field is missing in event payload - using current branch, tag or commit SHA`), await git.getCurrentRef()) const baseRef = git.getShortName(base) || defaultRef @@ -88,11 +94,11 @@ async function getChangedFilesFromGit(base: string, initialFetchDepth: number): 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 base is commit SHA we 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`) + core.warning(`'before' field is missing in event payload - changes will be detected from last commit`) return await git.getChangesInLastCommit() }