Dependency auto-fix #55
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Turns a red audit into a reviewable PR instead of a chore. Runs on a | |
| # schedule, on demand, and immediately after Audit finishes red. | |
| # | |
| # Note: PRs opened with GITHUB_TOKEN do not start other workflows, so the | |
| # PR this opens arrives without CI. See docs/DEPENDENCY-AUTOMATION.md for | |
| # the PAT swap that fixes it; the checks below run here in the meantime. | |
| name: Dependency auto-fix | |
| on: | |
| schedule: | |
| - cron: "0 7 * * 1" # Mondays 07:00 UTC, an hour after Audit | |
| workflow_dispatch: | |
| workflow_run: | |
| workflows: [Audit] | |
| types: [completed] | |
| permissions: | |
| contents: write # push the update branch | |
| pull-requests: write # open and label the PR | |
| issues: write # create the labels the PR is filed under | |
| # One update branch, one writer. A scheduled run and an audit-triggered | |
| # run must never race each other onto it. | |
| concurrency: | |
| group: dependency-auto-fix | |
| cancel-in-progress: false | |
| jobs: | |
| update: | |
| # A green audit has nothing to chase. Neither does a red one from a | |
| # pull request: that failure belongs to the PR branch — often a fork — | |
| # and updating dependencies there is the PR author's call, not ours. | |
| if: >- | |
| github.event_name != 'workflow_run' || | |
| (github.event.workflow_run.conclusion == 'failure' && | |
| github.event.workflow_run.event != 'pull_request') | |
| runs-on: ubuntu-22.04 | |
| steps: | |
| # Pinned to the default branch: on a workflow_run event the default | |
| # checkout follows whatever branch tripped the audit. | |
| - uses: actions/checkout@v7 | |
| with: | |
| ref: ${{ github.event.repository.default_branch }} | |
| - uses: dtolnay/rust-toolchain@stable | |
| with: | |
| components: clippy, rustfmt | |
| - uses: swatinem/rust-cache@v2 | |
| # `cargo audit fix` sits behind a non-default feature, so this one has | |
| # to be compiled — no prebuilt binary carries it. | |
| - name: Install cargo-audit with the fix subcommand | |
| run: cargo install cargo-audit --features=fix --locked | |
| # Best effort. `fix` is experimental and rewrites version requirements | |
| # in the manifests; when it can't help, `cargo update` still runs. | |
| - name: Apply advisory fixes | |
| run: cargo audit fix || echo "cargo audit fix applied nothing" | |
| # Refreshes Cargo.lock inside the existing semver ranges — this is what | |
| # picks up patch releases that quietly clear an advisory. | |
| - name: Update the lockfile | |
| run: cargo update | |
| - name: Check whether anything moved | |
| id: diff | |
| run: | | |
| if git diff --quiet; then | |
| echo "changed=false" >> "$GITHUB_OUTPUT" | |
| echo "Dependencies already current — no PR to open." | |
| else | |
| echo "changed=true" >> "$GITHUB_OUTPUT" | |
| git diff --stat | |
| fi | |
| # Never open a PR that CI would immediately turn red. Same three gates | |
| # as ci.yml, and the same reason for skipping the Tauri crate: building | |
| # it needs webkitgtk, which the release workflow already covers. | |
| - name: Verify the workspace still passes | |
| if: steps.diff.outputs.changed == 'true' | |
| run: | | |
| cargo fmt --check | |
| cargo clippy -p diskern-core -p diskern-cli -- -D warnings | |
| cargo test -p diskern-core -p diskern-cli | |
| # Written to RUNNER_TEMP, not the workspace: create-pull-request stages | |
| # every uncommitted file it finds, so a scratch file in the repo root | |
| # ends up committed to the branch. It did, once. | |
| # | |
| # Single-quoted printf throughout — the body is full of markdown | |
| # backticks, which bash would read as command substitution. | |
| - name: Summarize the update for the PR body | |
| if: steps.diff.outputs.changed == 'true' | |
| run: | | |
| { | |
| printf 'Automated dependency refresh.\n\n' | |
| printf '## What ran\n\n' | |
| printf -- '- `cargo audit fix` — raises version requirements that an advisory needs (experimental).\n' | |
| printf -- '- `cargo update` — refreshes `Cargo.lock` within the existing semver ranges.\n' | |
| printf -- '- `cargo fmt --check`, `cargo clippy -D warnings`, `cargo test` — all green before this PR opened.\n\n' | |
| printf '## Changes\n\n```diff\n' | |
| git diff -- Cargo.lock Cargo.toml 'crates/*/Cargo.toml' 'app/src-tauri/Cargo.toml' | head -c 20000 | |
| printf '\n```\n\n' | |
| printf '## Advisories after the update\n\n```\n' | |
| cargo audit --color never 2>&1 | head -c 10000 || true | |
| printf '\n```\n\n' | |
| printf '<sub>Opened by the Dependency auto-fix workflow. Both blocks are truncated at\n' | |
| printf 'the size GitHub will render; run the commands locally for the full output.</sub>\n' | |
| } > "$RUNNER_TEMP/pr-body.md" | |
| wc -c "$RUNNER_TEMP/pr-body.md" | |
| # create-pull-request fails the whole step on an unknown label rather | |
| # than opening the PR unlabelled, and `security` only exists once | |
| # audit.yml has run at least once. --force keeps this idempotent. | |
| - name: Ensure the PR labels exist | |
| if: steps.diff.outputs.changed == 'true' | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| gh label create security --force \ | |
| --color B60205 --description "Security advisory or hardening" | |
| gh label create dependencies --force \ | |
| --color 0366D6 --description "Pull requests that update a dependency file" | |
| gh label create rust --force \ | |
| --color 000000 --description "Pull requests that update rust code" | |
| - name: Open the update PR | |
| if: steps.diff.outputs.changed == 'true' | |
| uses: peter-evans/create-pull-request@v8 | |
| with: | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| base: ${{ github.event.repository.default_branch }} | |
| branch: automation/cargo-update | |
| delete-branch: true | |
| # Belt and braces alongside writing the body to RUNNER_TEMP: even | |
| # if some later step leaves a stray file in the tree, only the | |
| # dependency manifests can reach the commit. | |
| add-paths: | | |
| Cargo.lock | |
| Cargo.toml | |
| crates/*/Cargo.toml | |
| app/src-tauri/Cargo.toml | |
| commit-message: "chore(deps): refresh Rust dependencies" | |
| title: "chore(deps): refresh Rust dependencies" | |
| body-path: ${{ runner.temp }}/pr-body.md | |
| labels: | | |
| dependencies | |
| rust | |
| security | |
| assignees: Muawiya-contact | |
| reviewers: Muawiya-contact |