mirror of
https://github.com/ReCloudStudio/WebHooker.git
synced 2026-09-22 16:11:29 +00:00
fix(check): key check_run in-place edits on check name + head_sha
Providers that emit a fresh check run id per phase (e.g. Cloudflare Pages) no longer post a new message for each status change; same name + commit SHA now edits the existing message. Falls back to the run id when name or head_sha is absent. Docs updated in sync.
This commit is contained in:
parent
cc8bb4182f
commit
fd6ccda411
9 changed files with 47 additions and 8 deletions
|
|
@ -163,7 +163,7 @@ tests/__snapshots__/ # formatter snapshot golden files (toMatchSnapshot)
|
||||||
- Format 28 GitHub/Gitea event types plus `custom` webhooks as platform-neutral messages (Discord embeds + Telegram HTML)
|
- Format 28 GitHub/Gitea event types plus `custom` webhooks as platform-neutral messages (Discord embeds + Telegram HTML)
|
||||||
- Show the forge source (named per `Group.forgeSources` host entries) in the message footer when the group defines a host matching the event's repository
|
- Show the forge source (named per `Group.forgeSources` host entries) in the message footer when the group defines a host matching the event's repository
|
||||||
- Route messages to Discord channels/threads and Telegram chats/topics via REST
|
- Route messages to Discord channels/threads and Telegram chats/topics via REST
|
||||||
- Edit already-sent messages in place for `workflow_run` / `check_run` progress (stable `updateKey`, `message_tracking` via D1 with KV `msg:*` fallback)
|
- Edit already-sent messages in place for `workflow_run` / `check_run` progress (stable `updateKey`, `message_tracking` via D1 with KV `msg:*` fallback); `workflow_run` keys on the workflow id while `check_run` keys on check name + commit SHA (falling back to the run id) so providers that emit a fresh run id per phase — e.g. Cloudflare Pages — still edit the same message
|
||||||
- Record every dispatch attempt to D1 `send_logs` (route id, event, target, ok/error, duration, error code)
|
- Record every dispatch attempt to D1 `send_logs` (route id, event, target, ok/error, duration, error code)
|
||||||
- Aggregate delivery metrics (`server/lib/observability/metrics.ts`) from `send_logs` — totals, ok/failed counts + failure rate, per-platform/per-event/per-status breakdowns, average duration and attempts, recent failures; `getDeliveryMetrics(db, groupId?)` scopes every query by `group_id` when a group is passed
|
- Aggregate delivery metrics (`server/lib/observability/metrics.ts`) from `send_logs` — totals, ok/failed counts + failure rate, per-platform/per-event/per-status breakdowns, average duration and attempts, recent failures; `getDeliveryMetrics(db, groupId?)` scopes every query by `group_id` when a group is passed
|
||||||
- Expose admin observability endpoints — `GET /admin/api/metrics?groupId=` (delivery metrics, optional group scope; recent failures group-scoped for non-super) and `GET /admin/api/delivery/:deliveryId` (all send-log attempts for one delivery, group-scoped) — through the `/admin/api/[...slug]` catch-all route (`server/routes/admin/api/[...slug].ts`) that wires every admin API handler to its method+path
|
- Expose admin observability endpoints — `GET /admin/api/metrics?groupId=` (delivery metrics, optional group scope; recent failures group-scoped for non-super) and `GET /admin/api/delivery/:deliveryId` (all send-log attempts for one delivery, group-scoped) — through the `/admin/api/[...slug]` catch-all route (`server/routes/admin/api/[...slug].ts`) that wires every admin API handler to its method+path
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ GitHub / Gitea webhook → Discord / Telegram / Feishu dispatcher. Receives webh
|
||||||
- Combine filters into a boolean AST (`all` / `any` / `not` nodes) via the route editor's visual builder; reuse named filter fragments and dry-run any filter against a pasted JSON payload without storing it
|
- Combine filters into a boolean AST (`all` / `any` / `not` nodes) via the route editor's visual builder; reuse named filter fragments and dry-run any filter against a pasted JSON payload without storing it
|
||||||
- Rich messages with color coding, author avatars, fields, and timestamps — rendered as Discord embeds and Telegram HTML
|
- Rich messages with color coding, author avatars, fields, and timestamps — rendered as Discord embeds and Telegram HTML
|
||||||
- Route to Discord channels/threads, Telegram chats/topics, and Feishu group chats (multi-target routes)
|
- Route to Discord channels/threads, Telegram chats/topics, and Feishu group chats (multi-target routes)
|
||||||
- `workflow_run` / `check_run` progress is edited **in place** (single message updated as the run advances) on Discord, Telegram, and Feishu
|
- `workflow_run` / `check_run` progress is edited **in place** (single message updated as the run advances) on Discord, Telegram, and Feishu — `check_run` tracks by check name + commit SHA so deployments that issue a fresh run id per phase (e.g. Cloudflare Pages) still edit the same message
|
||||||
- **Per-group webhook log channel** — point a group at a Discord channel/thread, Telegram chat/topic, or Feishu chat and every webhook the group's routes dispatch is summarized there (✅/❌ per route × target)
|
- **Per-group webhook log channel** — point a group at a Discord channel/thread, Telegram chat/topic, or Feishu chat and every webhook the group's routes dispatch is summarized there (✅/❌ per route × target)
|
||||||
- GitHub OAuth for user actions (comment, edit comment, delete comment, merge, close, react)
|
- GitHub OAuth for user actions (comment, edit comment, delete comment, merge, close, react)
|
||||||
- **Web UI config console** (`/admin`) — manage routes and groups with GitHub OAuth + admin whitelist, view send logs
|
- **Web UI config console** (`/admin`) — manage routes and groups with GitHub OAuth + admin whitelist, view send logs
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ GitHub / Gitea webhook → Discord / Telegram / 飞书 分发服务。通过 Clo
|
||||||
- 在路由编辑器的可视化构建器中把过滤器组合成布尔 AST(`all` / `any` / `not` 节点);可复用命名过滤器片段,并可对粘贴的 JSON 载荷做无存储的试匹配
|
- 在路由编辑器的可视化构建器中把过滤器组合成布尔 AST(`all` / `any` / `not` 节点);可复用命名过滤器片段,并可对粘贴的 JSON 载荷做无存储的试匹配
|
||||||
- 富消息:颜色编码、作者头像、字段、时间戳——渲染为 Discord embed 与 Telegram HTML
|
- 富消息:颜色编码、作者头像、字段、时间戳——渲染为 Discord embed 与 Telegram HTML
|
||||||
- 路由到 Discord 频道/子区、Telegram 群组/话题与飞书群聊(一条路由可多目标)
|
- 路由到 Discord 频道/子区、Telegram 群组/话题与飞书群聊(一条路由可多目标)
|
||||||
- `workflow_run` / `check_run` 进度**原地编辑**同一条消息(运行推进时更新),Discord、Telegram 与飞书均支持
|
- `workflow_run` / `check_run` 进度**原地编辑**同一条消息(运行推进时更新),Discord、Telegram 与飞书均支持 —— `check_run` 以 check 名 + commit SHA 追踪,因此每个阶段都换新 run id 的部署(如 Cloudflare Pages)仍会编辑同一条消息
|
||||||
- **分组级 Webhook 日志频道** —— 为分组指定一个 Discord 频道/子区、Telegram 群组/话题或飞书群聊,该分组路由每次分发 webhook 都会向其中发送摘要(每条「路由 × 目标」一行,✅/❌ 结果)
|
- **分组级 Webhook 日志频道** —— 为分组指定一个 Discord 频道/子区、Telegram 群组/话题或飞书群聊,该分组路由每次分发 webhook 都会向其中发送摘要(每条「路由 × 目标」一行,✅/❌ 结果)
|
||||||
- GitHub OAuth 用户授权(评论、编辑评论、删除评论、合并、关闭、反应)
|
- GitHub OAuth 用户授权(评论、编辑评论、删除评论、合并、关闭、反应)
|
||||||
- **Web 配置控制台**(`/admin`)— 通过 GitHub OAuth + 管理员白名单管理路由与分组、查看发送日志
|
- **Web 配置控制台**(`/admin`)— 通过 GitHub OAuth + 管理员白名单管理路由与分组、查看发送日志
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ Any event type without a dedicated formatter falls through to the generic format
|
||||||
|
|
||||||
## In-Place Message Updates
|
## In-Place Message Updates
|
||||||
|
|
||||||
`workflow_run` and `check_run` events (queued → running → success/failure) are sent once and then **edited in place** for each subsequent phase instead of posting a new message. The original message's link preview, author, and field layout are preserved; only the status, conclusion emoji, duration, and title are refreshed. Supported on both Discord (`editMessage`) and Telegram (`editMessageText` / `editMessageCaption`).
|
`workflow_run` and `check_run` events (queued → running → success/failure) are sent once and then **edited in place** for each subsequent phase instead of posting a new message. The original message's link preview, author, and field layout are preserved; only the status, conclusion emoji, duration, and title are refreshed. Supported on both Discord (`editMessage`) and Telegram (`editMessageText` / `editMessageCaption`). `check_run` tracks by check name + commit SHA (falling back to the run id), so providers that issue a new run id per phase — e.g. Cloudflare Pages — still edit the same message.
|
||||||
|
|
||||||
## Filter Compatibility
|
## Filter Compatibility
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,4 +33,4 @@ With `Group.forgeSources` (a list of `{ host, type, name? }` entries) the messag
|
||||||
|
|
||||||
## In-Place Updates
|
## In-Place Updates
|
||||||
|
|
||||||
`workflow_run` and `check_run` messages are sent once and edited in place as the run progresses (queued → running → success/failure) — no duplicate messages. Tracking uses KV `msg:*` with a stable `updateKey` per run.
|
`workflow_run` and `check_run` messages are sent once and edited in place as the run progresses (queued → running → success/failure) — no duplicate messages. Tracking uses KV `msg:*` with a stable `updateKey` per run: `workflow_run` keys on the workflow id, while `check_run` keys on the check name + commit SHA (falling back to the run id) so deployments that emit a fresh run id per phase — e.g. Cloudflare Pages — still edit the same message.
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ WebHooker 支持 28 种 GitHub webhook 事件类型,每种都有专用的格
|
||||||
|
|
||||||
## 原地消息更新
|
## 原地消息更新
|
||||||
|
|
||||||
`workflow_run` / `check_run` 事件(queued → running → success/failure)只发送一条消息,后续每个阶段会**原地编辑**该消息,而不是发送新消息。消息的链接预览、作者和字段布局保持不变,仅刷新状态、结论 emoji、耗时和标题。Discord(`editMessage`)和 Telegram(`editMessageText` / `editMessageCaption`)均支持。
|
`workflow_run` / `check_run` 事件(queued → running → success/failure)只发送一条消息,后续每个阶段会**原地编辑**该消息,而不是发送新消息。消息的链接预览、作者和字段布局保持不变,仅刷新状态、结论 emoji、耗时和标题。Discord(`editMessage`)和 Telegram(`editMessageText` / `editMessageCaption`)均支持。`check_run` 以 check 名 + commit SHA 追踪(回退到 run id),因此每个阶段都会换新 run id 的提供方(例如 Cloudflare Pages)仍会编辑同一条消息。
|
||||||
|
|
||||||
## 过滤器兼容性
|
## 过滤器兼容性
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,4 +33,4 @@
|
||||||
|
|
||||||
## 原地更新
|
## 原地更新
|
||||||
|
|
||||||
`workflow_run` 与 `check_run` 消息只发送一次,随运行进度原地编辑(queued → running → success/failure),不会重复发消息。追踪使用 KV `msg:*` 与每次运行的稳定 `updateKey`。
|
`workflow_run` 与 `check_run` 消息只发送一次,随运行进度原地编辑(queued → running → success/failure),不会重复发消息。追踪使用 KV `msg:*` 与每次运行的稳定 `updateKey`:`workflow_run` 以 workflow id 为键,`check_run` 则以 check 名 + commit SHA 为键(回退到 run id),因此每个阶段都换新 run id 的部署(例如 Cloudflare Pages)仍会编辑同一条消息。
|
||||||
|
|
|
||||||
|
|
@ -178,6 +178,7 @@ export function formatCheckRun(
|
||||||
const checkRun = payload.check_run as {
|
const checkRun = payload.check_run as {
|
||||||
id?: number;
|
id?: number;
|
||||||
name?: string;
|
name?: string;
|
||||||
|
head_sha?: string;
|
||||||
conclusion?: string;
|
conclusion?: string;
|
||||||
html_url?: string;
|
html_url?: string;
|
||||||
details_url?: string;
|
details_url?: string;
|
||||||
|
|
@ -225,7 +226,12 @@ export function formatCheckRun(
|
||||||
url: checkRun.html_url,
|
url: checkRun.html_url,
|
||||||
color: GITHUB_COLORS[colorKey],
|
color: GITHUB_COLORS[colorKey],
|
||||||
fields,
|
fields,
|
||||||
updateKey: repo && checkRun.id != null ? `check_run:${repo}:${checkRun.id}` : undefined,
|
updateKey:
|
||||||
|
repo && checkRun.name && checkRun.head_sha
|
||||||
|
? `check_run:${repo}:${checkRun.name}:${checkRun.head_sha}`
|
||||||
|
: repo && checkRun.id != null
|
||||||
|
? `check_run:${repo}:${checkRun.id}`
|
||||||
|
: undefined,
|
||||||
},
|
},
|
||||||
t,
|
t,
|
||||||
repo,
|
repo,
|
||||||
|
|
|
||||||
|
|
@ -183,6 +183,39 @@ describe("message title spec", () => {
|
||||||
expect(msg.updateKey).toBeUndefined();
|
expect(msg.updateKey).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("check_run keys on name + head_sha for stable in-place edits", () => {
|
||||||
|
const running = formatEvent(
|
||||||
|
route,
|
||||||
|
event("check_run", {
|
||||||
|
check_run: {
|
||||||
|
id: 42,
|
||||||
|
name: "Cloudflare Pages",
|
||||||
|
head_sha: "abc123def456",
|
||||||
|
status: "in_progress",
|
||||||
|
conclusion: null,
|
||||||
|
},
|
||||||
|
repository: repo,
|
||||||
|
sender,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
const failed = formatEvent(
|
||||||
|
route,
|
||||||
|
event("check_run", {
|
||||||
|
check_run: {
|
||||||
|
id: 43,
|
||||||
|
name: "Cloudflare Pages",
|
||||||
|
head_sha: "abc123def456",
|
||||||
|
status: "completed",
|
||||||
|
conclusion: "failure",
|
||||||
|
},
|
||||||
|
repository: repo,
|
||||||
|
sender,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
expect(running.updateKey).toBe("check_run:acme/widget:Cloudflare Pages:abc123def456");
|
||||||
|
expect(failed.updateKey).toBe(running.updateKey);
|
||||||
|
});
|
||||||
|
|
||||||
it("check_suite shows conclusion, service, branch and commit", () => {
|
it("check_suite shows conclusion, service, branch and commit", () => {
|
||||||
const suite = {
|
const suite = {
|
||||||
html_url: "https://github.com/acme/widget/runs/2",
|
html_url: "https://github.com/acme/widget/runs/2",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue