Skip to content

GitHub Action

LucaCappelletti94 edited this page Oct 29, 2026 · 6 revisions

The refquiem action installs the tool, resolves the scan range from the triggering event and either posts a request-changes review on the pull request or fails the job on findings. It reports the comments, docs, strings and Markdown that still mention the Rust items, dependencies, features, binaries and examples your history removed.

Reviewing pull requests

on: pull_request

permissions:
  contents: read
  pull-requests: write

jobs:
  refquiem:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
      # The action and the tool use the same pinned revision.
      - name: Install refquiem from the pinned source
        run: cargo install refquiem --git https://github.com/LucaCappelletti94/cargo-refquiem.git --rev 5ac009ab93c17d40d3e72d50fa33415f84c1ce7f --locked
      - uses: LucaCappelletti94/cargo-refquiem@5ac009ab93c17d40d3e72d50fa33415f84c1ce7f
        with:
          install: false
          pr-number: ${{ github.event.pull_request.number }}

With pr-number, the action posts a review with REQUEST_CHANGES instead of failing the job. Each removed name gets one comment on the line of the diff where the pull request deleted it. The comment gives the headline of the removed name, links every remaining mention at the pull request head and embeds the first three as code snippets. In the headline the removed name links to its line at the report's base and the suggested name to its line at the pull request head, wherever the report places them. Every comment ends with a prefilled link to the wrong review form and a sponsor link.

A mention on a line of a file the pull request changes also gets its own comment there. When the report resolves the rewrite, that comment carries the rewritten mention line as a suggestion fence. The fence outgrows the longest backtick run of the line it holds, and several mentions on one line merge into one comment holding every message and the rewritten line of the first mention. Reports covers the replacement field the rewrite comes from.

GitHub attaches a review comment only to a line of the pull request diff. The deleted line comes from the report's declared_at, where the pull request's base declares the name, which is the left side of the diff. When a name has no such line, as when the pull request both added and removed it, or GitHub rejects the comment, its mentions are linked in the review body instead. An unused suppression marker outside the changed files is linked there too.

The review body counts the removed names and their mentions, splits the names into those with a suggested replacement, several possible replacements or none, and ends with the wrong review and sponsor links, the run link and the <!-- refquiem --> marker the action uses to recognize its own reviews. When the review holds a single finding and its comments carry all of it, the body is the marker alone, since the comment already names the place, the replacement and the links. A clean run and every new review dismiss the earlier CHANGES_REQUESTED reviews the marker identifies, whatever token posted them, and close the open threads of every earlier marked review, replied ones included, so only the newest review keeps open threads. A thread resolves when the job's token holds contents: write, which GitHub requires for resolving, and otherwise its comments are hidden as outdated, which pull-requests: write allows. A review GitHub accepted only with every finding in its body closes no thread, since the earlier threads are then the only comments anchored at its findings. A refquiem: allow(Name) marker that suppresses nothing is a finding too, with a comment naming the marker and no suggestion. Find your first stale reference walks the resolution.

Gating a push

Leave out pr-number and the job fails whenever the scan reports a finding, and a scan error fails the job the same way. That suits a push to main, a schedule entry and a workflow_dispatch run.

on:
  push:
    branches: [main]
  schedule:
    - cron: "23 4 * * 1"

permissions:
  contents: read

jobs:
  refquiem:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
      - name: Install refquiem from the pinned source
        run: cargo install refquiem --git https://github.com/LucaCappelletti94/cargo-refquiem.git --rev 5ac009ab93c17d40d3e72d50fa33415f84c1ce7f --locked
      - uses: LucaCappelletti94/cargo-refquiem@5ac009ab93c17d40d3e72d50fa33415f84c1ce7f
        with:
          install: false

Ranges

The action resolves the range from the event and deepens the shallow checkout until the two ends share an ancestor, fetching only the history it needs.

Trigger Range
pull_request from the merge base of the base branch tip and the pull request head, where the branch forked, to the pull request head, the commit the comments anchor to
pull_request_target refused. The trigger runs with the base repository's credentials, so the job fails before scanning
push from the merge base of before and after, the old tip after a fast-forward and the fork point after a force push, to the new tip. A new branch, or an old tip the remote dropped, starts from the default branch
merge_group from the event's base_sha to its head_sha
schedule, workflow_dispatch and the remaining events the whole history, or from the since input when given

The scan covers the base revision and the commits after it, compared against the event's head when it supplies one and the working tree otherwise. When the two ends share no ancestor, the action unshallows the clone and scans the whole history. The since input is ignored on the events that carry their own range. History and cache covers the parsed-file cache under target/refquiem.

Inputs

Input Default Effect
version empty the refquiem version to install. Empty resolves the newest available
install true install refquiem, or false uses the cargo refquiem already on the path
cwd empty the workspace directory to scan
package empty scan only the workspace member with this name
config empty the configuration file, by default .refquiem.toml at the workspace root. See Configuration
since empty the revision a manual or scheduled audit starts from. Empty audits the whole history
pr-number empty post the findings as a review on this pull request instead of failing the job
dry-run false render the review into the job summary without posting, in any mode
annotations true annotate every finding. Needs no token
mode review review posts the findings in one job, prepare writes the report for a later job and post reviews it on the pull request its workflow_run run built
payload empty the directory prepare writes and post reads, the runner's temp directory when empty

install: true downloads the prebuilt cargo-refquiem-<target>.tar.gz for the runner, Linux x86_64 and aarch64, macOS x86_64 and aarch64 and Windows x86_64. With version set it reads the v<version> release. Without it the newest release carrying the asset, because the action's own releases carry no binary and GitHub marks the newest one Latest. When no asset is available the step falls back to cargo install refquiem --locked, which needs the crate published on crates.io at the requested version. The examples above set install: false and install the tool from the pinned source instead.

Outputs

The action exposes one output, review-status, set by its review step.

Value Outcome
posted the review and its comments were posted. The earlier change requests were dismissed and their threads closed, unless GitHub accepted the review only with every finding in its body
dry-run the review was rendered into the job summary. Nothing was posted
prepared the report was written to payload for a later post
skipped-clean no finding. In review and post mode the earlier change requests were dismissed and their threads closed
skipped-readonly the review post came back read-only. The annotations and the job summary carry the findings
skipped-stale post mode found no open pull request at the head the triggering run built, or outside workflow_run the pull request moved on. Nothing was posted
rejected post mode refused the payload because it names a pull request or a head the triggering run did not build, its run did not come from pull_request, or its report failed validation. Nothing was posted or dismissed, and the step exits nonzero
failed the review step could not read the run, the pull request or its files, could not post, read an unexpected review state, or found no prepared payload in post mode. The step exits nonzero

A gate run that sets neither pr-number nor dry-run skips the review step and leaves the output empty, and a scan error fails the scan step before it runs.

Pull requests from forks and a read-only build

The pull request's code runs in the pull request job, so that job holds a read-only token and only prepares the review. It runs mode: prepare on every pull request and uploads the payload, and a workflow_run job posts it with mode: post. A fork's token is read-only anyway, so this pair is also the only way to review forks. The single-job form above runs the pull request's code with a write token, which this pair avoids.

The posting job holds a write token, so it never checks out or runs the pull request's code. It runs the base repository's workflow and the action at the pinned revision, checks out the default branch without persisting credentials, grants each scope on the job alone and downloads the payload outside the workspace. GitHub's guide on workflow_run explains the risk.

The payload carries the --json report with the pull request number, head commit and version the preparing run recorded, and post treats all of it as data. It reads the triggering run from the API, requires it to come from pull_request, and posts only on an open pull request whose head is the commit, repository and branch that run built. It validates the report and renders the review itself, so no review text comes from the payload. A clean report posts nothing, dismisses the earlier change requests and reports skipped-clean.

name: refquiem

on: pull_request

permissions:
  contents: read
  pull-requests: read

jobs:
  refquiem:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
      - name: Install refquiem from the pinned source
        run: cargo install refquiem --git https://github.com/LucaCappelletti94/cargo-refquiem.git --rev 5ac009ab93c17d40d3e72d50fa33415f84c1ce7f --locked
      # The pull request's code runs here, so this job only prepares the review.
      - uses: LucaCappelletti94/cargo-refquiem@5ac009ab93c17d40d3e72d50fa33415f84c1ce7f
        id: refquiem
        with:
          install: false
          pr-number: ${{ github.event.pull_request.number }}
          mode: prepare
          payload: refquiem-payload
      - if: steps.refquiem.outputs.review-status == 'prepared'
        uses: actions/upload-artifact@v4
        with:
          name: refquiem-payload
          path: refquiem-payload
          retention-days: 1
name: refquiem post

on:
  workflow_run:
    workflows: [refquiem]
    types: [completed]

permissions: {}

jobs:
  post:
    if: github.event.workflow_run.event == 'pull_request'
    runs-on: ubuntu-latest
    permissions:
      actions: read
      contents: read
      pull-requests: write
    steps:
      # The base repository's default branch, so the posting code never comes from the pull request.
      - uses: actions/checkout@v4
        with:
          persist-credentials: false
      - name: Download the prepared review
        id: download
        env:
          GH_TOKEN: ${{ github.token }}
          RUN_ID: ${{ github.event.workflow_run.id }}
        run: |
          if gh run download "$RUN_ID" --repo "$GITHUB_REPOSITORY" --name refquiem-payload --dir "$RUNNER_TEMP/refquiem-payload"; then
            echo "found=true" >> "$GITHUB_OUTPUT"
          else
            echo "the run prepared no review"
          fi
      - if: steps.download.outputs.found == 'true'
        uses: LucaCappelletti94/cargo-refquiem@5ac009ab93c17d40d3e72d50fa33415f84c1ce7f
        id: refquiem
        with:
          mode: post
          payload: ${{ runner.temp }}/refquiem-payload

The post job needs no Rust toolchain, because post mode skips the install and scan steps. The Dogfood and Dogfood post workflows run this pair on the repository itself.

Annotations and dry runs

annotations: true runs the scan with --github, which writes one workflow annotation per finding, one warning per removed dependency it could not resolve, and a ## refquiem job summary. The annotation level is ::error when the run can fail and ::warning when it cannot, as under a review or a dry run. It needs no token and is the findings display for a fork pull request or a read-only token. annotations: false omits it.

dry-run: true renders every comment the action would post, the deleted-line comments included, and the review body into the job summary, and posts, dismisses and closes nothing. A dry run also renders the comments for mentions in files outside the pull request diff, which a real run links from its deleted-line comments or its review body. The two are independent, so a dry run can carry annotations.

Clone this wiki locally