Dependabot:从更新发现、私有源到受控合并
机器人没有报错,依赖为什么半年没动
一个仓库同时放着根目录的 Node.js 服务、/services/billing 下的 Maven 服务,以及若干 GitHub Actions workflow。安全看板已经出现漏洞告警,团队也在仓库设置里打开了 Dependabot,但等待数轮后,只有根目录偶尔出现版本更新 PR:billing 服务没有动静,安全 PR 也没有出现。更糟的是,CI 在人工重跑后可以读取制品库,Dependabot 的更新任务却报私有源认证失败。
这不是一个“机器人开没开”的问题,而是四条彼此独立的链路被混在了一起:Version Updates 按仓库配置和调度发现常规新版本;Security Updates 由默认分支上的漏洞告警触发;Dependabot 更新任务使用自己的私库凭据;Dependabot PR 触发的 Actions 又运行在另一套令牌和 Secret 边界内。任意一段缺失,表面都可能只是“没有 PR”。
Dependabot 是 GitHub 管理的产品能力,不需要在仓库里安装常驻 Dependabot 服务。更新 Job 默认运行在 GitHub Actions 的标准 GitHub-hosted runner,也可以按平台能力选择 larger runner、私有仓库专用的 self-hosted runner 或受支持的私网连接;把 Job 放到自托管 runner 并不等于获得一个可独立升级的自托管 Dependabot 服务。真正的启用动作发生在两处:有仓库写权限的人把 .github/dependabot.yml 提交到默认分支,由此启用 Version Updates;Security Updates 则要求 Dependency graph、Dependabot alerts 和 Dependabot security updates 在仓库或组织策略中处于启用状态。部分仓库类型可能已经由平台或组织默认启用其中某些能力,因此接入时应核对实际状态和上层策略,而不是假定必须逐个手工打开。GitHub 对配置文件位置和两类更新的关系有明确说明:没有配置文件仍可能产生安全更新,但不会产生受调度控制的版本更新,详见关于 dependabot.yml与配置 Security Updates。
Version Updates 与 Security Updates 不是同一条定时任务
Version Updates 解决“已经有新版本,但当前版本未必存在已知漏洞”的维护问题。Dependabot 读取默认分支上的配置,根据每个 updates 块声明的生态、目录与 schedule 发起检查,解析 manifest 和 lockfile,过滤候选,再创建版本更新 PR。修改配置文件本身也会触发一次检查,所以首次接入时可能很快看到任务状态,但 PR 是否出现仍取决于候选、冷却、忽略规则和并发上限。
Dependabot 服务没有让仓库选择的独立产品版本号,GitHub Cloud 按滚动服务演进;GitHub Enterprise Server 则必须按实例发行线核对 Dependabot、Actions 与支持生态,并由站点管理员先完成企业级启用。dependabot.yml 的 version: 2 只是配置语法版本,不能拿来证明执行器版本。开源的 dependabot-core 是 MIT 许可的核心库,但 GitHub 托管服务的可用性、SLA、计费和数据边界受 GitHub 产品方案与条款约束;“核心库可自建”不等于 GitHub 服务本身是一个按 MIT 交付的自托管产品。需要自定义执行器时,应把 fork 的 core commit、容器身份、代理和凭据隔离另立运维基线,不能继续套用 GitHub 托管能力的承诺,边界见dependabot-core 官方仓库与GitHub Enterprise Server 的 Dependabot 启用说明。
Security Updates 解决的是“默认分支依赖图中的某个版本命中安全公告,并且存在可生成的修复方案”。它由 Dependabot alert 驱动,不等待 Version Updates 的 schedule。合并安全更新 PR 后,相应告警才可能变为已解决。它也不是“所有漏洞都必然自动出 PR”:若没有兼容修复版本、锁文件无法解析、私有依赖不可达、更新超过约束或已有 PR 上限,告警可以存在而自动修复失败。GitHub 在Dependabot PR 的触发说明中区分了这两条链。
两者会复用一部分 .github/dependabot.yml 自定义项,例如标签、分组、私有 Registry 与开放 PR 上限,但并非所有字段都共享语义。最容易踩坑的是 target-branch:Version Updates 可以把 PR 指向非默认维护分支;安全更新默认针对默认分支,而且为某个更新块设置 target-branch 后,该块的自定义通常只作用于版本更新。需要让 YAML 自定义参与安全更新时,目录必须能匹配默认分支上对应 manifest,并且不要给这个块设置 target-branch。schedule 和 cooldown 同样不能用来推断安全修复何时发生:安全更新不按版本更新计划触发,冷却也不阻止安全更新。
所以,看到漏洞告警而没有 PR 时,第一问不是“定时任务跑了吗”,而是“Security Updates 是否启用、告警是否可修复、默认分支上的依赖是否可解析”;看到普通依赖陈旧而没有 PR 时,才沿 Version Updates 的配置、调度、候选过滤和 PR 容量排查。
先让两个真实目录被正确发现
下面以一个可公开创建的测试仓库为实验对象。仓库需要启用 GitHub Actions;操作者需要仓库写权限,若要改安全功能、Secret、保护规则或自动合并设置,还需要对应的仓库管理权限。实验依赖放在公开 Registry,不需要真实业务凭据,也不要在共享生产仓库故意制造漏洞。
先准备这样的最小结构:
dependabot-lab/
├─ .github/
│ ├─ dependabot.yml
│ └─ workflows/
│ └─ ci.yml
├─ package.json
├─ package-lock.json
└─ services/
└─ billing/
└─ pom.xml先放入可直接解析的最小文件。版本只是实验基线;若执行时已经没有更高候选,任务成功但不产生 PR 也是有效证据,不要为了制造 PR 改用已知漏洞版本。
{
"name": "dependabot-lab",
"private": true,
"scripts": { "test": "tsc --version" },
"devDependencies": { "typescript": "5.6.3" }
}<!-- services/billing/pom.xml -->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>example.lab</groupId>
<artifactId>billing</artifactId>
<version>1.0.0</version>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.16</version>
</dependency>
</dependencies>
</project># .github/workflows/ci.yml
name: dependency-lab
on:
pull_request:
jobs:
verify:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "22"
cache: "npm"
- run: npm ci
- run: npm test
- run: mvn -B -f services/billing/pom.xml test在根目录运行 npm install --package-lock-only --ignore-scripts 生成 package-lock.json,再执行 npm ci && npm test 与 mvn -B -f services/billing/pom.xml test,确认基线可以解析后一起提交。这里没有为 Maven 伪造锁文件:Maven 的解析模型与 npm lockfile 不同,更新 PR 应以 POM 差异、解析日志和 CI 结果为证据。
将下面配置保存为默认分支的 .github/dependabot.yml:
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
day: "tuesday"
time: "03:30"
timezone: "Etc/UTC"
open-pull-requests-limit: 4
cooldown:
semver-major-days: 14
semver-minor-days: 7
semver-patch-days: 3
groups:
npm-production-minor-patch:
dependency-type: "production"
update-types:
- "minor"
- "patch"
npm-development:
dependency-type: "development"
patterns:
- "*"
- package-ecosystem: "maven"
directory: "/services/billing"
schedule:
interval: "weekly"
day: "wednesday"
time: "03:30"
timezone: "Etc/UTC"
open-pull-requests-limit: 2
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 2version: 2 是配置语法版本,不是 Dependabot 产品版本。每个 updates 项都是一条独立更新任务定义;package-ecosystem 决定使用哪个解析器,directory 决定从仓库根目录起去哪里找该生态的 manifest。目录写成 /services/billing 不会递归替代所有同类服务;多目录可以使用 directories,但同一个生态块中的 directory 与 directories 不能同时出现。支持哪些生态、每种生态识别哪些文件会变化,接入时应从支持的生态与仓库核对,而不是根据文件扩展名猜测。
schedule 控制 Version Updates 何时检查。上例把 npm 与 Maven 错开,避免两个解析器和两组 CI 同时抢资源;时区显式写出,避免团队把 UTC 误读成本地时间。当前配置参考还支持更多间隔形式及对应字段,使用 cron 等模式时应直接按Dependabot 配置项参考组合必需字段。
cooldown 把刚发布的常规版本暂时挡在候选之外。它减少上游撤回版本或快速补丁造成的抖动,却会增加普通修复进入仓库的等待时间;安全更新不受这里的冷却期阻挡。不要依赖平台默认冷却值,把组织接受的 major、minor、patch 等待策略显式写进模板,并给紧急漏洞保留 Security Updates 与人工升级通道。
groups 在单一生态内把多个候选装进一个 PR。匹配顺序有意义:一个更新只进入它命中的第一个组,因此规则要从窄到宽。上例先收生产 minor/patch,再用通配符接住开发依赖;major 生产更新不会被第一个组接纳,会保持独立 PR,便于安排迁移评审。分组降低 PR 数,却扩大一次回滚的变更集合;耦合组件适合一起升,互不相关的核心库不应只为“看起来整洁”塞进同组。
open-pull-requests-limit 是该生态更新块的在途容量,不是每轮创建数量,也不是全仓库共享总额。达到上限后,任务仍可能完成候选计算,但新 PR 要等待旧 PR 合并或关闭。把它设为 0 可以停掉该块的 Version Update PR;若 Security Updates 已启用,这并不等于关闭安全更新。容量不能只按机器人吞吐决定,还要按 reviewer、CI 队列和回滚能力决定。
正向实验:从配置提交观察到三条更新链
为了让实验可重复,先在测试仓库中把 npm、Maven 与一个公开 Action 固定到各自当前仍可解析、但存在更新候选的旧版本。具体版本不要从文章照抄:实验当天从对应 Registry 和 Action 发布页选择一个仍受支持的旧版本,避免把已删除版本或真实漏洞带入长期样例。
提交配置后,在仓库的 Dependency graph 中打开 Dependabot 状态页。对每个 manifest 手动触发一次检查,或等待各自计划运行。预期先出现三类中间证据:
状态页列出 npm、Maven 和 GitHub Actions 对应的 manifest/任务,而不是只显示仓库级“已启用”。Recent jobs 中能看到版本更新任务,日志包含 manifest 发现、依赖查询、候选过滤以及 PR 创建或不创建的原因。若确有满足约束且已过冷却期的候选,Pull requests 中出现 dependabot/* 分支;npm 分组 PR 应同时修改同组依赖及锁文件,Maven 与 Actions 更新保持各自独立。
若候选刚发布,正确结果可能是日志显示候选被 cooldown 延后,而不是立即有 PR。若所有依赖本来就是最新版本,正确结果是任务成功但不创建 PR。这个区别很重要:任务成功、候选存在、PR 可创建是三个状态,不能把“没有 PR”直接判成服务故障。
实验记录应保存 job ID、对应 manifest、结果摘要、PR URL 和检查结论,不要复制包含私有包名、内部路径或认证端点的完整日志到公共工单。GitHub 的Dependabot job logs说明了任务类型、关联 PR、错误摘要和完整日志之间的关系。
反向实验:目录写错时,成功运行也发现不了服务
在实验分支把 Maven 块临时改成:
- package-ecosystem: "maven"
directory: "/service/billing"
schedule:
interval: "weekly"这里故意把 services 写成 service。合并到默认分支并触发检查后,预期证据不是 Java 编译失败,而是 Dependabot 在对应位置找不到受支持的依赖文件,状态页或 job log 指向 manifest 目录错误;也不会出现 billing 更新 PR。根目录 npm 任务仍可正常工作,这证明一个 updates 块失败不会自动说明整份配置完全失效。
修复时恢复 /services/billing,再次触发检查。再验证不能只看错误消失:日志应重新列出 pom.xml 的依赖解析,若存在合格候选才应创建 PR。若恢复后仍无 PR,继续检查候选是否被版本约束、ignore、cooldown 或开放 PR 上限过滤,而不是反复改 schedule。
还可以做一个纯配置反例:把同一更新块同时写上 directory 和 directories,或给 schedule 使用不支持的字段组合。预期配置校验拒绝该声明,并在 Dependabot 状态中给出解析错误。修复后应看到新的 job 读取已提交的默认分支配置;只在功能分支修好 YAML 不会改变线上 Dependabot 的任务定义。
target-branch 是维护分支策略,不是安全补丁路由器
维护旧发行线时,可以为 Version Updates 单独增加一块:
- package-ecosystem: "npm"
directory: "/"
target-branch: "release-maintenance"
schedule:
interval: "monthly"
open-pull-requests-limit: 1
allow:
- dependency-type: "direct"
update-types:
- "version-update:semver-patch"这个块的 manifest 仍从目标分支读取,版本 PR 指向 release-maintenance。它适合仍在维护但不再接收常规功能的发行线,代价是团队要为该分支维护独立 CI、约束和回滚能力。不要据此期待 Dependabot 把默认分支的安全更新自动路由到维护分支。Security Updates 围绕默认分支上的 alert 工作;旧发行线的补丁回传需要明确的发布支持制度,可以由默认分支修复后 cherry-pick,也可以人工创建受审查的维护分支升级。
同一生态、同一目录如果建立多个目标分支块,要把 PR 容量和 CI 成本按分支分别计算。否则一个低价值维护 PR 也可能占住 runner,拖慢默认分支的安全修复。版本策略不是越自动越好;当维护分支缺少持续测试、制品发布或值班 owner 时,机器人只能制造“有人提 PR”的错觉。
私有 Registry 要同时接通定义、引用和凭据
私有依赖更新需要三层对象协作:顶层 registries 定义如何访问某个 Registry;具体 updates 块通过 registries 列表授权该解析任务使用哪些定义;用户名、密码或 token 存在 Dependabot secrets 中,由 ${{secrets.NAME}} 引用。不要把值写进 YAML,也不要误用 Actions secrets 替代 Dependabot secrets。
GitHub Packages 与第三方 Registry 还要分开。对 GitHub Packages 和 Container registry,包设置中的 “Manage Actions access” 可以把运行 Dependabot 的仓库授予 Read,随后 Dependabot 用自己的 GITHUB_TOKEN 请求 packages: read,不再需要在 YAML 中放 PAT 型 registry 定义;Artifactory、Nexus、Azure Artifacts 等第三方源仍需要 registries 与 Dependabot secret。这个自动授权只覆盖被显式授予仓库读取权的包,不是组织内全部私包通行证,行为见GitHub 托管 Registry 的自动访问。
下面示例使用保留域名,不对应真实服务:
version: 2
registries:
private-npm:
type: "npm-registry"
url: "https://packages.example.invalid"
token: "${{secrets.DEPENDABOT_NPM_READ_TOKEN}}"
updates:
- package-ecosystem: "npm"
directory: "/"
registries:
- "private-npm"
schedule:
interval: "weekly"
open-pull-requests-limit: 3先在测试仓库的 Dependabot Secret 存储中创建 DEPENDABOT_NPM_READ_TOKEN,令牌只授予读取实验命名空间和元数据所需权限;更新机器人通常不需要发布、删除包或管理用户。若使用组织级 Dependabot secret,要把可访问仓库收敛到明确集合。GitHub 对 Secret 引用格式、组织可见性和私有源配置的说明见配置 Dependabot 访问私有 Registry。
私有 Registry 接通不只意味着“能下载目标包”。某些包管理器为了计算新 lockfile,必须解析完整依赖图;任意一个私有传递依赖不可读,整个更新任务都可能失败。网络还要允许 GitHub 托管执行环境访问 Registry;若 Registry 只在私网开放,需要评估 GitHub 当前支持的自托管 runner 路径、代理与防火墙策略,而不是把服务暴露到公网来迎合机器人。
反向实验:错误 Secret 应怎样留下证据
在测试 Registry 或隔离账号中创建一个明确无读取权限的令牌,把 Dependabot secret 临时替换为该值,再触发 npm 更新任务。预期 Recent jobs 失败,错误摘要落在 Registry 认证或可达性类别;GitHub 的Dependabot errors列出了 private_source_authentication_failure、private_source_not_reachable、超时与证书失败等可区分证据。
这四类错误的修法不同。认证失败检查 secret 名称、令牌权限和 Registry 账号状态;不可达检查 URL、DNS、防火墙与私网边界;超时检查网络路径和 Registry 容量;证书失败应修证书链与主机名,不能关闭 TLS 校验。日志中若显示公开 Registry 可查、私有包失败,就不要先改 manifest 版本约束。
恢复正确的只读 Secret 后再次运行,预期错误类别消失并进入依赖解析。随后立即撤销实验令牌,而不是把故意错误的凭据留作长期测试。真实轮换采用“新令牌创建并验证、Secret 原位替换、旧令牌撤销、再跑一次更新任务”的顺序,避免先撤旧令牌造成全仓库停摆。
自托管 runner 只接管执行环境,不接管产品控制面
私有 Registry 只在内网可达时,可以让 Dependabot 更新 Job 使用 self-hosted runner。当前要求是 Linux x64、Docker 可用,并给 runner 配置 dependabot 或组织指定标签;启用 “Dependabot on self-hosted runners” 后,更新 Job 只等待匹配 runner,不会自动回退到标准 GitHub-hosted runner。先开设置却没有在线匹配 runner,预期证据是 Job 长期排队,而不是 Registry 认证失败。配置入口和队列行为见配置 Dependabot self-hosted runner与Dependabot runner 选择。
runner 需要访问 GitHub、公开版本源和获准的内部 Registry,并允许出站访问 dependabot-actions.githubapp.com;自签 CA 既要进入操作系统信任,也要配置给 Node.js。网络策略只开放必要主机、端口和内部命名空间,runner 使用短生命周期实例或每次清理工作目录与 Docker 资源,不与可执行不受信 PR 的普通 runner 混池。反向网络实验可以临时阻断测试 Registry:预期 manifest 仍被调度,但 Job 在连接阶段失败;恢复规则后同一任务应进入解析。不要在生产 runner 上故意阻断 GitHub 控制端,也不要靠关闭 TLS 校验制造“成功”。
标准 GitHub-hosted runner 与 self-hosted runner 上的 Dependabot 更新 Job 不计入 Actions included minutes,larger runner 按常规费率计费,但并发槽位、内部带宽、Registry QPS、缓存、宿主机和后续 PR CI 仍是实际容量。若一台 VM 承载多个并发 runner,应依据真实生态的 CPU、内存、磁盘与 Docker 网络消耗压测;官方给出的机器示例只能作为起点,不能变成全公司的容量常数。
外部代码执行是凭据边界,不是兼容性开关
部分生态在解析 manifest 时可能需要执行外部代码。配置了私有 Registry 后,Dependabot 会为降低凭据被恶意包或 manifest 代码读取的风险而禁用这类执行;某些项目因此无法完成版本更新。支持的生态可以显式加入:
insecure-external-code-execution: "allow"字段名已经清楚说明代价:这会让更新过程执行 manifest 相关代码。即使 GitHub 对可访问 Registry 做了约束,受污染的依赖仍可能利用执行环境尝试窃取当前更新块可用的凭据或访问网络。配置参考会动态列出支持该选项的生态与精确行为,启用前应核对insecure-external-code-execution 参考。
正确决策顺序是先消除不必要的动态 manifest 逻辑、预生成稳定锁文件或把私有源凭据缩成只读且只覆盖单一命名空间;仍无法解析时,再在隔离测试仓库验证开启后的实际调用和网络访问。不要给所有更新块全局放开,也不要复用拥有发布权限的 CI token。若风险无法接受,保留人工更新或在更可控的内部构建环境生成更新,比让托管任务执行不受信代码更诚实。
GitHub Actions 更新的是远程引用,不是任意 YAML 字符串
package-ecosystem: "github-actions" 让 Dependabot 检查 .github/workflows 中的 Action 引用和被调用的 reusable workflow Git 引用。directory: "/" 表示从仓库的 workflow 位置工作,不是让机器人扫描所有 YAML 中看似版本号的文本。当前支持与限制应从使用 Dependabot 更新 Actions和支持的生态核对;本地 action、某些 docker:// 写法或自定义拼接引用不能因为文件在 .github 下就假定会更新。
一个可被发现的 workflow 引用如下:
name: CI
on:
pull_request:
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: ".nvmrc"
cache: "npm"
- run: npm ci
- run: npm test生产仓库若要求 Action 固定到完整 commit SHA,Dependabot 更新 PR 应改变 SHA,并由 reviewer 核对同一发布轨道、上游仓库和变更说明;不要为了让机器人容易识别而退回可移动分支。Action 升级改变的是 CI 供应链代码,风险可能高于普通开发依赖:它能读取 checkout 内容、job token 和允许的 Secret,还可能影响制品发布。因此 Actions 更新通常应独立分组、运行完整 CI,并由平台 owner 审核。
Dependabot PR 触发 Actions 时使用受限安全模型。对由 dependabot[bot] 触发的常见事件,GITHUB_TOKEN 默认只读,普通 Actions secrets 不可用,workflow 读取的是 Dependabot secrets 安全域。这个行为常造成“人工 PR 能过,Dependabot PR 登录私库失败”。不要把 pull_request_target 与不受信 PR 代码组合来绕过限制;先按Dependabot on GitHub Actions把只读构建凭据放入正确 Secret 域,并让 workflow 只申请实际需要的 permissions。
PR 只是候选载体,分支保护才决定能不能进入主线
Dependabot 创建 PR 后,GitHub 仍按普通 PR 处理 required status checks、required reviews、CODEOWNERS、签名、线性历史和规则集。机器人作者身份只说明谁提出变更,不是合并授权。安全底线应放在目标分支保护中,而不是依赖某个 workflow “自觉先跑测试”。
Dependabot 默认会签署自己创建的提交,即使仓库没有要求签名。验收时要打开 PR 的 Commits 视图确认提交显示预期的 dependabot[bot] 与 Verified,再用规则集要求 signed commits 做一次真实阻断测试;不能仅根据 PR 作者或分支名前缀推断签名。平台签名证明 GitHub 验证了提交身份,不证明上游版本安全、lockfile 无恶意脚本或 CI 已覆盖兼容性,边界见Dependabot security updates。
一个稳妥的起点是:默认分支要求构建、单元测试、锁文件一致性与供应链扫描通过;高风险目录由 CODEOWNERS 审核;禁止机器人绕过规则;自动合并只对明确允许的 patch/minor 类型开启。GitHub 官方的使用 Actions 自动化 Dependabot也强调,依赖 status checks 时应把它们设为目标分支的 required checks。
下面 workflow 只在 Dependabot PR 上读取元数据,并对 patch 更新申请 auto-merge;它不直接绕过保护规则:
name: Dependabot auto-merge
on:
pull_request:
permissions:
contents: write
pull-requests: write
jobs:
enable-auto-merge:
if: >-
github.event.pull_request.user.login == 'dependabot[bot]' &&
github.event.pull_request.base.ref == github.event.repository.default_branch
runs-on: ubuntu-latest
steps:
- name: Read Dependabot metadata
id: metadata
uses: dependabot/fetch-metadata@v2
with:
github-token: "${{ secrets.GITHUB_TOKEN }}"
- name: Enable auto-merge for patch updates
if: steps.metadata.outputs.update-type == 'version-update:semver-patch'
env:
GH_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
PR_URL: "${{ github.event.pull_request.html_url }}"
run: gh pr merge --auto --squash "$PR_URL"为了便于实验,上例使用官方 Action 的 major tag;生产应根据组织供应链规则把 dependabot/fetch-metadata 固定到已审查的完整 commit SHA,并让 github-actions 更新块维护该引用。contents: write 与 pull-requests: write 只授给这个 job,不使用 write-all。过滤条件同时校验 PR 作者与目标分支,避免仅凭可伪造标签触发。
auto-merge 的含义是“检查和评审满足后由平台合并”,不是 workflow 执行到这一步就立即写入默认分支。若 required checks 配错名称、没有设为 required,失败测试仍可能不阻止自动合并;若规则要求人工 review,机器人也必须等待 review。反向验证应在实验 PR 中故意让一个 required test 失败:预期 auto-merge 可以处于已申请状态,但 PR 保持不可合并;修复测试后重新运行,所有 required checks 通过才进入合并阶段。
Merge Queue 又多一层边界。启用队列后,PR 需要以目标分支最新队头状态重新验证,不能把普通 auto-merge 当成已经入队。GitHub 当前的使用 Actions 自动化 Dependabot说明,内置 GITHUB_TOKEN 不能把 PR 加入 merge queue;需要具备合并权限的 GitHub App token 或个人访问令牌替代。企业环境优先使用短期 GitHub App installation token,把权限限制在选定仓库和合并操作,并把“谁允许进入队列”与“谁创建更新 PR”分成两个身份。不要仅为排队给 Dependabot 或 CI 机器人分支保护绕过权。
从日志判断是发现、解析、创建还是 CI 失败
排障先确定失败发生在哪个状态,而不是先重跑所有 workflow:
默认分支配置被读取
-> 找到 ecosystem 与 manifest
-> 访问 Registry 并解析依赖图
-> 计算候选并应用 allow/ignore/cooldown/groups
-> 检查开放 PR 容量
-> 创建或更新 Dependabot 分支与 PR
-> Actions 执行 required checks
-> review / auto-merge / merge queue
-> 合并并重新计算依赖图Dependabot 状态页和 recent job logs 主要解释前半段;PR Checks 和 Actions 日志解释后半段。private_source_authentication_failure 属于 Registry 凭据,不应通过重跑 CI 修;npm ci 在 PR 上失败说明候选已经创建,应该检查 lockfile、脚本与 Dependabot PR 的 Secret 边界;PR 全绿却不能合并则查看 review、规则集、merge queue 与 token 权限。
日志也不是无限制的调试数据湖。完整解析日志可能暴露私有包名、依赖拓扑、Registry 主机和内部目录。共享到外部系统前做字段脱敏,监控标签不要直接复制仓库名、包名、漏洞编号和人员账号形成高基数数据。长期指标保留聚合后的任务成功率、从候选到 PR 的延迟、PR 年龄、失败类别、队列等待和每轮 CI 消耗;详细日志按最小访问与保留制度管理。
Fork、长期无人处理与“安静停摆”
Fork 即使从上游带来了 .github/dependabot.yml,Version Updates 也不会因此自动启用;fork owner 需要在自己的仓库设置中显式启用。这是避免复制仓库意外产生更新流量和 PR 的保护行为,见在 Fork 上启用 Version Updates。因此模板仓库或开源项目不能把“文件存在”当作所有下游 fork 已受维护的证据。
另一种停摆更隐蔽:仓库长期无人处理 Dependabot PR 时,GitHub 可能暂停新版本与安全更新,并停止为不活跃 PR rebase。平台会在 PR、设置或告警界面提示暂停;恢复动作可以是处理 PR、修改配置、手动触发更新或重新启用安全更新。暂停判定会随平台策略演进,治理规则应依据Dependabot updates stopped核对,不把某个天数写进监控脚本的永久假设。
所以“最近没有 PR”不能当健康指标。每个仓库至少要有更新 owner,定期检查每个 manifest 的最近任务结果、最老开放 PR、失败类别、是否暂停,以及安全告警是否存在可用修复却没有 PR。owner 离职、仓库归档或发布线冻结时,要显式转移责任或关闭能力,不能让平台以沉默代替决策。
容量、成本与治理从第一批 PR 就开始
Dependabot 管理了调度、版本查询与 PR 创建的产品控制面,但更新 Job 的 runner、后续 CI、review、制品下载、私有 Registry 流量和 merge queue 仍消耗团队容量。标准 GitHub-hosted 与 self-hosted 的 Dependabot 更新 Job 本身不计入 Actions included minutes,larger runner 会按 Actions 费率计费;PR 触发的普通 CI、团队自建 runner 和内部基础设施则按各自计费与容量制度承担。首次启用时,陈旧仓库可能同时产生大量候选;过高的 PR limit 会让 runner、reviewer 和缓存冷启动一起尖峰,过低又会让关键更新在队尾等待。分组减少固定开销,却会让失败定位和回滚更粗。
接入顺序应从一个生态、一个目录、低开放 PR 上限开始。先观察一次完整任务和一次真实 CI,再增加其他目录;Actions 更新与生产依赖分开;重大版本保持独立;有强耦合的 SDK/BOM/插件族才组在一起。容量基线关注趋势而非万能数字:开放 PR 是否持续增长,最老 PR 是否越来越旧,失败重试是否单调增加,runner 等待是否挤压业务 PR,Registry 请求是否触发限流。阈值由仓库 SLO、升级窗口和 runner 预算决定。
组织模板应规定配置 owner、允许生态、目录命名、默认调度错峰、分组原则、只读 Registry 凭据、Secret 轮换、required checks、自动合并允许类型和停摆告警。仓库级配置仍要允许业务团队表达维护分支和兼容窗口,但 open-pull-requests-limit: 0、长期 ignore、放开外部代码执行、扩大 Registry 凭据和新增自动合并 workflow 应进入评审。
更新策略也需要审计。Dependabot PR 评论命令可以快速 ignore 某个依赖,但这类偏好可能保存在平台侧而不显眼;多人仓库更适合把 ignore 原因写回 YAML 或关联决策记录,并设置复查信号。否则机器人“正常运行”,被忽略的关键依赖却永久停在旧版本。
从其他机器人迁入时先避免双写
从 Renovate、自建脚本或另一个 Dependabot 策略迁入时,不要先全量打开新工具。选择一个生态做影子核对:记录旧工具当前开放 PR、分组和忽略规则,把等价策略转换成 dependabot.yml,但先用 open-pull-requests-limit: 0 阻止 Version Update PR。Security Updates 是否继续由 GitHub 负责要单独决定,因为这个开关不随 limit 一起关闭。
核对 manifest 发现、候选版本、私库访问和目标分支后,先停旧工具在该生态的 PR 创建,再把 limit 调到受控值。关闭或标记旧 PR,避免两个机器人对同一依赖反复 rebase;随后运行完整 CI,比较锁文件 diff 和更新粒度。Renovate 的 datasource、package rules 和跨生态分组不一定能一对一翻译成 Dependabot,无法保持原子性的升级单元要么改成人工编排,要么保留更适合的工具,不能靠名称相似强行迁移。
迁移完成的证据不是“新机器人提了一个 PR”,而是每个受管 manifest 都有最近成功任务,旧集成不再创建分支,私库旧凭据已经撤销,保护规则继续阻止失败更新,积压趋势没有因为分组或 limit 改变而失真。
暂停、回滚与彻底退出是三种动作
临时冻结某个生态的 Version Updates,可把对应块的 open-pull-requests-limit 设为 0 并提交到默认分支;保留配置能让冻结原因和恢复 diff 可审查。冻结不应顺手关闭 Alerts,也不会自动关闭 Security Updates。只忽略某个不兼容版本时,用精确 ignore 条件并记录解除信号,避免把所有后续安全修复一起挡住。
配置变更造成 PR 风暴或错误分组时,先回滚默认分支上的 .github/dependabot.yml,关闭错误 PR,再触发一次更新任务验证旧策略恢复。若 PR 已经合并,回滚依赖变更要按该生态恢复 manifest 与 lockfile,并重新跑 required checks;仅关闭 Dependabot 不会撤销已经进入主线的版本。
彻底退出 Version Updates 时删除 .github/dependabot.yml。这不会自动关闭 Dependabot security updates、Dependabot alerts 或 dependency graph;它们在仓库或组织安全策略中分别管理。若替换为其他机器人,先停止 Dependabot 的 PR 创建并处理现有 dependabot/* PR,再启动新工具,避免双写。最后撤销 Dependabot 私库 Secret 对应的 Registry token、删除不再需要的自动合并 workflow 或身份、清理实验分支,并保留不含敏感值的配置摘要和退出审批。一次性实验仓库还应撤销协作者与 App 授权后删除或归档,不能把故意错误的配置和测试身份留成无人维护资产。
退出验收要跨过一个完整更新窗口:Dependabot 不再创建或更新 Version Update PR,新工具只创建预期候选,Security Updates 的保留或关闭状态与决策一致,旧 token 在 Registry 审计中不再被使用,默认分支的 required checks 和 review 规则没有因机器人退场被削弱。删除一个 YAML 文件很容易,证明没有遗留身份、重复 PR 和失管漏洞才是完整退出。
