From 94194a842d2e4902f530b567e8103650c016731d Mon Sep 17 00:00:00 2001 From: David Leong Date: Sat, 22 Aug 2026 01:46:49 +0000 Subject: [PATCH] ci: Watch cargo deps with dependabot MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dependabot watches `pip` and `github-actions` but not `cargo`, so a new `openjd-expr` / `openjd-model` / `openjd-sessions` release is only noticed when somebody goes looking. #335 is the worked example: the crates published, and picking them up was a manual chase for pins, `Cargo.lock`, and `THIRD-PARTY-LICENSES.txt`. Two groups, ordered: cargo-patch all patch bumps cargo-minor all minor bumps except openjd-* An `openjd-*` minor therefore matches no group and gets its own PR, which is the point: those crates are 0.x, where cargo treats a minor as breaking, and they carry the API surface these bindings wrap. That is not theoretical — openjd-expr 0.4.0 carried a breaking coercion change, and openjd-model 0.5.2 changed the job-side `StepScript` wire format. Everything else is grouped. `tokio`, `uuid` and `serde_json` are 1.x, where a minor is additive by cargo's own rules, so isolating those buys nothing; the same goes for the 0.x crates this package does not wrap (`pyo3`, `log`, `windows`) and for transitive `Cargo.lock` bumps, which `dependency-type: indirect` support means cargo dependabot will raise. Ordering matters and is load-bearing: dependabot places a dependency in the first group it matches, so an `openjd-*` patch still rides in `cargo-patch` and only minors reach the exclusion. Loosening the version requirements would not have helped. They are already permissive — `openjd-model = "0.6.0"` is `^0.6.0`, so `>=0.6.0, <0.7.0`, and a 0.6.1 satisfies it without an edit. `Cargo.lock` is what actually pins the build, and it is committed and is what CI resolves from. So a patch pickup needs a `cargo update` and a commit no matter how loose the requirement string is, and a `*` requirement would give up the reproducibility the lock exists for. The one manual step this leaves: `scripts/check_third_party_licenses.sh` renders crate versions out of `Cargo.lock` and hard-fails on any diff, so the `third_party_licenses` check is red on every cargo PR until someone pushes `scripts/check_third_party_licenses.sh --update` onto the branch. Dependabot cannot do that itself. The note is in `dependabot.yml` rather than only here so it is findable after this commit scrolls away. Testing: config parses as YAML and declares all three ecosystems. Routing was checked by simulating dependabot's documented rules (first matching group wins; unmatched dependencies get their own PR) over 19 cases covering openjd-* minors and patches, the 1.x and 0.x direct deps, two transitive crates, and majors — all 19 route as intended. Against the previous single-group config the same check fails 10 cases, including `tokio`/`uuid`/`serde_json` minors, which confirms the second group is load-bearing. Dependabot itself cannot be run locally, so the schedule and the real grouping behaviour are unverified until it runs on a Monday. Signed-off-by: David Leong <116610336+leongdl@users.noreply.github.com> --- .github/dependabot.yml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 22747bee..5c2209e6 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -25,6 +25,36 @@ updates: update-types: - "minor" - "patch" + - package-ecosystem: "cargo" + directory: "/" # Workspace root; rust-bindings is discovered as a member + schedule: + interval: "weekly" + day: "monday" + commit-message: + prefix: "chore(deps):" + # openjd-* minor bumps get a PR each: they are 0.x, where cargo treats a + # minor as breaking, and they carry the API surface these bindings wrap. + # Everything else is grouped — tokio, uuid and serde_json are 1.x, where a + # minor is additive, and transitive Cargo.lock bumps are noise. + # + # Every cargo PR needs `scripts/check_third_party_licenses.sh --update` + # committed onto its branch: that check renders crate versions from + # Cargo.lock and dependabot cannot regenerate it. + groups: + # Matched first, so an openjd-* patch groups here and only minors reach + # the exclusion below. Majors match no group and so get a PR each. + cargo-patch: + patterns: + - "*" + update-types: + - "patch" + cargo-minor: + patterns: + - "*" + exclude-patterns: + - "openjd-*" + update-types: + - "minor" - package-ecosystem: "github-actions" directory: "/" # Location of package manifests schedule: