Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce 'paths_filter' option and 'changed_files' output #181

Merged
merged 9 commits into from
Jan 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 116 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ However, GitHub's `paths-ignore` has some limitations:
To overcome those limitations, `skip-duplicate-actions` provides a more flexible `paths_ignore`-feature with an efficient backtracking-algorithm.
Instead of stupidly looking at the current commit, `paths_ignore` will look for successful checks in your commit-history.

You can use the [`paths_filter` option](#paths_filter) if you need to define multiple `paths_ignore` patterns in a single workflow.

## Skip if paths not changed

In some projects, there are tasks that should be only executed if specific sub-directories were changed.
Expand All @@ -59,6 +61,8 @@ However, GitHub's `paths` has some limitations:
To overcome those limitations, `skip-duplicate-actions` provides a more sophisticated `paths`-feature.
Instead of blindly skipping checks, the backtracking-algorithm will only skip something if it can find a suitable check in your commit-history.

You can use the [`paths_filter` option](#paths_filter) if you need to define multiple `paths` patterns in a single workflow.

## Cancel outdated workflow-runs

Typically, workflows should only run for the most recent commit.
Expand All @@ -71,18 +75,36 @@ Therefore, when you push changes to a branch, `skip-duplicate-actions` can be co

### `paths_ignore`

A JSON-array with ignored path-patterns, e.g. something like `'["**/README.md", "**/docs/**"]'`.
A JSON-array with ignored path patterns, e.g. something like `["**/README.md", "**/docs/**"]`.
See [cheat sheet](https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#filter-pattern-cheat-sheet) for path-pattern examples.
See [micromatch](https://github.com/micromatch/micromatch) for details about supported path-patterns.
Default `[]`.

### `paths`

A JSON-array with path-patterns, e.g. something like `'["platform-specific/**"]'`.
A JSON-array with path patterns, e.g. something like `["platform-specific/**"]`.
If this is non-empty, then `skip-duplicate-actions` will try to skip commits that did not change any of those paths.
It uses the same syntax as `paths_ignore`.
Default `[]`.

### `paths_filter`

A YAML-string with named paths_ignore/paths patterns, e.g. something like:

```yaml
frontend:
paths_ignore:
- 'frontend/docs/**/*'
paths:
- 'frontend/**/*'
backend:
paths:
- 'backend/**/*'
```

Useful if you have multiple jobs in a workflow and want to skip them based on different `paths_ignore` / `paths` patterns.
See the corresponding [`paths_result` output](#paths_result-object) and [example configuration](#example-3-skip-using-paths_filter).

### `cancel_others`

If true, then workflow-runs from outdated commits will be cancelled. Default `false`.
Expand All @@ -93,32 +115,72 @@ If true, skip if an already finished duplicate run can be found. Default `true`.

### `do_not_skip`

A JSON-array with triggers that should never be skipped. Default `'["workflow_dispatch", "schedule"]'`.
A JSON-array with triggers that should never be skipped. Default `["workflow_dispatch", "schedule"]`.

### `concurrent_skipping`

One of `never`, `same_content`, `same_content_newer`, `outdated_runs`, `always`. Default `never`.

## Outputs

### `should_skip`
### `should_skip` (string)

Returns true if the current run should be skipped according to your configured rules. This should be evaluated for either individual steps or entire jobs.

### `reason`
### `reason` (string)

The reason why the current run is considered skippable or unskippable. Corresponds approximately to the input options.

The reason why the current run is considered skippable or unskippable. Corresponds approximately to the input options, for example `skip_after_successful_duplicate`.
- Example: `skip_after_successful_duplicate`

### `skipped_by`
### `skipped_by` (object)

Information about the workflow run which caused the current run to be skipped. Returns information only when current run is considered skippable.
Returns information about the workflow run which caused the current run to be skipped. Returns information only when current run is considered skippable.

### `paths_result` (object)

Returns information for each configured filter in `paths_filter`.

- Example:
```json
{
"frontend": {
"should_skip": true,
"backtrack_count": 1,
"skipped_by": {
// Information about the workflow run
}
},
"backend": {
"should_skip": false,
"backtrack_count": 1,
"matched_files": ["backend/file.txt"]
},
"global": {
"should_skip": false,
"backtrack_count": 0
}
}
```
- The `global` key corresponds to the "global" `paths_ignore` and `paths` inputs, which makes it possible to profit from the information of paths_result even when only defining "global" `paths_ignore` and/or `paths` inputs.
- A list of matched files is returned in `matched_files`, if there are any.
- The `skipped_by` the return value behaves the same as the "global" [`skipped_by` output](#skipped_by-object).
- The `backtrack_count` shows how many commits where traced back (skipped) until a result has been found.
- If `skip-duplicate-actions` terminates before the paths checks are performed (for example, when a successful duplicate run has been found) `paths_result` returns an empty object (`{}`).

### `changed_files` (array)

A two-dimensional array, with a list of changed files for each commit that has been traced back. Returns information only if one of the options `paths_ignore`, `paths` or `paths_filter` is set.

- Example: `[["some/example/file.txt", "another/example/file.txt"], ["frontend/file.txt"]]`
- Having a two-dimensional list makes processing flexible. For example, one can flatten (and uniquify) the list to get all changed files for all commits which were traced back. Or one can use `changed_files[0]` to get the changed files in the current commit. One might also use the output of `backtrack_count` from `paths_result` to process the list of changed files.

## Usage examples

You can use `skip-duplicate-actions` to either skip individual steps or entire jobs.
To minimize changes to existing jobs, it is often easier to skip entire jobs.

### Option 1: Skip entire jobs
### Example 1: Skip entire jobs

To skip entire jobs, you should add a `pre_job` that acts as a pre-condition for your `main_job`.
Although this example looks like a lot of code, there are only two additional lines in your project-specific `main_job` (the `needs`-clause and the `if`-clause):
Expand Down Expand Up @@ -149,7 +211,7 @@ jobs:
- run: echo "Running slow tests..." && sleep 30
```

### Option 2: Skip individual steps
### Example 2: Skip individual steps

The following example demonstrates how to skip an individual step with an `if`-clause and an `id`.
In this example, the step will be skipped if no files in `src/` or `dist/` were changed:
Expand All @@ -170,6 +232,50 @@ jobs:
echo "Do other stuff..."
```

### Example 3: Skip using `paths_filter`

The `paths_filter` option can be used if you have multiple jobs in a workflow and want to skip them based on different `paths_ignore` / `paths` patterns. When defining such filters, the action returns corresponding information in the `paths_result` output.
For example in a monorepo, you might want to run jobs related to the "frontend" only if some files in the corresponding "frontend/" folder have changed and the same for "backend". This can be achieved with the following configuration:

```yml
jobs:
pre_job:
runs-on: ubuntu-latest
outputs:
should_skip: ${{ steps.skip_check.outputs.should_skip }}
paths_result: ${{ steps.skip_check.outputs.paths_result }}
steps:
- id: skip_check
uses: fkirc/skip-duplicate-actions@master
with:
paths_filter: |
frontend:
paths_ignore:
- 'frontend/docs/**/*'
paths:
- 'frontend/**/*'
backend:
paths:
- 'backend/**/*'
### You can also control the backtracking behavior
# Can be a boolean or number (default: true)
# 5 means to stop after having traced back 5 commits
# backtracking: 5
# Can be mixed with the "global" paths_ignore/paths options, e.g.
# paths_ignore: '["**/README.md"]'

frontend:
needs: pre_job
# If 'skip-duplicate-actions' terminates before the paths checks are performed (e.g., when a successful duplicate run has
# been found) paths_result output returns an empty object ({}). This can be easily intercepted in the if condition of a job
# by checking the result of the "global" should_skip output first.
if: needs.pre_job.outputs.should_skip != 'true' || !fromJSON(needs.pre_job.outputs.paths_result).frontend.should_skip
...

backend:
...
```

## How does it work?

`skip-duplicate-actions` uses the [Workflow Runs API](https://docs.github.com/en/rest/reference/actions#workflow-runs) to query workflow-runs.
Expand Down
17 changes: 12 additions & 5 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,16 @@ inputs:
required: true
default: ${{ github.token }}
paths_ignore:
description: 'A JSON-array with ignored path patterns, e.g. something like ["**/README.md", "**/docs/**"]'
description: 'A JSON-array with ignored path patterns'
required: false
default: '[]'
paths:
description: 'A JSON-array with path patterns, e.g. something like ["platform-specific/**"]'
description: 'A JSON-array with path patterns'
required: false
default: '[]'
paths_filter:
description: 'A YAML-string with named paths_ignore/paths patterns'
required: false
cancel_others:
description: 'If true, then workflow-runs from outdated commits will be cancelled'
required: false
Expand All @@ -35,11 +38,15 @@ inputs:
default: 'never'
outputs:
should_skip:
description: 'Returns true if the current run should be skipped according to your configured rules. This should be evaluated for either individual steps or entire jobs.'
description: 'Returns true if the current run should be skipped according to your configured rules'
reason:
description: 'The reason why the current run is considered skippable or unskippable. Corresponds approximately to the input options, for example `skip_after_successful_duplicate`.'
description: 'The reason why the current run is considered skippable or unskippable'
skipped_by:
description: 'Information about the workflow run which caused the current run to be skipped.'
description: 'Returns information about the workflow run which caused the current run to be skipped'
changed_files:
description: 'A two-dimensional array, with a list of changed files for each commit that was traced back'
paths_result:
description: 'Returns information for each configured filter in paths_filter'
runs:
using: 'node16'
main: 'dist/index.js'
Loading