Problem
generated dependency docs in .github/workflows/license-dependencies.yml runs on every pull request, with no scoping. It takes about nine minutes. Across the last twelve completed runs:
8m19s 8m36s 8m46s 9m5s 9m9s 9m37s 9m39s 9m43s 10m26s 10m35s 10m47s 16m55s
The job regenerates dependencies.md with tools/collect-dependencies and diffs it against the checked-in copy. That output is derived entirely from dependency manifests, so a pull request touching no manifest cannot change it, and the nine minutes buys nothing.
Two recent examples:
The job is also the slowest part of the Java dependency path, because collecting the Java runtime inventory shells out to bazel build for each registered component, which is why it needs Bazelisk, a JDK and a 1.4 GB Bazel cache.
Why this is not just a paths: filter
The obvious fix is on.pull_request.paths, but two things make that wrong here.
A skipped required check never reports, so branch protection blocks the merge unless a companion no-op job is added. That is avoidable complexity.
More importantly, the input set is wider than the manifest files. tools/collect-dependencies/java_deps.go builds each Java component's runtime_inventory.json through Bazel, so the inventory reflects the build graph, not only maven_install.json. Moving an artifact between compile and runtime deps in a Java component's BUILD.bazel can change the generated output without touching a single manifest. A naive path filter would skip that and let dependencies.md go stale silently, which is the exact failure the job exists to catch.
Scoping has to fail closed: when in doubt, run.
Proposal
Keep the job always running, so it always reports and branch protection is untouched, and gate only the expensive steps on a cheap scope decision computed from the merge base.
Relevant inputs, from reading the collector:
imports.yaml, dependencies.md, tools/collect-dependencies/**, the check script and this workflow
- Go:
go.mod, go.sum, vendor/**
- Rust:
Cargo.toml
- Java:
pom.xml, bazel-java-ci.json, and BUILD.bazel or *.bzl under a registered Java component root
- Python:
pyproject.toml, requirements*.txt
- Node:
pnpm-lock.yaml
- Helm:
Chart.yaml, Chart.lock
- Bazel:
MODULE.bazel, MODULE.bazel.lock, maven_install.json, .bazelversion
The Java component roots are discovered rather than hardcoded, by locating bazel-java-ci.json, so a new Java service is covered the day it is added. A BUILD.bazel change under a Go or Rust subtree, which is the #945 case, is correctly treated as irrelevant.
Always run in full for push to main, merge_group and workflow_dispatch, so the default branch and the merge queue are never scoped down. Only pull requests are scoped.
The decision belongs in a script under tools/ci/ with unit tests, not inline YAML, so the fail-closed behaviour is actually verifiable.
Problem
generated dependency docsin.github/workflows/license-dependencies.ymlruns on every pull request, with no scoping. It takes about nine minutes. Across the last twelve completed runs:The job regenerates
dependencies.mdwithtools/collect-dependenciesand diffs it against the checked-in copy. That output is derived entirely from dependency manifests, so a pull request touching no manifest cannot change it, and the nine minutes buys nothing.Two recent examples:
BUILD.bazel, undersrc/compute-plane-services/nvsnap/. Nogo.mod,go.sumorvendor/.The job is also the slowest part of the Java dependency path, because collecting the Java runtime inventory shells out to
bazel buildfor each registered component, which is why it needs Bazelisk, a JDK and a 1.4 GB Bazel cache.Why this is not just a
paths:filterThe obvious fix is
on.pull_request.paths, but two things make that wrong here.A skipped required check never reports, so branch protection blocks the merge unless a companion no-op job is added. That is avoidable complexity.
More importantly, the input set is wider than the manifest files.
tools/collect-dependencies/java_deps.gobuilds each Java component'sruntime_inventory.jsonthrough Bazel, so the inventory reflects the build graph, not onlymaven_install.json. Moving an artifact between compile and runtime deps in a Java component'sBUILD.bazelcan change the generated output without touching a single manifest. A naive path filter would skip that and letdependencies.mdgo stale silently, which is the exact failure the job exists to catch.Scoping has to fail closed: when in doubt, run.
Proposal
Keep the job always running, so it always reports and branch protection is untouched, and gate only the expensive steps on a cheap scope decision computed from the merge base.
Relevant inputs, from reading the collector:
imports.yaml,dependencies.md,tools/collect-dependencies/**, the check script and this workflowgo.mod,go.sum,vendor/**Cargo.tomlpom.xml,bazel-java-ci.json, andBUILD.bazelor*.bzlunder a registered Java component rootpyproject.toml,requirements*.txtpnpm-lock.yamlChart.yaml,Chart.lockMODULE.bazel,MODULE.bazel.lock,maven_install.json,.bazelversionThe Java component roots are discovered rather than hardcoded, by locating
bazel-java-ci.json, so a new Java service is covered the day it is added. ABUILD.bazelchange under a Go or Rust subtree, which is the #945 case, is correctly treated as irrelevant.Always run in full for
pushtomain,merge_groupandworkflow_dispatch, so the default branch and the merge queue are never scoped down. Only pull requests are scoped.The decision belongs in a script under
tools/ci/with unit tests, not inline YAML, so the fail-closed behaviour is actually verifiable.