GitHub Actions scheduling
GitHub Actions scheduling 表示仓库把 workflow 触发事件、并发 namespace、cancel-in-progress 和 required check 可见性组织成可审计的运行调度合同。目标不是让每个事件只跑一次,而是让当前 PR head 获得正确且可观察的 gate,同时不让无关 PR、main、deploy 或手工任务被同一个并发键误取消。
- 运维
- 工具
目录
Definition
GitHub Actions scheduling 表示仓库把 workflow 触发事件、并发 namespace、cancel-in-progress 和 required check 可见性组织成可审计的运行调度合同。目标不是让每个事件只跑一次,而是让当前 PR head 获得正确且可观察的 gate,同时不让无关 PR、main、deploy 或手工任务被同一个并发键误取消。
它处理的是“为什么该运行被取消、重跑或缺失”的问题:同一 PR 的新提交、标题或 base metadata 重验、review-state 更新、mainline、发布、部署和手工 dispatch 是不同运行类别,不能仅因它们都来自同一仓库就共享取消语义。它不替代 Quality gates 对 merge-readiness 的定义,不替代 PR label release 对发布意图的约束,也不把普通测试失败、workflow_call 复用或发布业务逻辑归入调度问题。
这里必须区分两种策略:普通、可替换的 PR candidate 或 revalidation 可以在其 per-PR namespace 中用 cancel-in-progress: true 丢弃过期工作;required gate 则必须确保每一次已经排队的评估都不会被后续事件抢占。后者用 queue: max 保留一个运行中 run 和最多 100 个 pending run,且不要与 cancel-in-progress: true 组合。GitHub 按各 run 开始等待并发组的时间 FIFO 处理;实际开始时间可能变化,所以不要把 dispatch 时间当成严格顺序保证(GitHub Actions concurrency documentation)。
When to use
当 .github/workflows/*.yml 同时包含 pull_request、pull_request_target、push、merge_group、workflow_dispatch 或路径过滤,并且使用 concurrency 时,应先检查本 Topic。特别是 workflow 监听 edited、多类事件共用 group、运行被标记为 cancelled,或 required check 在当前 head 上缺失时,不能直接把问题归因于测试失败。
使用前必须同时核验当前 workflow YAML、对应的 live run history、branch protection 或 ruleset 中的 required checks,以及仓库本地 workflow contract。只有确认触发器、group 与取消关系确实导致错误调度时才修改;若 job 已实际执行并因代码或环境失败,或 filter 本就应排除该改动,则分别按测试、运行环境或项目业务合同处理。
Habits / defaults
- 先为每个 run 分类:新 candidate SHA、metadata revalidation、review-state 更新、mainline/release、deploy 和 manual dispatch。记录它的事件、head SHA、group、取消条件和 required-check 名称,再决定是否需要改 YAML(
projects/octo-rill.md、projects/isolappurr-usb-hub.md、projects/display-ambient-light-desktop.md)。 workflow_runsidecar 的订阅 allowlist 也是 scheduling contract:expected-success workflow names 必须与实际 top-levelname精确一致,通知 workflow 自身要显式排除;失败判断绑定conclusion == failure,不能把成功 skip 或 cancellation 当作 incident(projects/dockrev.md)。- 同一 PR 的
synchronize可以按 PR 取消过期 candidate;metadata-only 的edited重验必须使用与 candidate 分离的 namespace,避免取消仍在执行的当前 head gate。pull_request_review.edited是 review-state 事件,不能因同名 action 被误当成 PR metadata(projects/octo-rill.md、projects/codex-vibe-monitor.md)。 - required PR completion gate 默认只监听
opened、synchronize、reopened和ready_for_review,排除pull_request.edited。如果确实需要 metadata-only revalidation,应放入独立 workflow/namespace,显式防止 closed/merged PR 触发,并使用独立 check context;不要把edited直接加入 required completion gate。Dockrev 的release-completion-pr.yml与test_release_workflow_contract.py是 exact event-set contract 样本(projects/dockrev.md)。 - 对 ordinary candidate/revalidation workflow,只有“新 run 完全取代旧 run”时才使用 per-PR
cancel-in-progress: true。对Label Gate、release completion 等 required gate,使用 per-PRqueue: max并省略或显式设为false的cancel-in-progress;queue: max与cancel-in-progress: true语义冲突,GitHub 会拒绝该 workflow(projects/flux-purr.md、GitHub Actions concurrency documentation)。 - delayed required gate 在实际评估时必须用 runtime GitHub API 重新读取 PR labels,并把该 JSON snapshot 传给 validator;不能信任可能在排队期间过时的
github.event.pull_request.labels。对pull_request_target,这个读取保持在 trusted base checkout 与最小只读权限边界内,不能为读取 labels checkout 或执行不可信 PR 内容(projects/flux-purr.md)。 - 不同交付类别不能共享宽泛的可取消 group。PR preview 可以只在同一 PR 内取代旧 build;mainline、deploy、release 和手工 dispatch 必须有独立 namespace,并在没有可追溯补跑机制前保留在途工作(
projects/isolappurr-usb-hub.md、projects/display-ambient-light-desktop.md)。 - 检查 branch/path/event filters 和 required-check 名称是否仍能让当前 head 产生可见 gate。不得通过删除 required check、放宽保护规则或隐藏 workflow 来掩盖调度缺陷(
projects/codex-vibe-monitor.md、projects/kaisoumail.md)。 pull_request_target的 trusted base source、workflow permissions 和 checkout 边界是独立安全合同。隔离 metadata run 时只改 group 计算,不把未审查的 PR 内容带入有权限的执行路径(projects/octo-rill.md)。- rerun identity 必须保留
workflow_run.id与run_attempt,并让通知链接指向具体 run;不能只用 branch 或 head SHA 合并不同 attempt,否则维护者无法判断当前失败是否已被重跑(projects/dockrev.md)。 - 本地
actionlint1.7.12 尚不识别queue: max。在该工具支持前,把 GitHub runtime 与官方文档视为语法权威,保留针对 workflow 文本的项目测试,并跟踪 actionlint #687;不得为了通过旧 linter 删除 required-gate 的非抢占队列。
Reference implementations
projects/octo-rill.md:ci.yml、label-gate.yml和review-policy.yml同时展示 candidate、trusted metadata gate 与 review-state gate,是验证edited重验隔离规则的首个样本。projects/codex-vibe-monitor.md、projects/kaisoumail.md与projects/proxy-broker.md:都把 PR candidate 的edited放进可取消 per-PR group,适合作为后续逐仓库取证对象,而不是直接复制 OctoRill 的 YAML。projects/flux-purr.md:Validate PR labels与Release completion是 per-PR 的 required gates;它们以queue: max非抢占排队、在 trusted base checkout 中读取当前 labels API snapshot,说明 required gate 不能沿用可取消 candidate 的策略。projects/dockrev.md:release-completion-pr.yml用 trustedpull_request_targetcheckout,并以 exact event set 排除edited;项目 contract test 防止 required completion gate 在后合并元数据编辑时重新触发。projects/isolappurr-usb-hub.md:Pages workflow 把 PR、merge_group和 dispatch 放入全局pagesgroup,说明跨事件冲突与同 PR candidate 取消是不同问题。projects/display-ambient-light-desktop.md:手工 release build 的可取消 workflow/ref group 说明 manual dispatch 需要单独判断,不应套用 PR 规则。
Failure modes
- 把
edited与synchronize放进同一可取消 group,会让 metadata 重验取消仍有价值的当前 head candidate,随后只剩迟到或缺失的 required check。 - 把
pull_request.edited直接加入 required completion workflow,会让 PR 合并或关闭后的元数据编辑重新产生 required gate,造成错误、迟到或无法解释的合并状态;默认排除edited,确需重验时单独隔离其 workflow、namespace 和 check context。 - 把
queue: max与cancel-in-progress: true写在同一 concurrency block 会触发 GitHub workflow validation error;required gate 不能一边承诺 FIFO 等待、一边允许新 run 抢占在途工作。 - required gate 从 event payload 读取 labels,会在排队期间遗漏已经发生的 label 变更;应在 job runtime 从 GitHub API 获取并消费同一份 snapshot。
- 用全局 group 管理 PR build、main deploy 与 dispatch,会把不同风险等级的工作互相取消;反过来给所有 run 加唯一 group,又会丢失同 PR 新提交取代旧 candidate 的效率。
- 只看 workflow 是否触发、不查 run history 与 required checks,容易把取消、filter 排除和真实测试失败混为一类,导致修复落在错误层面。
- 只更新通知 workflow 的单个订阅项、不更新 checked-in allowlist 或 contract test,会让新增 expected-success workflow 静默漏报;应把 workflow classification、route 和 exclusion 同步维护(
projects/dockrev.md)。 - 为获得绿色状态而修改保护规则、required check、权限或
pull_request_targetcheckout,会破坏原有安全与 merge-readiness 合同;调度修复必须保持这些边界不变。 - 将一个仓库的 group 命名直接复制到另一个仓库,会忽略不同 trigger、发布面和本地 contract;每次同步前都应重新取证并补足项目测试。
- 为通过未支持语法的本地 linter 而移除
queue: max,会以工具兼容性换取 required gate 可被取消的回归;应保留 GitHub 官方文档、GitHub runtime 和仓库测试作为当前验证链路。
Evidence index
projects/codex-vibe-monitor.mdprojects/kaisoumail.mdprojects/octo-rill.mdprojects/proxy-broker.mdprojects/flux-purr.mdprojects/dockrev.md- GitHub Actions concurrency documentation
- actionlint issue #687
projects/isolappurr-usb-hub.mdprojects/display-ambient-light-desktop.mdskills/style-delivery/SKILL.md
相关内容
Codex Vibe Monitor
项目实践Codex Vibe Monitor 是面向自部署的 OpenAI 兼容代理观测工作台:它接入 /v1/* HTTP / WebSocket 流量,将调用留证、实时 SSE、历史统计、请求排障、上游账号池、forward proxy、价格目录和 SQLite 持久化收在同一套运营界面中。
display-ambient-light-desktop
项目实践Cross-platform desktop app for ambient lighting control: Tauri backend + a TS/SolidJS frontend, with Rust toolchain pinned. Evidence: src-tauri/, package.json, rust-toolchain.toml.
Dockrev
项目实践Dockrev 是 self-hosted Docker/Compose update manager。README.md 把根文档定位为快速入口,完整部署、使用、运维、排障和 API 文档在 docs-site/,这说明它是运维工具而不是普通 CRUD 管理台。
Flux Purr
项目实践Flux Purr 是 embedded firmware + React control console 的设备 mono-repo。README.md 将仓库组织为 firmware/、web/、tools/flux-purr-devd/、docs/specs/、docs/hardware/、.
IsolaPurr USB Hub
项目实践IsolaPurr USB Hub 是带 USB-C 上行、USB-C 下行和 USB-A 下行的有源 USB 集线器/供电模块。README.md 记录 CH334P、URB2405S、CH224Q、TPS55288、SW2303/IP6557、DC5025 输入和理想二极管 OR-ing 等硬件方案,说明这是硬件/固件/Web/文档一体的 device mono-repo。
OctoRill
项目实践OctoRill 把与 Ivan 相关的 GitHub 动态整理成适合持续阅读的工作区:发布更新、获星/关注动态、日报和 Inbox,发布内容还支持中文翻译和要点整理。README.md 同时指向 public docs-site、quick start、config、product 和 internal docs,说明它是产品化 Rust 服务 + React UI + 文档站,而不是一次性脚本。
Oidrune
项目实践Oidrune 是一个由运营者控制的 GitHub Actions 通知中继:它验证短期 GitHub OIDC 身份,只接受已批准来源的 workflow completion event,并把调用方提供的规范化正文投递到一个运营者控制的 Telegram destination。
Proxy Broker
项目实践Proxy Broker 是 standalone Rust service,用于加载 mihomo subscriptions,按 IP 构建带 probe/geo metadata 的代理池,按选择的 IP 开关本地 proxy listener sessions,并从同一 binary 提供 Bun-built operator web console。README.