feat(filters): unified glob (*/?) and //-regex matching for all filter types

This commit is contained in:
RhenCloud 2026-08-13 18:44:13 +08:00
parent ebae2dc94f
commit cc3ccadc24
No known key found for this signature in database
GPG key ID: A574A617378C4E0B
10 changed files with 1395 additions and 1191 deletions

View file

@ -296,21 +296,22 @@ owner及超级管理员可在分组的「成员」面板创建一次性邀
实操指南见[过滤器教程](./filters),包含完整示例。
| 类型 | 匹配对象 | 示例 |
| --------- | ---------------- | -------------------------------- |
| `event` | GitHub 事件名称 | `push`, `pull_request`, `issues` |
| `repo` | 仓库全名 | `org/repo` |
| `actor` | 发送者登录名 | `username`, `[bot]` |
| `action` | 事件操作 | `opened`, `closed`, `published` |
| `branch` | 分支名称 | `main`, `develop` |
| `keyword` | 载荷正文中的文本 | `deploy`, `/fix\s+\d+/` (正则) |
| 类型 | 匹配对象 | 示例 |
| --------- | ---------------- | ----------------------------------------- |
| `event` | GitHub 事件名称 | `push`, `pull_*`, `pull_request` |
| `repo` | 仓库全名 | `org/repo`, `org/*` |
| `actor` | 发送者登录名 | `username`, `[bot]`, `*[bot]` |
| `action` | 事件操作 | `opened`, `closed`, `published` |
| `branch` | 分支名称 | `main`, `feature-?`, `/^release-/` |
| `keyword` | 载荷正文中的文本 | `deploy`, `*release-*`, `/fix\s+\d+/` |
### 过滤器行为
- 路由中的所有过滤器必须都匹配才触发路由AND 逻辑)
- 在任何过滤器上设置 `"exclude": true` 可反转匹配逻辑NOT 逻辑)
- 非 keyword 过滤器为**精确、不区分大小写**的匹配——不支持通配符(`repo: "org/*"` 不会匹配任何内容)
- `keyword` 过滤器支持正则表达式——正则有误或超过 200 个字符时回退到子串匹配
- 所有过滤器类型支持相同的模式形式:纯文本、`*`/`?` **通配符**`*` 任意长度、`?` 单字符)以及 `/正则表达式/`——均不区分大小写
- 字段过滤器(`event`/`repo`/`actor`/`action`/`branch`)的通配符匹配整个值;`keyword` 的通配符和正则搜索载荷任意位置;`keyword` 的纯文本为子串搜索
- 超过 200 个字符的模式不编译为通配符/正则;`//` 包裹的非法正则匹配不到任何内容
- `branch` 过滤器适用于 push、pull_request、pull_request_review、pull_request_review_comment、create/delete、workflow_run、workflow_job、check_suite、deployment 和 code_scanning_alert 事件
### 匹配值

View file

@ -1,224 +1,263 @@
# 过滤器教程
过滤器决定哪些 Webhook 事件会被[路由](./configuration#路由)转发。只有当路由 `filters` 数组中的**每一个**过滤器都匹配时路由才会触发AND 逻辑)。本页是一份上手教程:解释每种过滤器类型的行为,以及如何组合它们实现真实场景的路由规则。
参考表格见配置指南的[过滤器类型](./configuration#过滤器类型),完整事件列表见[支持的事件](../events/supported)。
## 匹配机制
- 路由中所有过滤器都必须匹配,否则该路由被跳过。
- 每个过滤器将事件与 Webhook 载荷的某个字段进行匹配。
- 匹配**不区分大小写且为精确匹配**`main` 能匹配 `main``Main``MAIN`,但不能匹配 `main-v2`
- `match` 值可以是单个字符串,也可以是字符串数组。数组相当于 OR——只要其中一个值匹配该过滤器即匹配。
- 设置 `"exclude": true` 会反转结果NOT 逻辑):当值**不**等于 match 值时,该过滤器才匹配。
```json
{
"type": "event",
"match": ["push", "pull_request"],
"exclude": false
}
```
上面这条路由同时匹配 `push``pull_request` 事件。
## 各过滤器类型详解
### `event` — 事件类型
匹配 GitHub 事件名称,如 `push``pull_request``issues``release`。它是每条路由的主干。
```json
{ "type": "event", "match": "release" }
```
用数组匹配多种事件:
```json
{ "type": "event", "match": ["create", "delete"] }
```
### `repo` — 仓库
匹配仓库**全名**`owner/name`)。不区分大小写。
```json
{ "type": "repo", "match": "myorg/backend" }
```
将多个仓库路由到同一频道:
```json
{ "type": "repo", "match": ["myorg/backend", "myorg/frontend"] }
```
### `actor` — 发送者
匹配触发事件的 **GitHub 发送者登录名**(载荷中的 `sender.login`)。常用于忽略机器人。
```json
{ "type": "actor", "match": "dependabot[bot]", "exclude": true }
```
上面这条路由对**除** Dependabot 触发之外的所有事件都会触发。
### `action` — 事件操作
匹配载荷中的 `action` 字段,如 `opened``closed``published``completed`。并非所有事件都带有 action——参见[过滤器兼容性](../events/supported#过滤器兼容性)。与 `event` 组合可精确到某个生命周期步骤:
```json
{
"type": "event",
"match": "pull_request",
"exclude": false
},
{
"type": "action",
"match": ["opened", "reopened"]
}
```
上面的规则在拉取请求被打开或重新打开时触发(合并/关闭/编辑时不触发)。
### `branch` — 分支
匹配事件涉及的分支。何种字段算作「分支」取决于事件类型:
| 事件 | 提取的分支 |
| --------------------------- | ----------------------------------- |
| `push` | 推送到的目标分支 |
| `pull_request`(及 review | 拉取请求的 **head**(源)分支 |
| `create` / `delete` | 创建/删除的分支或标签 |
| `workflow_run` | 工作流运行所在的 `head_branch` |
| `workflow_job` | 作业运行所在的 `head_branch` |
| `check_suite` | 检查套件的 `head_branch` |
| `deployment` | 部署引用(去除 `refs/heads/` 前缀) |
| `code_scanning_alert` | 告警所属的分支 |
```json
{
"type": "event",
"match": "push"
},
{
"type": "branch",
"match": "main"
}
```
仅当推送到 `main` 时触发。要关注多个长期分支:
```json
{ "type": "branch", "match": ["main", "develop"] }
```
> [!NOTE]
> `branch` 匹配是**精确且不区分大小写**的,不是通配符或前缀匹配。`feature/*` 这样的值**不会**生效。需要前缀或通配符式匹配时,请改用 `keyword` 过滤器匹配载荷(见下)。
### `keyword` — 载荷中的文本
匹配整个 JSON 载荷(转为小写)。它支持正则表达式,因此是最灵活的过滤器。模式以 `i`(忽略大小写)标志编译。
```json
{ "type": "keyword", "match": "/deploy/started/i" }
```
当载荷中任意位置包含 `deploy/started` 时触发。由于载荷已被转为小写,`i` 标志可有可无但无副作用。
一些实用示例:
```json
{ "type": "keyword", "match": "/dependabot/" }
```
```json
{ "type": "keyword", "match": "/^(fix|hotfix)/" }
```
```json
{ "type": "keyword", "match": "/release-[0-9]+/" }
```
行为细节:
- 超过 200 个字符的模式**不**编译为正则,回退为纯子串匹配。
- 如果某模式不是合法正则,也会回退为子串匹配,而不是报错。
- 要搜索是正则特殊字符的文本(如 `v1.2.3`),可以省略正则语法直接依赖子串回退——不含正则元字符的模式两种方式行为相同。
- 搜索覆盖**整个**载荷提交信息、PR 标题与正文、标签、引用,甚至仓库名和发送者名。
### `keyword``exclude` 组合
与其他过滤器一样,`exclude` 会反转关键词匹配:
```json
{ "type": "keyword", "match": "/wip|draft/", "exclude": true }
```
跳过载荷中提及 `wip``draft` 的事件。
## 示例 1PR 通知,跳过机器人和草稿
转发拉取请求动态,但忽略机器人作者和草稿 PR发往 `#prs` 频道:
```json
{
"id": "pr-notices",
"name": "PR Notices",
"enabled": true,
"groupId": "eng",
"filters": [
{ "type": "event", "match": "pull_request" },
{ "type": "actor", "match": "dependabot[bot]", "exclude": true },
{ "type": "keyword", "match": "\"draft\": true", "exclude": true }
],
"target": { "channelId": "111111111111111111" }
}
```
`"draft": true` 模式匹配 GitHub 在拉取请求载荷中包含的 `draft` 字段;配合 `exclude: true` 即可过滤掉草稿 PR。
## 示例 2仅发布通知频道
只转发特定仓库的已发布 release
```json
{
"id": "release-alerts",
"name": "Release Alerts",
"enabled": true,
"groupId": "eng",
"filters": [
{ "type": "event", "match": "release" },
{ "type": "action", "match": "published" },
{ "type": "repo", "match": "myorg/backend" }
],
"target": { "channelId": "222222222222222222" }
}
```
## 示例 3CI 失败
转发任意分支上以失败结束的 workflow run发往 `#ci` 频道:
```json
{
"id": "ci-failures",
"name": "CI Failures",
"enabled": true,
"groupId": "eng",
"filters": [
{ "type": "event", "match": "workflow_run" },
{ "type": "action", "match": "completed" },
{ "type": "keyword", "match": "\"conclusion\":\"failure\"" }
],
"target": { "channelId": "333333333333333333" }
}
```
## 常见陷阱
- **非 keyword 过滤器不支持通配符。** `event``repo``actor``action``branch` 都是精确匹配。`repo: "myorg/*"` 不会匹配任何内容。
- **`action` 过滤器遇到无 action 的事件永远不匹配。** 先确认该事件带有 `action` 字段(见[过滤器兼容性](../events/supported#过滤器兼容性))。
- **`branch` 过滤器遇到无分支的事件永远不匹配。** 在 `issues` 事件上使用 `branch` 过滤器恒为假。此时需要类似分支的匹配可用 `keyword`
- **`keyword` 会搜索一切。** 因为它扫描整个载荷,`"fix"` 这样的模式可能同时匹配提交信息、issue 标题和仓库名。请尽量写得更具体。
- **牢记 `exclude` 语义。** `exclude: true` 反转的是整个过滤器——数组中的一个值不匹配并不会「阻断」路由;只有当**所有**值都不匹配时,取反后的过滤器才匹配。
# 过滤器教程
过滤器决定哪些 Webhook 事件会被[路由](./configuration#路由)转发。只有当路由 `filters` 数组中的**每一个**过滤器都匹配时路由才会触发AND 逻辑)。本页是一份上手教程:解释每种过滤器类型的行为,以及如何组合它们实现真实场景的路由规则。
参考表格见配置指南的[过滤器类型](./configuration#过滤器类型),完整事件列表见[支持的事件](../events/supported)。
## 匹配机制
- 路由中所有过滤器都必须匹配,否则该路由被跳过。
- 每个过滤器将事件与 Webhook 载荷的某个字段进行匹配。
- 所有过滤器类型都**不区分大小写**。
- `match` 值可以是单个字符串,也可以是字符串数组。数组相当于 OR——只要其中一个值匹配该过滤器即匹配。
- 设置 `"exclude": true` 会反转结果NOT 逻辑):当值**不**匹配时,该过滤器才匹配。
```json
{
"type": "event",
"match": ["push", "pull_request"],
"exclude": false
}
```
上面这条路由同时匹配 `push``pull_request` 事件。
## 模式语法
所有过滤器类型共享以下三种模式写法:
| 模式 | 含义 |
| -------------- | ----------------------------------------------------------------- |
| `纯文本` | 字段过滤器:**完全相等**匹配;`keyword`:在载荷中任意位置搜索。 |
| `*` / `?` | **通配符glob**——`*` 任意长度、`?` 恰好一个字符。 |
| `/正则表达式/` | 按**正则表达式**编译(忽略大小写标志)。 |
- 字段过滤器(`event`/`repo`/`actor`/`action`/`branch`)的纯文本与通配符匹配整个值;`keyword` 则在载荷中任意位置搜索。
- 正则表达式始终是搜索语义:`/^feat/` 匹配以 `feat` **开头**的值,`/feat/` 匹配任意位置出现 `feat` 的值。
示例:
```json
{ "type": "event", "match": "pull_*" }
```
匹配 `pull_request``pull_request_review``pull_request_review_comment` 等。
```json
{ "type": "repo", "match": "myorg/*" }
```
```json
{ "type": "branch", "match": "feature-?" }
```
匹配 `feature-x``feature-1`,但不匹配 `feature-xy`
```json
{ "type": "branch", "match": "/^feat/" }
```
匹配任何以 `feat` 开头的分支名。
> [!TIP]
> 通配符和正则同样不区分大小写,且 `*` 可以跨过仓库名中的 `/``myorg/*` 也能匹配 `myorg/sub/backend`)。
## 各过滤器类型详解
### `event` — 事件类型
匹配 GitHub 事件名称,如 `push``pull_request``issues``release`。它是每条路由的主干。
```json
{ "type": "event", "match": "release" }
```
用数组匹配多种事件:
```json
{ "type": "event", "match": ["create", "delete"] }
```
### `repo` — 仓库
匹配仓库**全名**`owner/name`)。不区分大小写。
```json
{ "type": "repo", "match": "myorg/backend" }
```
将多个仓库路由到同一频道:
```json
{ "type": "repo", "match": ["myorg/backend", "myorg/frontend"] }
```
### `actor` — 发送者
匹配触发事件的 **GitHub 发送者登录名**(载荷中的 `sender.login`)。常用于忽略机器人。
```json
{ "type": "actor", "match": "dependabot[bot]", "exclude": true }
```
上面这条路由对**除** Dependabot 触发之外的所有事件都会触发。
### `action` — 事件操作
匹配载荷中的 `action` 字段,如 `opened``closed``published``completed`。并非所有事件都带有 action——参见[过滤器兼容性](../events/supported#过滤器兼容性)。与 `event` 组合可精确到某个生命周期步骤:
```json
{
"type": "event",
"match": "pull_request",
"exclude": false
},
{
"type": "action",
"match": ["opened", "reopened"]
}
```
上面的规则在拉取请求被打开或重新打开时触发(合并/关闭/编辑时不触发)。
### `branch` — 分支
匹配事件涉及的分支。何种字段算作「分支」取决于事件类型:
| 事件 | 提取的分支 |
| --------------------------- | ----------------------------------- |
| `push` | 推送到的目标分支 |
| `pull_request`(及 review | 拉取请求的 **head**(源)分支 |
| `create` / `delete` | 创建/删除的分支或标签 |
| `workflow_run` | 工作流运行所在的 `head_branch` |
| `workflow_job` | 作业运行所在的 `head_branch` |
| `check_suite` | 检查套件的 `head_branch` |
| `deployment` | 部署引用(去除 `refs/heads/` 前缀) |
| `code_scanning_alert` | 告警所属的分支 |
```json
{
"type": "event",
"match": "push"
},
{
"type": "branch",
"match": "main"
}
```
仅当推送到 `main` 时触发。要关注多个长期分支:
```json
{ "type": "branch", "match": ["main", "develop"] }
```
> [!NOTE]
> `branch` 匹配不区分大小写。需要前缀或通配符式匹配时,可直接使用通配符(`feature/*`)或用 `/` 包裹正则(`/^release-/`)。
### `keyword` — 载荷中的文本
匹配整个 JSON 载荷(转为小写)。它是最灵活的过滤器:纯文本在任意位置搜索,`*`/`?` 通配符带通配搜索,`/` 包裹的模式按正则表达式编译(带 `i` 标志)。
```json
{ "type": "keyword", "match": "deploy" }
```
当载荷中任意位置包含 `deploy` 时触发。由于载荷已被转为小写,`Deploy``DEPLOY` 等都会匹配。
```json
{ "type": "keyword", "match": "*release-*" }
```
```json
{ "type": "keyword", "match": "/^(fix|hotfix)/" }
```
```json
{ "type": "keyword", "match": "/release-[0-9]+/" }
```
行为细节:
- 超过 200 个字符的模式**不**编译为通配符/正则,回退为纯文本匹配。
- 被 `/` 包裹但**不是合法正则**的模式匹配**任何内容都不命中**(过滤器恒为 false而不会报错。
- 要搜索是通配符或正则特殊字符的文本(如 `v1.2.3`),使用纯文本形式即可——不含 `*``?` 且未被 `//` 包裹的模式按字面匹配。
- 搜索覆盖**整个**载荷提交信息、PR 标题与正文、标签、引用,甚至仓库名和发送者名。
### `keyword``exclude` 组合
与其他过滤器一样,`exclude` 会反转关键词匹配:
```json
{ "type": "keyword", "match": "/wip|draft/", "exclude": true }
```
跳过载荷中提及 `wip``draft` 的事件。
## 示例 1PR 通知,跳过机器人和草稿
转发拉取请求动态,但忽略机器人作者和草稿 PR发往 `#prs` 频道:
```json
{
"id": "pr-notices",
"name": "PR Notices",
"enabled": true,
"groupId": "eng",
"filters": [
{ "type": "event", "match": "pull_request" },
{ "type": "actor", "match": "dependabot[bot]", "exclude": true },
{ "type": "keyword", "match": "\"draft\": true", "exclude": true }
],
"target": { "channelId": "111111111111111111" }
}
```
`"draft": true` 模式匹配 GitHub 在拉取请求载荷中包含的 `draft` 字段;配合 `exclude: true` 即可过滤掉草稿 PR。
## 示例 2仅发布通知频道
只转发特定仓库的已发布 release
```json
{
"id": "release-alerts",
"name": "Release Alerts",
"enabled": true,
"groupId": "eng",
"filters": [
{ "type": "event", "match": "release" },
{ "type": "action", "match": "published" },
{ "type": "repo", "match": "myorg/backend" }
],
"target": { "channelId": "222222222222222222" }
}
```
## 示例 3CI 失败
转发任意分支上以失败结束的 workflow run发往 `#ci` 频道:
```json
{
"id": "ci-failures",
"name": "CI Failures",
"enabled": true,
"groupId": "eng",
"filters": [
{ "type": "event", "match": "workflow_run" },
{ "type": "action", "match": "completed" },
{ "type": "keyword", "match": "\"conclusion\":\"failure\"" }
],
"target": { "channelId": "333333333333333333" }
}
```
## 常见陷阱
- **通配符是 glob不是正则。** `repo: "myorg/*"` 匹配 `myorg` 下的任意仓库(含 `myorg/sub/backend`),但 `repo: "myorg/.*"` 按字面匹配。需要正则请用 `/` 包裹:`"/myorg\/.*/"`
- **被 `/` 包裹的非法正则永远不匹配。** 与纯文本不同——未包裹的非法模式按字面匹配。只有确定是真正的正则时才使用 `//` 包裹。
- **`action` 过滤器遇到无 action 的事件永远不匹配。** 先确认该事件带有 `action` 字段(见[过滤器兼容性](../events/supported#过滤器兼容性))。
- **`branch` 过滤器遇到无分支的事件永远不匹配。** 在 `issues` 事件上使用 `branch` 过滤器恒为假。此时需要类似分支的匹配可用 `keyword`
- **`keyword` 会搜索一切。** 因为它扫描整个载荷,`"fix"` 这样的模式可能同时匹配提交信息、issue 标题和仓库名。请尽量写得更具体。
- **牢记 `exclude` 语义。** `exclude: true` 反转的是整个过滤器——数组中的一个值不匹配并不会「阻断」路由;只有当**所有**值都不匹配时,取反后的过滤器才匹配。