mirror of
https://gitea.com/actions/dorny-paths-filter.git
synced 2025-12-23 23:48:20 +00:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1cbb925a17 | ||
|
|
a2e5f9f7bb | ||
|
|
0612377665 | ||
|
|
0c9e16cc6d | ||
|
|
9b321c4b3a | ||
|
|
25327a213e |
4
.github/filters.yml
vendored
Normal file
4
.github/filters.yml
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
error:
|
||||
- not_existing_path/**/*
|
||||
any:
|
||||
- "**/*"
|
||||
14
.github/workflows/build.yml
vendored
Normal file
14
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
name: "Build"
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- run: |
|
||||
npm install
|
||||
npm run all
|
||||
58
.github/workflows/pull-request-verification.yml
vendored
Normal file
58
.github/workflows/pull-request-verification.yml
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
name: "Pull Request Verification"
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- synchronize
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- run: |
|
||||
npm install
|
||||
npm run all
|
||||
|
||||
test-inline:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: ./
|
||||
id: filter
|
||||
with:
|
||||
filters: |
|
||||
error:
|
||||
- not_existing_path/**/*
|
||||
any:
|
||||
- "**/*"
|
||||
- name: filter-test
|
||||
if: steps.filter.outputs.any != 'true' || steps.filter.outputs.error == 'true'
|
||||
run: exit 1
|
||||
|
||||
test-external:
|
||||
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
|
||||
|
||||
test-without-token:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: ./
|
||||
id: filter
|
||||
with:
|
||||
token: ''
|
||||
filters: '.github/filters.yml'
|
||||
- name: filter-test
|
||||
if: steps.filter.outputs.any != 'true' || steps.filter.outputs.error == 'true'
|
||||
run: exit 1
|
||||
36
.github/workflows/test.yml
vendored
36
.github/workflows/test.yml
vendored
@@ -1,36 +0,0 @@
|
||||
name: "build-test"
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- synchronize
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- run: |
|
||||
npm install
|
||||
npm run all
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: ./
|
||||
id: filter
|
||||
with:
|
||||
githubToken: ${{ github.token }}
|
||||
filters: |
|
||||
src:
|
||||
- src/**/*
|
||||
tests:
|
||||
- __tests__/**/*
|
||||
any:
|
||||
- "**/*"
|
||||
- name: filter-test
|
||||
if: steps.filter.outputs.any != 'true'
|
||||
run: exit 1
|
||||
19
README.md
19
README.md
@@ -1,5 +1,5 @@
|
||||
<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-test/badge.svg"></a>
|
||||
<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>
|
||||
</p>
|
||||
|
||||
**CAUTION**: This action can be only used in a workflow triggered by `pull_request` event.
|
||||
@@ -21,8 +21,8 @@ Corresponding output variable will be created to indicate if there's a changed f
|
||||
Output variables can be later used in the `if` clause to conditionally run specific steps.
|
||||
|
||||
### Inputs
|
||||
- **`githubToken`**: GitHub Access Token - use `${{ github.token }}`
|
||||
- **`filters`**: YAML dictionary where keys specifies rule names and values are lists of file path patterns
|
||||
- **`token`**: GitHub Access Token - defaults to `${{ github.token }}`
|
||||
- **`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
|
||||
- For each rule it sets output variable named by the rule to text:
|
||||
@@ -36,14 +36,12 @@ Output variables can be later used in the `if` clause to conditionally run speci
|
||||
|
||||
### Sample workflow
|
||||
```yaml
|
||||
...
|
||||
name: Build verification
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- edited
|
||||
- synchronize
|
||||
branches:
|
||||
- master
|
||||
@@ -55,7 +53,7 @@ jobs:
|
||||
- uses: dorny/pr-changed-files-filter@v1
|
||||
id: filter
|
||||
with:
|
||||
githubToken: ${{ github.token }}
|
||||
# inline YAML or path to separate file (e.g.: .github/filters.yaml)
|
||||
filters: |
|
||||
backend:
|
||||
- 'backend/**/*'
|
||||
@@ -80,10 +78,11 @@ jobs:
|
||||
|
||||
## How it works
|
||||
|
||||
1. Required inputs are checked (`githubToken` & `filters`)
|
||||
2. Provided access token is used to fetch list of changed files.
|
||||
3. For each filter rule it checks if there is any matching file
|
||||
4. Output variables are set
|
||||
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
|
||||
|
||||
## Difference from related projects:
|
||||
|
||||
|
||||
@@ -2,11 +2,12 @@ name: 'Pull request changed files filter'
|
||||
description: 'Enables conditional execution of workflow job steps considering which files are modified by a pull request.'
|
||||
author: 'Michal Dorner <dorner.michal@gmail.com>'
|
||||
inputs:
|
||||
githubToken:
|
||||
token:
|
||||
description: 'GitHub Access Token'
|
||||
required: true
|
||||
required: false
|
||||
default: ${{ github.token }}
|
||||
filters:
|
||||
description: 'YAML dictionary where keys specifies rule names and values are lists of (globbing) file path patterns'
|
||||
description: 'Path to the configuration file or YAML string with filters definition'
|
||||
required: true
|
||||
runs:
|
||||
using: 'node12'
|
||||
|
||||
1480
dist/index.js
vendored
1480
dist/index.js
vendored
File diff suppressed because it is too large
Load Diff
11598
package-lock.json
generated
11598
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
25
package.json
25
package.json
@@ -25,27 +25,28 @@
|
||||
"author": "YourNameOrOrganization",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.2.0",
|
||||
"@actions/core": "^1.2.4",
|
||||
"@actions/exec": "^1.0.4",
|
||||
"@actions/github": "^2.2.0",
|
||||
"@octokit/webhooks": "^7.6.1",
|
||||
"@octokit/webhooks": "^7.6.2",
|
||||
"minimatch": "^3.0.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^24.0.23",
|
||||
"@types/jest": "^25.2.3",
|
||||
"@types/js-yaml": "^3.12.4",
|
||||
"@types/minimatch": "^3.0.3",
|
||||
"@types/node": "^12.7.12",
|
||||
"@typescript-eslint/parser": "^2.8.0",
|
||||
"@zeit/ncc": "^0.22.2",
|
||||
"@types/node": "^14.0.5",
|
||||
"@typescript-eslint/parser": "^3.0.0",
|
||||
"@zeit/ncc": "^0.22.3",
|
||||
"eslint": "^5.16.0",
|
||||
"eslint-plugin-github": "^2.0.0",
|
||||
"eslint-plugin-jest": "^22.21.0",
|
||||
"jest": "^24.9.0",
|
||||
"jest-circus": "^24.9.0",
|
||||
"js-yaml": "^3.13.1",
|
||||
"prettier": "^1.19.1",
|
||||
"ts-jest": "^24.2.0",
|
||||
"typescript": "^3.6.4"
|
||||
"jest": "^26.0.1",
|
||||
"jest-circus": "^26.0.1",
|
||||
"js-yaml": "^3.14.0",
|
||||
"prettier": "^2.0.5",
|
||||
"ts-jest": "^26.0.0",
|
||||
"typescript": "^3.9.3"
|
||||
},
|
||||
"jest": {
|
||||
"testEnvironment": "node"
|
||||
|
||||
26
src/git.ts
Normal file
26
src/git.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import {exec} from '@actions/exec'
|
||||
|
||||
export async function fetchBranch(base: string): Promise<void> {
|
||||
const exitCode = await exec('git', ['fetch', '--depth=1', 'origin', base])
|
||||
if (exitCode !== 0) {
|
||||
throw new Error(`Fetching branch ${base} failed, exiting`)
|
||||
}
|
||||
}
|
||||
|
||||
export async function getChangedFiles(base: string): Promise<string[]> {
|
||||
let output = ''
|
||||
const exitCode = await exec('git', ['diff-index', '--name-only', base], {
|
||||
listeners: {
|
||||
stdout: (data: Buffer) => (output += data.toString())
|
||||
}
|
||||
})
|
||||
|
||||
if (exitCode !== 0) {
|
||||
throw new Error(`Couldn't determine changed files, exiting`)
|
||||
}
|
||||
|
||||
return output
|
||||
.split('\n')
|
||||
.map(s => s.trim())
|
||||
.filter(s => s.length > 0)
|
||||
}
|
||||
42
src/main.ts
42
src/main.ts
@@ -1,14 +1,16 @@
|
||||
import * as fs from 'fs'
|
||||
import * as core from '@actions/core'
|
||||
import * as github from '@actions/github'
|
||||
import {Webhooks} from '@octokit/webhooks'
|
||||
|
||||
import Filter from './filter'
|
||||
import * as git from './git'
|
||||
|
||||
async function run(): Promise<void> {
|
||||
try {
|
||||
const token = core.getInput('githubToken', {required: true})
|
||||
const filterYaml = core.getInput('filters', {required: true})
|
||||
const client = new github.GitHub(token)
|
||||
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')
|
||||
@@ -16,8 +18,8 @@ async function run(): Promise<void> {
|
||||
}
|
||||
|
||||
const pr = github.context.payload.pull_request as Webhooks.WebhookPayloadPullRequestPullRequest
|
||||
const filter = new Filter(filterYaml)
|
||||
const files = await getChangedFiles(client, pr)
|
||||
const filter = new Filter(filtersYaml)
|
||||
const files = token ? await getChangedFilesFromApi(token, pr) : await getChangedFilesFromGit(pr)
|
||||
|
||||
const result = filter.match(files)
|
||||
for (const key in result) {
|
||||
@@ -28,11 +30,37 @@ async function run(): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
function isPathInput(text: string): boolean {
|
||||
return !text.includes('\n')
|
||||
}
|
||||
|
||||
function getConfigFileContent(configPath: string): string {
|
||||
if (!fs.existsSync(configPath)) {
|
||||
throw new Error(`Configuration file '${configPath}' not found`)
|
||||
}
|
||||
|
||||
if (!fs.lstatSync(configPath).isFile()) {
|
||||
throw new Error(`'${configPath}' is not a file.`)
|
||||
}
|
||||
|
||||
return fs.readFileSync(configPath, {encoding: 'utf8'})
|
||||
}
|
||||
|
||||
// Fetch base branch and use `git diff` to determine changed files
|
||||
async function getChangedFilesFromGit(pullRequest: Webhooks.WebhookPayloadPullRequestPullRequest): 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)
|
||||
}
|
||||
|
||||
// Uses github REST api to get list of files changed in PR
|
||||
async function getChangedFiles(
|
||||
client: github.GitHub,
|
||||
async function getChangedFilesFromApi(
|
||||
token: string,
|
||||
pullRequest: Webhooks.WebhookPayloadPullRequestPullRequest
|
||||
): Promise<string[]> {
|
||||
core.debug('Fetching list of modified files from Github API')
|
||||
const client = new github.GitHub(token)
|
||||
const pageSize = 100
|
||||
const files: string[] = []
|
||||
for (let page = 0; page * pageSize < pullRequest.changed_files; page++) {
|
||||
|
||||
Reference in New Issue
Block a user