Conditionally run actions based on files modified by PR, feature branch or pushed commits
| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
| 1 个月前 | ||
| 6 年前 | ||
| 28 天前 | ||
| 28 天前 | ||
| 28 天前 | ||
| 5 年前 | ||
| 6 年前 | ||
| 4 年前 | ||
| 5 年前 | ||
| 6 年前 | ||
| 6 年前 | ||
| 6 年前 | ||
| 1 个月前 | ||
| 6 年前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 6 年前 | ||
| 5 个月前 | ||
| 5 个月前 | ||
| 5 年前 |
以下内容由 AI 翻译,如有问题请 点此提交 issue 反馈
路径变更过滤器
GitHub Action,可根据拉取请求、功能分支或最近推送的提交所修改的文件,有条件地执行工作流步骤和作业。
仅对已更改的组件运行集成测试或部署等耗时任务。这样可以节省时间和资源,在单体仓库设置中尤为如此。GitHub 工作流内置的路径过滤器不支持此功能,因为它们无法在单个作业或步骤级别运行。
实际使用示例:
- sentry.io - backend.yml
- GoogleChrome/web.dev - lint-workflow.yml
- 博客文章《使用 GitHub Actions 配置 Python 代码 linting 作为 CI/CD 的一部分》 - py_linter.yml
支持的工作流
- 拉取请求:
- 由 pull_request 或 pull_request_target 事件触发的工作流
- 针对拉取请求的基准分支检测变更
- 使用 GitHub REST API 获取已修改文件列表
- 需要 pull-requests: read 权限
- 功能分支:
- 合并队列:
- 由 merge_group 事件触发的工作流
base和ref输入参数默认为事件中的提交哈希,除非明确指定- 使用 git 命令检测变更 - 仓库必须已 检出
- 主分支、发布分支或其他长期存在的分支:
- 本地变更
- 当
base输入参数设置为HEAD时,由任何事件触发的工作流 - 针对当前 HEAD 检测变更
- 忽略未跟踪文件
- 当
示例
- uses: dorny/paths-filter@v4
id: changes
with:
filters: |
src:
- 'src/**'
# run only if some file in 'src' folder was changed
- if: steps.changes.outputs.src == 'true'
run: ...
有关更多场景,请参见示例部分。
注意事项
- 路径表达式使用picomatch库进行计算。路径表达式格式的文档可在该项目的GitHub页面上找到。
- Picomatch的dot选项已设置为true。 glob模式匹配也会匹配文件名或文件夹名以点开头的路径。
- 建议使用
'或"引用您的路径表达式。否则,如果表达式以*开头,将会报错。 - 使用act进行本地执行时,仅适用于替代的运行器镜像。默认运行器没有
git二进制文件。- 使用:
act -P ubuntu-latest=nektos/act-environments-ubuntu:18.04
- 使用:
- 容器作业中的Git“可疑所有权”错误会自动处理 - 该操作会使用包含
safe.directory条目的临时HOME重试,这与actions/checkout使用的技术相同。 只有当获取操作依赖于存储在HOME相关文件(例如~/.git-credentials或~/.netrc)中的凭据时,才需要在该操作之前的步骤中自行将仓库标记为安全:git config --global --add safe.directory "$GITHUB_WORKSPACE"
新增功能
- 为
predicate-quantifier输入参数添加了some-with-excludes值 - 自动解决容器作业中的Git“可疑所有权”错误
- 更新到Node 24后发布新的主要版本
v4【重大变更】 - 添加
ref输入参数 - 添加
list-files: csv格式 - 使用
changes输出配置矩阵作业,使其针对每个有变更的文件夹运行 - 改进了
list-files: shell和list-files: escape选项的匹配文件列表 - 路径表达式现在使用picomatch库进行计算
有关更多信息,请参见CHANGELOG
使用方法
- uses: dorny/paths-filter@v4
with:
# Defines filters applied to detected changed files.
# Each filter has a name and a list of rules.
# Rule is a glob expression - paths of all changed
# files are matched against it.
# Rule can optionally specify if the file
# should be added, modified, or deleted.
# For each filter, there will be a corresponding output variable to
# indicate if there's a changed file matching any of the rules.
# Optionally, there can be a second output variable
# set to list of all files matching the filter.
# Filters can be provided inline as a string (containing valid YAML document),
# or as a relative path to a file (e.g.: .github/filters.yaml).
# Filters syntax is documented by example - see examples section.
filters: ''
# Branch, tag, or commit SHA against which the changes will be detected.
# If it references the same branch it was pushed to,
# changes are detected against the most recent commit before the push.
# If it is empty and action is triggered by merge_group event,
# the base commit in the event will be used.
# Otherwise, it uses git merge-base to find the best common ancestor between
# current branch (HEAD) and base.
# When merge-base is found, it's used for change detection - only changes
# introduced by the current branch are considered.
# All files are considered as added if there is no common ancestor with
# base branch or no previous commit.
# This option is ignored if action is triggered by pull_request event,
# unless 'token' is set to an empty string (see the 'token' input below).
# Default: repository default branch (e.g. master)
base: ''
# Git reference (e.g. branch name) from which the changes will be detected.
# Useful when workflow can be triggered only on the default branch (e.g. repository_dispatch event)
# but you want to get changes on a different branch.
# If this is empty and action is triggered by merge_group event,
# the head commit in the event will be used.
# This option is ignored if action is triggered by pull_request event.
# default: ${{ github.ref }}
ref:
# How many commits are initially fetched from the base branch.
# If needed, each subsequent fetch doubles the
# previously requested number of commits until the merge-base
# is found, or there are no more commits in the history.
# This option takes effect only when changes are detected
# using git against base branch (feature branch workflow).
# Default: 100
initial-fetch-depth: ''
# Enables listing of files matching the filter:
# 'none' - Disables listing of matching files (default).
# 'csv' - Coma separated list of filenames.
# If needed, it uses double quotes to wrap filename with unsafe characters.
# 'json' - File paths are formatted as JSON array.
# 'shell' - Space delimited list usable as command-line argument list in Linux shell.
# If needed, it uses single or double quotes to wrap filename with unsafe characters.
# 'escape'- Space delimited list usable as command-line argument list in Linux shell.
# Backslash escapes every potentially unsafe character.
# Default: none
list-files: ''
# Relative path under $GITHUB_WORKSPACE where the repository was checked out.
working-directory: ''
# Personal access token used to fetch a list of changed files
# from GitHub REST API.
# It's only used if action is triggered by a pull request event.
# GitHub token from workflow context is used as default value.
# If an empty string is provided, the action falls back to detect
# changes using git commands. In that case, on pull request events
# the 'base' input overrides the pull request base - e.g. set
# base: ${{ github.event.before }} to detect changes since the last push.
# Default: ${{ github.token }}
token: ''
# Optional parameter to override the default behavior of file matching algorithm.
# Supported values:
# 'some' - File is included if it matches at least one pattern (default).
# 'every' - File is included only if it matches all of the patterns.
# 'some-with-excludes' - File is included if it matches at least one pattern
# and no negated pattern (the ones prefixed with '!').
#
# An example scenario where this is useful if you would like to match all
# .ts files in a sub-directory but not .md files.
# The filters below will match markdown files despite the exclusion syntax UNLESS
# you specify 'every' or 'some-with-excludes' as the predicate-quantifier parameter.
# When you do that, it will only match the .ts files in the subdirectory as expected.
#
# backend:
# - 'pkg/a/b/c/**'
# - '!**/*.jpeg'
# - '!**/*.md'
predicate-quantifier: 'some'
输出
- 每个过滤器都会设置一个以过滤器命名的输出变量,其文本值取决于
predicate-quantifier设置:- 当
predicate-quantifier: 'some'(默认值)时:'true'- 若任何已更改文件匹配过滤器的至少一条规则'false'- 若没有已更改文件匹配过滤器的至少一条规则
- 当
predicate-quantifier: 'every'时:'true'- 若任何已更改文件匹配过滤器的所有规则'false'- 若没有已更改文件匹配过滤器的所有规则
- 当
predicate-quantifier: 'some-with-excludes'时:'true'- 若任何已更改文件匹配过滤器的至少一条规则且不匹配任何其否定规则'false'- 若没有已更改文件匹配过滤器的至少一条规则且不匹配任何其否定规则
- 当
- 每个过滤器都会设置一个名为
${FILTER_NAME}_count的输出变量,用于表示匹配文件的数量。 - 若启用,每个过滤器会设置一个名为
${FILTER_NAME}_files的输出变量,其中包含所有匹配该过滤器的文件列表。 changes- 包含所有与已更改文件匹配的过滤器名称的 JSON 数组。
示例
条件执行
仅当子文件夹中的某些文件发生更改时,才执行工作流作业中的步骤
jobs:
tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: dorny/paths-filter@v4
id: filter
with:
filters: |
backend:
- 'backend/**'
frontend:
- 'frontend/**'
# run only if 'backend' files were changed
- name: backend tests
if: steps.filter.outputs.backend == 'true'
run: ...
# run only if 'frontend' files were changed
- name: frontend tests
if: steps.filter.outputs.frontend == 'true'
run: ...
# run if 'backend' or 'frontend' files were changed
- name: e2e tests
if: steps.filter.outputs.backend == 'true' || steps.filter.outputs.frontend == 'true'
run: ...
仅当子文件夹中的某些文件发生更改时,才在工作流中执行 job
jobs:
# JOB to run change detection
changes:
runs-on: ubuntu-latest
# Required permissions
permissions:
pull-requests: read
# 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@v4
id: filter
with:
filters: |
backend:
- 'backend/**'
frontend:
- 'frontend/**'
# JOB to build and test backend code
backend:
needs: changes
if: ${{ needs.changes.outputs.backend == 'true' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- ...
# JOB to build and test frontend code
frontend:
needs: changes
if: ${{ needs.changes.outputs.frontend == 'true' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- ...
使用变更检测配置矩阵任务
jobs:
# JOB to run change detection
changes:
runs-on: ubuntu-latest
# Required permissions
permissions:
pull-requests: read
outputs:
# Expose matched filters as job 'packages' output variable
packages: ${{ steps.filter.outputs.changes }}
steps:
# For pull requests it's not necessary to checkout the code
- uses: dorny/paths-filter@v4
id: filter
with:
filters: |
package1: src/package1
package2: src/package2
# JOB to build and test each of modified packages
build:
needs: changes
strategy:
matrix:
# Parse JSON array containing names of all filters matching any of changed files
# e.g. ['package1', 'package2'] if both package folders contains changes
package: ${{ fromJSON(needs.changes.outputs.packages) }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- ...
变更检测工作流
拉取请求: 检测相对于 PR 基准分支的变更
on:
pull_request:
branches: # PRs to the following branches will trigger the workflow
- master
- develop
# Optionally you can use the action in the merge queue
# if your repository enables the feature.
merge_group:
branches:
- master
- develop
jobs:
build:
runs-on: ubuntu-latest
# Required permissions
permissions:
contents: read # required by actions/checkout
pull-requests: read # required by dorny/paths-filter
steps:
- uses: actions/checkout@v6
- uses: dorny/paths-filter@v4
id: filter
with:
filters: ... # Configure your filters
功能分支:根据已配置的基准分支检测变更
on:
push:
branches: # Push to following branches will trigger the workflow
- feature/**
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
# This may save additional git fetch roundtrip if
# merge-base is found within latest 20 commits
fetch-depth: 20
- uses: dorny/paths-filter@v4
id: filter
with:
base: develop # Change detection against merge-base with this branch
filters: ... # Configure your filters
长期分支: 针对推送前同一分支上的最新提交检测变更
on:
push:
branches: # Push to the following branches will trigger the workflow
- master
- develop
- release/**
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: dorny/paths-filter@v4
id: filter
with:
# Use context to get the branch where commits were pushed.
# If there is only one long-lived branch (e.g. master),
# you can specify it directly.
# If it's not configured, the repository default branch is used.
base: ${{ github.ref }}
filters: ... # Configure your filters
本地变更: 检测暂存和未暂存的本地变更
on:
push:
branches: # Push to following branches will trigger the workflow
- master
- develop
- release/**
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
# Some action that 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@v4
id: filter
with:
base: HEAD
filters: ... # Configure your filters
高级选项
在独立文件中定义过滤规则
- uses: dorny/paths-filter@v4
id: filter
with:
# Path to file where filters are defined
filters: .github/filters.yaml
使用 YAML 锚点在另一个规则中重用路径表达式
- uses: dorny/paths-filter@v4
id: filter
with:
# &shared is YAML anchor,
# *shared references previously defined anchor
# src filter will match any path under common, config and src folders
filters: |
shared: &shared
- common/**
- config/**
src:
- *shared
- src/**
考虑文件是新增、修改还是删除
- uses: dorny/paths-filter@v4
id: filter
with:
# Changed file can be 'added', 'modified', or 'deleted'.
# By default, the type of change is not considered.
# Optionally, it's possible to specify it using nested
# dictionary, where the type of change composes the key.
# Multiple change types can be specified using `|` as the delimiter.
filters: |
shared: &shared
- common/**
- config/**
addedOrModified:
- added|modified: '**'
allChanges:
- added|deleted|modified: '**'
addedOrModifiedAnchors:
- added|modified: *shared
仅针对部分文件扩展名检测文件夹中的更改
- uses: dorny/paths-filter@v4
id: filter
with:
# This makes it so that all the patterns have to match a file for it to be
# considered changed. Because we have the exclusions for .jpeg and .md files
# the end result is that if those files are changed they will be ignored
# because they don't match the respective rules excluding them.
#
# This can be leveraged to ensure that you only build & test software changes
# that have real impact on the behavior of the code, e.g. you can set up your
# build to run when Typescript/Rust/etc. files are changed but markdown
# changes in the diff will be ignored and you consume less resources to build.
predicate-quantifier: 'every'
filters: |
backend:
- 'pkg/a/b/c/**'
- '!**/*.jpeg'
- '!**/*.md'
检测多个不相关路径中的变更并排除部分文件扩展名
- uses: dorny/paths-filter@v4
id: filter
with:
# With 'some-with-excludes' a file is matched when it matches at least one pattern
# and none of the negated ones. The filter below therefore matches all the files
# in the 'mobile' folder and the workflow file, but never a markdown file or
# anything in 'mobile/.config'.
#
# An exclusion is final - a file excluded by one pattern can't be included back
# by another one. Consequently, a filter consisting of negated patterns only
# never matches anything.
predicate-quantifier: 'some-with-excludes'
filters: |
mobile:
- 'mobile/**'
- '!mobile/**/*.md'
- '!mobile/.config/**'
- '.github/workflows/test_mobile.yml'
变更文件的自定义处理
在 Linux shell 中将已修改文件列表作为命令行参数传递
- uses: dorny/paths-filter@v4
id: filter
with:
# Enable listing of files matching each filter.
# Paths to files will be available in `${FILTER_NAME}_files` output variable.
# Paths will be escaped and space-delimited.
# Output is usable as command-line argument list in Linux shell
list-files: shell
# In this example changed files will be checked by linter.
# It doesn't make sense to lint deleted files.
# Therefore we specify we are only interested in added or modified files.
filters: |
markdown:
- added|modified: '*.md'
- name: Lint Markdown
if: ${{ steps.filter.outputs.markdown == 'true' }}
run: npx textlint ${{ steps.filter.outputs.markdown_files }}
将修改文件列表以 JSON 数组形式传递给另一个 action
- uses: dorny/paths-filter@v4
id: filter
with:
# Enable listing of files matching each filter.
# Paths to files will be available in `${FILTER_NAME}_files` output variable.
# Paths will be formatted as JSON array
list-files: json
# In this example all changed files are passed to the following action to do
# some custom processing.
filters: |
changed:
- '**'
- name: Lint Markdown
uses: johndoe/some-action@v1
with:
files: ${{ steps.filter.outputs.changed_files }}
另请参阅
- test-reporter - 直接在 GitHub 中显示来自主流测试框架的测试结果
许可证
本项目中的脚本和文档均根据 MIT 许可证 发布