Files
regorus/.github/workflows/dependabot-refresh-cargo-lockfiles.yml
Anand Krishnamoorthi c312e30372 build(deps): update all Rust dependencies and fix lockfile refresh workflow (#704)
* build(deps): update all Rust dependencies to latest versions

Bulk-update all Cargo.lock files across the workspace and bindings
to their latest compatible versions. This supersedes the individual
per-directory dependabot PRs (#678-#682) that fail CI due to version
skew when only one lockfile is updated.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* ci: refresh ALL Cargo lockfiles on dependabot PRs

Dependabot security updates bypass the grouped-updates config and
create per-directory PRs (one per Cargo.lock). This causes version
skew — e.g. rand gets bumped in bindings/ruby but stays old elsewhere,
breaking the build.

Fix by unconditionally refreshing all lockfiles whenever any Cargo
manifest or lockfile changes, rather than only the affected directory.

Also harden the workflow against expression injection:
- Move head.ref and base_ref to env vars (not inline ${{ }})
- Validate refs via git check-ref-format --branch
- Validate SHA format (hex, 40 chars) before use
- Fetch base branch by ref (not bare SHA) for reliable diffing
- Add security boundary comment on untrusted code checkout
- Add version comment on pinned checkout action SHA

Ref: https://github.com/dependabot/dependabot-core/issues/7547

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-04 15:20:05 -05:00

138 lines
5.3 KiB
YAML

# Copyright (c) Microsoft Corporation. All rights reserved.
#
name: dependabot/refresh-cargo-lockfiles
on:
pull_request_target:
types: [opened, synchronize, reopened]
branches: ["main"]
concurrency:
group: dependabot-refresh-cargo-lockfiles-${{ github.event.pull_request.number }}
cancel-in-progress: true
permissions:
contents: read
env:
CARGO_TERM_COLOR: always
jobs:
refresh-cargo-lockfiles:
permissions:
contents: write
if: >-
github.event.pull_request.user.login == 'dependabot[bot]' &&
github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
steps:
# SECURITY: This checks out untrusted PR code at the EXACT commit that
# triggered the event (immutable SHA, not mutable branch ref) to avoid
# TOCTOU if the branch moves between event dispatch and checkout.
# ONLY cargo update and cargo metadata (which do NOT execute build
# scripts) may run against this checkout. Do NOT add cargo build/check/
# test/run steps.
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.2.2
with:
repository: ${{ github.event.pull_request.head.repo.full_name }}
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 1
persist-credentials: false
- name: Setup Rust toolchain
run: |
rustup toolchain install 1.92.0 --profile minimal
rustup override set 1.92.0
cargo --version
rustc --version
- name: Refresh all Cargo lockfiles
shell: bash
env:
BASE_REF: ${{ github.base_ref }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
run: |
set -euo pipefail
# Validate inputs (defense-in-depth against expression injection).
if ! git check-ref-format "refs/heads/$BASE_REF" > /dev/null 2>&1; then
echo "::error::Invalid base ref format: '$BASE_REF'"
exit 1
fi
if [[ ! "$HEAD_SHA" =~ ^[0-9a-f]{40}$ ]]; then
echo "::error::Invalid head SHA format: '$HEAD_SHA'"
exit 1
fi
# Fetch the base branch into its remote-tracking ref so we can diff.
# fetch-depth: 0 on the head ref doesn't guarantee the base branch
# tip is reachable if it has diverged.
git fetch --no-tags --depth=1 origin "refs/heads/${BASE_REF}:refs/remotes/origin/${BASE_REF}"
# Diff against the base branch tip to detect Cargo changes.
# False positives (base advanced) are harmless — they just trigger
# a no-op refresh since we update ALL lockfiles unconditionally.
mapfile -t changed_files < <(git diff --name-only "origin/${BASE_REF}" "$HEAD_SHA" -- ':(glob)**/Cargo.toml' ':(glob)**/Cargo.lock')
if [ "${#changed_files[@]}" -eq 0 ]; then
echo "No Cargo manifest or lockfile changes detected."
exit 0
fi
# Always refresh ALL lockfiles when any Cargo change is detected.
# Dependabot security updates bypass grouping and create per-directory
# PRs, causing version skew if we only refresh the affected directory.
# See: https://github.com/dependabot/dependabot-core/issues/7547
#
# We use `cargo update` (not `cargo metadata`) to actually propagate
# version bumps across lockfiles. `cargo update` only resolves
# dependencies and rewrites Cargo.lock — it does NOT execute build
# scripts, so it is safe to run on untrusted PR code.
all_manifests=(
"Cargo.toml"
"bindings/ffi/Cargo.toml"
"bindings/java/Cargo.toml"
"bindings/python/Cargo.toml"
"bindings/ruby/Cargo.toml"
"bindings/wasm/Cargo.toml"
)
for manifest in "${all_manifests[@]}"; do
echo "Refreshing lockfile for $manifest"
cargo update --manifest-path "$manifest"
done
- name: Commit lockfile refresh
shell: bash
env:
GH_TOKEN: ${{ github.token }}
HEAD_REF: ${{ github.event.pull_request.head.ref }}
run: |
set -euo pipefail
# Validate ref format (defense-in-depth against expression injection).
if ! git check-ref-format "refs/heads/$HEAD_REF" > /dev/null 2>&1; then
echo "::error::Invalid head ref format: '$HEAD_REF'"
exit 1
fi
mapfile -t lockfiles < <(git ls-files -m -o --exclude-standard -- ':(glob)**/Cargo.lock')
for lockfile in "${lockfiles[@]}"; do
git add "$lockfile"
done
if git diff --cached --quiet; then
echo "No Cargo lockfile changes required."
exit 0
fi
auth_header=$(printf 'x-access-token:%s' "$GH_TOKEN" | base64 | tr -d '\n')
trap 'git config --unset-all http.https://github.com/.extraheader' EXIT
git config http.https://github.com/.extraheader "AUTHORIZATION: basic ${auth_header}"
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git commit -m "build(deps): refresh Cargo lockfiles"
git push origin "HEAD:refs/heads/${HEAD_REF}"