How to Manage Cargo and GitHub Actions Dependency Updates
How to Manage Cargo and GitHub Actions Dependency Updates
Section titled “How to Manage Cargo and GitHub Actions Dependency Updates”Triage and act on a Dependabot PR, run a manual dependency audit, or add/remove
a dependency in the mif-rs workspace while staying inside the license and
supply-chain policy enforced by deny.toml.
Prerequisites
Section titled “Prerequisites”- Write access to
modeled-information-format/mif-rs(or a fork with a PR open). ghCLI authenticated.cargo-denyandcargo-auditinstalled locally for manual checks:Terminal window cargo install cargo-deny cargo-audit
Handle an incoming Dependabot PR
Section titled “Handle an incoming Dependabot PR”Dependabot is configured in .github/dependabot.yml across three ecosystems,
all on the same weekly cadence (Mondays 09:00 America/Chicago):
| Ecosystem | Directory | PR limit | Grouped updates (minor + patch) |
|---|---|---|---|
cargo |
/ |
10 | dev-dependencies (proptest*, test-*, criterion*), async-runtime (tokio*, async-*), serde-ecosystem (serde*) |
github-actions |
/ |
5 | all actions grouped together |
docker |
/ |
5 | ungrouped — keeps Dockerfile base-image digest pins fresh |
Every ecosystem uses commit prefix chore(deps), labels dependencies (plus
an ecosystem-specific label), and reviewer modeled-information-format.
Step 1 — Check whether it auto-merges
Section titled “Step 1 — Check whether it auto-merges”dependabot-automerge.yml runs on every Dependabot PR (opened,
synchronize, reopened, gated on github.actor == 'dependabot[bot]') and
calls gh pr merge --auto --squash for patch and minor version
updates only:
gh pr view <number> --json title,labels,authorIf the PR is a major version bump, auto-merge does not fire — it needs manual review.
Step 2 — Review a major bump or a flagged PR
Section titled “Step 2 — Review a major bump or a flagged PR”gh pr diff <number>Read the dependency’s changelog for breaking changes, then:
gh pr review <number> --approvegh pr merge <number> --squashStep 3 — Recover from a stuck or conflicted PR
Section titled “Step 3 — Recover from a stuck or conflicted PR”If Dependabot flags a merge conflict in Cargo.lock, ask it to rebase first:
gh pr comment <number> --body "@dependabot rebase"If that doesn’t resolve it, check the PR out and regenerate the lockfile manually:
gh pr checkout <number>git merge maincargo update -p <crate-from-dependabot-pr>git add Cargo.lockgit commit -m "chore: resolve merge conflict in Cargo.lock"git pushRun a manual dependency audit
Section titled “Run a manual dependency audit”Use this when a Dependabot cycle hasn’t caught something yet, or before a release.
Step 1 — Update the advisory database and audit
Section titled “Step 1 — Update the advisory database and audit”cargo audit fetchcargo audit --deny warningssecurity-audit.yml runs the same command daily at 00:00 UTC, on every push
that touches Cargo.toml/Cargo.lock, and on manual workflow_dispatch.
Step 2 — Run the full supply-chain policy check
Section titled “Step 2 — Run the full supply-chain policy check”cargo deny checkdeny.toml (workspace root) enforces four categories, each checkable on its
own:
cargo deny check advisories # RustSec DB — all advisory types deniedcargo deny check licenses # SPDX allow-list onlycargo deny check bans # openssl, atty denied; multiple-versions = warncargo deny check sources # crates.io onlyThe license allow-list (deny.toml [licenses] allow) is:
MIT, MIT-0, Apache-2.0, Apache-2.0 WITH LLVM-exception, BSD-2-Clause,BSD-3-Clause, ISC, Zlib, MPL-2.0, Unicode-DFS-2016, Unicode-3.0, CC0-1.0,BSL-1.0, 0BSD[bans] deny blocks openssl (use rustls) and atty (use
std::io::IsTerminal).
Step 3 — Inspect the dependency graph
Section titled “Step 3 — Inspect the dependency graph”cargo tree # full graphcargo tree --duplicates # duplicate transitive versions (warn, not deny)cargo tree -i <crate-name> # who pulls in a given crate, and whyFix a security advisory cargo-audit or Dependabot flags
Section titled “Fix a security advisory cargo-audit or Dependabot flags”Step 1 — Assess it
Section titled “Step 1 — Assess it”cargo auditcargo tree -i <affected-crate> # is the vulnerable path actually reachable?Step 2 — Update the dependency
Section titled “Step 2 — Update the dependency”cargo update -p <crate-name>cargo auditStep 3 — If no fix is available yet
Section titled “Step 3 — If no fix is available yet”In order of preference:
- Pin to an unaffected version in the relevant crate’s
Cargo.toml. - Add a temporary, dated exception in
deny.toml:[advisories]ignore = ["RUSTSEC-2024-XXXX", # No fix available; unexploitable in our usage. Revisit by YYYY-MM-DD.] - Replace the dependency with an alternative crate.
- Fork and patch it.
Step 4 — Verify and push
Section titled “Step 4 — Verify and push”cargo deny check advisoriescargo test --workspace --all-featuresgit add Cargo.toml Cargo.lockgit commit -m "fix(deps): address RUSTSEC-XXXX-YYYY in <crate>"git pushAdd a new dependency
Section titled “Add a new dependency”Step 1 — Evaluate it against workspace policy
Section titled “Step 1 — Evaluate it against workspace policy”- Can this be done with
stdor an existing workspace dependency instead? - Is the license in the
deny.tomlallow-list above? - Does it avoid pulling in
openssloratty(checkcargo tree -i)? - Does it support the workspace MSRV, Rust 1.95 (
rust-version.workspace = truein every crate’sCargo.toml)? - Is it actively maintained, tested, documented?
Step 2 — Add it and check its feature footprint
Section titled “Step 2 — Add it and check its feature footprint”cargo add <crate-name> # or --dev for a dev-only dependencycargo tree -p <crate-name> # what it pulls in transitivelyCheck default features before accepting them. jsonschema in this workspace
is a precedent: its defaults pull in a full reqwest/rustls/aws-lc-rs
HTTP-resolver stack the workspace doesn’t use, so it’s pinned
default-features = false in [workspace.dependencies]. If a new
dependency’s defaults pull in something unexpected, disable defaults and
enable only the features actually needed.
Step 3 — Verify
Section titled “Step 3 — Verify”cargo deny checkcargo clippy --workspace --all-targets --all-features -- -D warningscargo test --workspace --all-featuresStep 4 — If the license isn’t in the allow-list
Section titled “Step 4 — If the license isn’t in the allow-list”- Confirm it’s genuinely a safe permissive license —
cargo deny check licensesnames the exact crate and license string. - Add it to
deny.toml:[licenses]allow = [# ...existing licenses..."NEW-LICENSE-SPDX",] - State why the license was added in the commit message.
Remove an unused dependency
Section titled “Remove an unused dependency”cargo install cargo-machetecargo machete # find unused dependenciescargo remove <crate-name>cargo updatecargo clippy --workspace --all-targets --all-features -- -D warningscargo test --workspace --all-featurescargo deny checkChange the Dependabot schedule or grouping
Section titled “Change the Dependabot schedule or grouping”Edit .github/dependabot.yml. To add a grouped update:
groups: new-group-name: patterns: - "crate-prefix*" update-types: - "minor" - "patch"To ignore a dependency entirely:
ignore: - dependency-name: "crate-to-ignore" versions: [">=2.0.0"] # or omit to ignore all updatesThe dependency set is now current with policy, and the audit trail (commit
messages, deny.toml exceptions with revisit dates) is in place for anything
that couldn’t be fixed immediately.
Quick Reference
Section titled “Quick Reference”| Task | Command |
|---|---|
| Full supply-chain check | cargo deny check |
| Security advisory audit | cargo audit --deny warnings |
| Update all dependencies | cargo update |
| Update one dependency | cargo update -p <crate> |
| Show dependency tree | cargo tree |
| Find duplicate versions | cargo tree --duplicates |
| Find unused dependencies | cargo machete |
| Add a dependency | cargo add <crate> |
| Remove a dependency | cargo remove <crate> |
| List open Dependabot PRs | gh pr list --label dependencies |