From 4afaaa22a7d271a81e367b908878fde080fa222d Mon Sep 17 00:00:00 2001 From: Joshua Griffith Date: Fri, 2 Oct 2026 11:43:46 -0500 Subject: [PATCH 1/4] ci: record Cargo-Rail planning evidence Add a "Planning evidence" job to the Check workflow. It runs on push to main and on manual dispatch. It records which workspace files the cargo.build work item reads. It saves the record in the Actions cache under the commit that it describes. Both plan jobs now install the stable toolchain, restore the evidence of the base commit, and give it to the planner. A change that no compiler reads, such as a Markdown edit, then skips the jobs that route on cargo.build. A cache miss only widens the plan. Clippy evidence is not recorded. Cargo-Rail 0.30.1 marks it incomplete, because the clippy::cargo lints make clippy-driver run cargo metadata. The evidence binds the Cargo environment. Move RUSTFLAGS from the Check workflow env to the jobs that install mold. The evidence job and both plan jobs then see the same Cargo environment. Remove the since inputs and the full-history fetch from the plan jobs. The v10 Action selects the same comparison by default and fetches the history that it needs. The plan and evidence checkouts do not keep credentials. Narrow the ci work item to the workflows and the Cargo-Rail policy. A new taplo work item routes .taplo.toml to the Format job only. No CI job reads the Makefile, so it routes no work. The extension has no Rust tests, so route on cargo.build in place of cargo.test. Note in the extension manifest that the cdylib has no Rust doctests. Remove three doc examples that could not compile. Each one called a function without the Ruby VM setup that it needs. The prose above each example already says what the example showed. --- .config/rail.toml | 6 +++- .github/workflows/general.yaml | 63 +++++++++++++++++++++++++++++---- .github/workflows/quality.yaml | 10 ++++-- ext/prosody/Cargo.toml | 1 + ext/prosody/src/tracing_util.rs | 8 ----- ext/prosody/src/util.rs | 14 -------- 6 files changed, 71 insertions(+), 31 deletions(-) diff --git a/.config/rail.toml b/.config/rail.toml index 7838f73..fb87190 100644 --- a/.config/rail.toml +++ b/.config/rail.toml @@ -1,5 +1,5 @@ [plan.work.ci] -paths = [".config/rail.toml", ".github/**", ".taplo.toml", "Makefile"] +paths = [".config/rail.toml", ".github/**"] scope = "repository" [plan.work.ruby] @@ -24,3 +24,7 @@ paths = [ "typecheck_negative/**", ] scope = "repository" + +[plan.work.taplo] +paths = [".taplo.toml"] +scope = "repository" diff --git a/.github/workflows/general.yaml b/.github/workflows/general.yaml index a753f14..7ecffc8 100644 --- a/.github/workflows/general.yaml +++ b/.github/workflows/general.yaml @@ -6,11 +6,11 @@ on: pull_request: branches: - main + workflow_dispatch: env: CARGO_TERM_COLOR: always CARGO_INCREMENTAL: 0 CARGO_PROFILE_TEST_DEBUG: 0 - RUSTFLAGS: -C target-cpu=x86-64-v3 -C link-arg=-fuse-ld=mold FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true permissions: contents: read @@ -23,20 +23,66 @@ jobs: steps: - uses: actions/checkout@v7 with: - fetch-depth: 0 + persist-credentials: false + - uses: dtolnay/rust-toolchain@stable + - name: Restore planning evidence + uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: target/planning-evidence + key: cargo-rail-evidence-${{ runner.os }}-${{ runner.arch }}-${{ github.event.pull_request.base.sha || github.event.before }} - id: rail uses: loadingalias/cargo-rail-action@409962f05d90a78aae995ef6b1c8466010c0a460 # v10.1.1 with: - since: ${{ github.event_name == 'push' && github.event.before || '' }} + evidence: target/planning-evidence + + # A separate job records evidence on every push. The routed jobs can skip, and their RUSTFLAGS would not match the plan jobs. + evidence: + name: Planning evidence + if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + with: + persist-credentials: false + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.4' + - name: Install build dependencies + if: runner.os == 'Linux' + uses: awalsh128/cache-apt-pkgs-action@latest + with: + packages: cmake libcurl4-openssl-dev protobuf-compiler + version: 1.0 + - uses: dtolnay/rust-toolchain@stable + - uses: loadingalias/cargo-rail-action/setup@409962f05d90a78aae995ef6b1c8466010c0a460 # v10.1.1 + # Clippy evidence is not recorded: Cargo-Rail 0.30.1 marks it incomplete when the clippy::cargo lints run cargo metadata. + - name: Record build evidence + run: > + cargo rail plan evidence --work cargo.build + --output target/planning-evidence/cargo.build.json + -- build --locked --workspace --all-features + - name: Print evidence bindings + run: > + jq '{source_base, cargo_identity, cargo_configuration_identity, + toolchain_identity, target_identity, platform}' + target/planning-evidence/*.json + - name: Save planning evidence + uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: target/planning-evidence + key: cargo-rail-evidence-${{ runner.os }}-${{ runner.arch }}-${{ github.sha }} test: name: Tests needs: plan if: >- - contains(fromJSON(needs.plan.outputs.required-work), 'cargo.test') || + contains(fromJSON(needs.plan.outputs.required-work), 'cargo.build') || contains(fromJSON(needs.plan.outputs.required-work), 'ruby') || contains(fromJSON(needs.plan.outputs.required-work), 'ci') runs-on: ubuntu-latest + env: + RUSTFLAGS: -C target-cpu=x86-64-v3 -C link-arg=-fuse-ld=mold services: kafka: image: apache/kafka:latest @@ -123,11 +169,13 @@ jobs: needs: plan if: >- contains(fromJSON(needs.plan.outputs.required-work), 'cargo.fmt') || + contains(fromJSON(needs.plan.outputs.required-work), 'taplo') || contains(fromJSON(needs.plan.outputs.required-work), 'cargo.build') || - contains(fromJSON(needs.plan.outputs.required-work), 'cargo.test') || contains(fromJSON(needs.plan.outputs.required-work), 'ruby') || contains(fromJSON(needs.plan.outputs.required-work), 'ci') runs-on: ubuntu-latest + env: + RUSTFLAGS: -C target-cpu=x86-64-v3 -C link-arg=-fuse-ld=mold steps: - uses: actions/checkout@v7 - name: Set up mold @@ -160,9 +208,10 @@ jobs: if: >- contains(fromJSON(needs.plan.outputs.required-work), 'dependency-policy') || contains(fromJSON(needs.plan.outputs.required-work), 'cargo.build') || - contains(fromJSON(needs.plan.outputs.required-work), 'cargo.test') || contains(fromJSON(needs.plan.outputs.required-work), 'ci') runs-on: ubuntu-latest + env: + RUSTFLAGS: -C target-cpu=x86-64-v3 -C link-arg=-fuse-ld=mold steps: - uses: actions/checkout@v7 @@ -194,6 +243,8 @@ jobs: contains(fromJSON(needs.plan.outputs.required-work), 'cargo.build') || contains(fromJSON(needs.plan.outputs.required-work), 'ci') runs-on: ubuntu-latest + env: + RUSTFLAGS: -C target-cpu=x86-64-v3 -C link-arg=-fuse-ld=mold steps: - uses: actions/checkout@v7 - name: Set up mold diff --git a/.github/workflows/quality.yaml b/.github/workflows/quality.yaml index 66a787f..191a551 100644 --- a/.github/workflows/quality.yaml +++ b/.github/workflows/quality.yaml @@ -25,11 +25,17 @@ jobs: steps: - uses: actions/checkout@v7 with: - fetch-depth: 0 + persist-credentials: false + - uses: dtolnay/rust-toolchain@stable + - name: Restore planning evidence + uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: target/planning-evidence + key: cargo-rail-evidence-${{ runner.os }}-${{ runner.arch }}-${{ github.event.pull_request.base.sha || github.event.before }} - id: rail uses: loadingalias/cargo-rail-action@409962f05d90a78aae995ef6b1c8466010c0a460 # v10.1.1 with: - since: ${{ github.event_name == 'push' && github.event.before || '' }} + evidence: target/planning-evidence clippy: name: Clippy diff --git a/ext/prosody/Cargo.toml b/ext/prosody/Cargo.toml index 308d5b2..a78a059 100644 --- a/ext/prosody/Cargo.toml +++ b/ext/prosody/Cargo.toml @@ -6,6 +6,7 @@ publish = false version = "0.1.0" [lib] +# cdylib only: the extension has no Rust doctests; the RSpec suite covers it. crate-type = ["cdylib"] [dependencies] diff --git a/ext/prosody/src/tracing_util.rs b/ext/prosody/src/tracing_util.rs index e7a97c2..61a9290 100644 --- a/ext/prosody/src/tracing_util.rs +++ b/ext/prosody/src/tracing_util.rs @@ -32,14 +32,6 @@ use std::collections::HashMap; /// - OpenTelemetry module is not available in Ruby /// - Context extraction or propagation fails /// - Ruby-to-Rust type conversion fails -/// -/// # Example -/// -/// ```rust -/// let context = extract_opentelemetry_context(ruby, &propagator)?; -/// let span = info_span!("operation"); -/// span.set_parent(context); -/// ``` pub fn extract_opentelemetry_context( ruby: &Ruby, propagator: &TextMapCompositePropagator, diff --git a/ext/prosody/src/util.rs b/ext/prosody/src/util.rs index 4a49f04..5ddb14d 100644 --- a/ext/prosody/src/util.rs +++ b/ext/prosody/src/util.rs @@ -29,13 +29,6 @@ use tracing::{error, warn}; /// /// This macro requires a `ruby: &Ruby` parameter to enforce that it can only /// be used within a Ruby thread context, ensuring thread safety. -/// -/// # Examples -/// -/// ``` -/// let method_name = id!(ruby, "to_s"); -/// // Use method_name with Ruby function calls -/// ``` #[macro_export] macro_rules! id { ($ruby:expr, $str:expr) => {{ @@ -256,13 +249,6 @@ impl ForkGuard { /// /// `Some(EnterGuard)` if we entered a new runtime (hold the guard), or `None` /// if already in a runtime context. -/// -/// # Examples -/// -/// ```rust -/// let _guard = ensure_runtime_context(ruby); -/// // Safe to perform async operations -/// ``` pub fn ensure_runtime_context(ruby: &Ruby) -> Option> { let guard = Handle::try_current().is_err().then(|| RUNTIME.enter()); From bc195865d4afacd768e85025701febc8a484364f Mon Sep 17 00:00:00 2001 From: Joshua Griffith Date: Fri, 2 Oct 2026 12:25:57 -0500 Subject: [PATCH 2/4] ci: create the evidence directory before planning The planner rejects a missing evidence directory. A cache miss leaves no directory, so the plan job failed. Create the directory before the planner runs. An empty directory only widens the plan. --- .github/workflows/general.yaml | 3 +++ .github/workflows/quality.yaml | 3 +++ 2 files changed, 6 insertions(+) diff --git a/.github/workflows/general.yaml b/.github/workflows/general.yaml index 7ecffc8..b1ff4b0 100644 --- a/.github/workflows/general.yaml +++ b/.github/workflows/general.yaml @@ -30,6 +30,9 @@ jobs: with: path: target/planning-evidence key: cargo-rail-evidence-${{ runner.os }}-${{ runner.arch }}-${{ github.event.pull_request.base.sha || github.event.before }} + # The planner rejects a missing evidence directory but accepts an empty one after a cache miss. + - name: Create the evidence directory + run: mkdir -p target/planning-evidence - id: rail uses: loadingalias/cargo-rail-action@409962f05d90a78aae995ef6b1c8466010c0a460 # v10.1.1 with: diff --git a/.github/workflows/quality.yaml b/.github/workflows/quality.yaml index 191a551..a050642 100644 --- a/.github/workflows/quality.yaml +++ b/.github/workflows/quality.yaml @@ -32,6 +32,9 @@ jobs: with: path: target/planning-evidence key: cargo-rail-evidence-${{ runner.os }}-${{ runner.arch }}-${{ github.event.pull_request.base.sha || github.event.before }} + # The planner rejects a missing evidence directory but accepts an empty one after a cache miss. + - name: Create the evidence directory + run: mkdir -p target/planning-evidence - id: rail uses: loadingalias/cargo-rail-action@409962f05d90a78aae995ef6b1c8466010c0a460 # v10.1.1 with: From b81d4ba5c667a69b3bb8db0bc30594ce9bb37854 Mon Sep 17 00:00:00 2001 From: Joshua Griffith Date: Fri, 2 Oct 2026 12:38:11 -0500 Subject: [PATCH 3/4] ci: plan every work item on manual dispatch A manual run has no push or pull-request base. The Action then compares against HEAD~1, which a shallow checkout of a branch does not hold, and the plan job fails. Plan every work item on workflow_dispatch instead. --- .github/workflows/general.yaml | 1 + .github/workflows/quality.yaml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/general.yaml b/.github/workflows/general.yaml index b1ff4b0..8af2309 100644 --- a/.github/workflows/general.yaml +++ b/.github/workflows/general.yaml @@ -36,6 +36,7 @@ jobs: - id: rail uses: loadingalias/cargo-rail-action@409962f05d90a78aae995ef6b1c8466010c0a460 # v10.1.1 with: + all: ${{ github.event_name == 'workflow_dispatch' }} evidence: target/planning-evidence # A separate job records evidence on every push. The routed jobs can skip, and their RUSTFLAGS would not match the plan jobs. diff --git a/.github/workflows/quality.yaml b/.github/workflows/quality.yaml index a050642..928bb14 100644 --- a/.github/workflows/quality.yaml +++ b/.github/workflows/quality.yaml @@ -38,6 +38,7 @@ jobs: - id: rail uses: loadingalias/cargo-rail-action@409962f05d90a78aae995ef6b1c8466010c0a460 # v10.1.1 with: + all: ${{ github.event_name == 'workflow_dispatch' }} evidence: target/planning-evidence clippy: From ef2baa6211ee2ee6cf1cc1cf41ee7f1ddaf6d521 Mon Sep 17 00:00:00 2001 From: Joshua Griffith Date: Fri, 2 Oct 2026 13:47:46 -0500 Subject: [PATCH 4/4] ci: route Clippy and Documentation on recorded build evidence Cargo-Rail has no complete evidence for cargo.clippy and cargo.doc, so jobs that route on them never skip. Route the Clippy and Documentation jobs on cargo.build. Its evidence covers the same compiler inputs, because the extension has no cfg(doc) or cfg(test) code. Remove the workflow_dispatch trigger from the Check workflow, and record evidence only on push. The Quality planner plans every work item on schedule and manual dispatch, so the Clippy condition reads only the required work. Restore evidence only on push and pull requests, because a scheduled or manual run has no base commit and the key prefix can match an unrelated commit. Give the plan and evidence jobs read-only permissions. Remove the jq step, so that a jq failure cannot skip the cache save. Add rust-cache to the evidence job, because recording works on a warm target directory. Use mold as the default linker in place of job-level RUSTFLAGS. The Cargo configuration already sets target-cpu=x86-64-v3. --- .github/workflows/general.yaml | 37 +++++++++------------------------- .github/workflows/quality.yaml | 9 +++++---- 2 files changed, 15 insertions(+), 31 deletions(-) diff --git a/.github/workflows/general.yaml b/.github/workflows/general.yaml index 8af2309..9e48847 100644 --- a/.github/workflows/general.yaml +++ b/.github/workflows/general.yaml @@ -6,7 +6,6 @@ on: pull_request: branches: - main - workflow_dispatch: env: CARGO_TERM_COLOR: always CARGO_INCREMENTAL: 0 @@ -18,6 +17,8 @@ jobs: plan: name: Plan runs-on: ubuntu-latest + permissions: + contents: read outputs: required-work: ${{ steps.rail.outputs.required-work }} steps: @@ -26,6 +27,7 @@ jobs: persist-credentials: false - uses: dtolnay/rust-toolchain@stable - name: Restore planning evidence + if: github.event_name == 'push' || github.event_name == 'pull_request' uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: target/planning-evidence @@ -36,14 +38,15 @@ jobs: - id: rail uses: loadingalias/cargo-rail-action@409962f05d90a78aae995ef6b1c8466010c0a460 # v10.1.1 with: - all: ${{ github.event_name == 'workflow_dispatch' }} evidence: target/planning-evidence - # A separate job records evidence on every push. The routed jobs can skip, and their RUSTFLAGS would not match the plan jobs. + # A separate job records evidence on every push, because the routed jobs can skip. evidence: name: Planning evidence - if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' + if: github.event_name == 'push' runs-on: ubuntu-latest + permissions: + contents: read steps: - uses: actions/checkout@v7 with: @@ -59,18 +62,15 @@ jobs: packages: cmake libcurl4-openssl-dev protobuf-compiler version: 1.0 - uses: dtolnay/rust-toolchain@stable + - uses: Swatinem/rust-cache@v2 - uses: loadingalias/cargo-rail-action/setup@409962f05d90a78aae995ef6b1c8466010c0a460 # v10.1.1 - # Clippy evidence is not recorded: Cargo-Rail 0.30.1 marks it incomplete when the clippy::cargo lints run cargo metadata. + # Do not record Clippy evidence: Cargo-Rail 0.30.1 marks it incomplete when the clippy::cargo lints run cargo metadata. + # Cargo-Rail has no complete evidence for cargo.clippy and cargo.doc. The Clippy and Documentation jobs route on cargo.build, whose evidence covers the same compiler inputs. - name: Record build evidence run: > cargo rail plan evidence --work cargo.build --output target/planning-evidence/cargo.build.json -- build --locked --workspace --all-features - - name: Print evidence bindings - run: > - jq '{source_base, cargo_identity, cargo_configuration_identity, - toolchain_identity, target_identity, platform}' - target/planning-evidence/*.json - name: Save planning evidence uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: @@ -85,8 +85,6 @@ jobs: contains(fromJSON(needs.plan.outputs.required-work), 'ruby') || contains(fromJSON(needs.plan.outputs.required-work), 'ci') runs-on: ubuntu-latest - env: - RUSTFLAGS: -C target-cpu=x86-64-v3 -C link-arg=-fuse-ld=mold services: kafka: image: apache/kafka:latest @@ -130,8 +128,6 @@ jobs: uses: dtolnay/rust-toolchain@stable - name: Set up mold uses: rui314/setup-mold@10ca16bf91dc22e05ebdc935cad9c75ea248f621 # v1 - with: - make-default: false - name: Cache Rust dependencies uses: Swatinem/rust-cache@v2 - name: Install build dependencies @@ -178,14 +174,10 @@ jobs: contains(fromJSON(needs.plan.outputs.required-work), 'ruby') || contains(fromJSON(needs.plan.outputs.required-work), 'ci') runs-on: ubuntu-latest - env: - RUSTFLAGS: -C target-cpu=x86-64-v3 -C link-arg=-fuse-ld=mold steps: - uses: actions/checkout@v7 - name: Set up mold uses: rui314/setup-mold@10ca16bf91dc22e05ebdc935cad9c75ea248f621 # v1 - with: - make-default: false - uses: dtolnay/rust-toolchain@nightly with: components: rustfmt @@ -214,16 +206,12 @@ jobs: contains(fromJSON(needs.plan.outputs.required-work), 'cargo.build') || contains(fromJSON(needs.plan.outputs.required-work), 'ci') runs-on: ubuntu-latest - env: - RUSTFLAGS: -C target-cpu=x86-64-v3 -C link-arg=-fuse-ld=mold steps: - uses: actions/checkout@v7 - uses: dtolnay/rust-toolchain@nightly - name: Set up mold uses: rui314/setup-mold@10ca16bf91dc22e05ebdc935cad9c75ea248f621 # v1 - with: - make-default: false - uses: Swatinem/rust-cache@v2 - name: Install udeps from crates.io uses: baptiste0928/cargo-install@v3 @@ -243,18 +231,13 @@ jobs: name: Documentation needs: plan if: >- - contains(fromJSON(needs.plan.outputs.required-work), 'cargo.doc') || contains(fromJSON(needs.plan.outputs.required-work), 'cargo.build') || contains(fromJSON(needs.plan.outputs.required-work), 'ci') runs-on: ubuntu-latest - env: - RUSTFLAGS: -C target-cpu=x86-64-v3 -C link-arg=-fuse-ld=mold steps: - uses: actions/checkout@v7 - name: Set up mold uses: rui314/setup-mold@10ca16bf91dc22e05ebdc935cad9c75ea248f621 # v1 - with: - make-default: false - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 diff --git a/.github/workflows/quality.yaml b/.github/workflows/quality.yaml index 928bb14..f936649 100644 --- a/.github/workflows/quality.yaml +++ b/.github/workflows/quality.yaml @@ -20,6 +20,8 @@ jobs: plan: name: Plan runs-on: ubuntu-latest + permissions: + contents: read outputs: required-work: ${{ steps.rail.outputs.required-work }} steps: @@ -28,6 +30,7 @@ jobs: persist-credentials: false - uses: dtolnay/rust-toolchain@stable - name: Restore planning evidence + if: github.event_name == 'push' || github.event_name == 'pull_request' uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: target/planning-evidence @@ -38,16 +41,14 @@ jobs: - id: rail uses: loadingalias/cargo-rail-action@409962f05d90a78aae995ef6b1c8466010c0a460 # v10.1.1 with: - all: ${{ github.event_name == 'workflow_dispatch' }} + all: ${{ github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' }} evidence: target/planning-evidence clippy: name: Clippy needs: plan if: >- - github.event_name == 'schedule' || - github.event_name == 'workflow_dispatch' || - contains(fromJSON(needs.plan.outputs.required-work), 'cargo.clippy') || + contains(fromJSON(needs.plan.outputs.required-work), 'cargo.build') || contains(fromJSON(needs.plan.outputs.required-work), 'ci') runs-on: ubuntu-latest steps: