Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions .github/actions/ensure-rustup/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: Ensure rustup
description: >-
Make `rustup` available on the job PATH. GitHub-hosted runners preinstall it;
the CodeBuild standard image does not. When it is absent, install a pinned
rustup-init (version and sha256 below) with no default toolchain, so the
workflow's own `rustup toolchain install` lines keep deciding which toolchain
is used. No-op when rustup is already present.

runs:
using: composite
steps:
- name: Install rustup if missing
shell: bash
env:
RUSTUP_INIT_VERSION: 1.29.1
RUSTUP_INIT_SHA256: dda7234360b7f578ca8b0ddcb80145646fa61a67c1720a5abc7051b35c9fcb71
run: |
set -euo pipefail
if command -v rustup >/dev/null 2>&1; then
echo "rustup present: $(command -v rustup)"
exit 0
fi
if [ "$(uname -m)" != "x86_64" ]; then
echo "::error::ensure-rustup only pins the x86_64 rustup-init; runner is $(uname -m)"
exit 1
fi
tmp="$(mktemp -d)"
curl -fsSL --proto '=https' --tlsv1.2 \
"https://static.rust-lang.org/rustup/archive/${RUSTUP_INIT_VERSION}/x86_64-unknown-linux-gnu/rustup-init" \
-o "$tmp/rustup-init"
echo "${RUSTUP_INIT_SHA256} $tmp/rustup-init" | sha256sum -c -
chmod +x "$tmp/rustup-init"
"$tmp/rustup-init" -y --no-modify-path --profile minimal --default-toolchain none
rm -rf "$tmp"
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
"$HOME/.cargo/bin/rustup" --version
41 changes: 34 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,16 @@ jobs:
# `dependencies` job, and the `msrv` job so a failure names itself.
check:
name: check (sdk)
runs-on: ubuntu-latest
# CodeBuild-hosted runner in the Rust testing account (EC2 compute); the label
# must match the CodeBuild project name. Pull requests from forks are routed
# to GitHub-hosted runners. This is routing, not a trust boundary: the fork
# boundary is the repository's approval setting for fork workflows.
runs-on: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository && 'ubuntu-latest' || format('codebuild-github-actions-runner-{0}-{1}', github.run_id, github.run_attempt) }}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please include Dependabot in the hosted-runner condition. Dependabot branches belong to this repository, so the current expression runs updated dependency build scripts and procedural macros inside the CodeBuild account before maintainer review. Python PR 747 routes Dependabot builds to ubuntu-latest. Please add github.actor == 'dependabot[bot]' to this condition and the repeated conditions. The cloud and conformance workflows should instead skip fork and Dependabot PRs because those jobs require unavailable secrets.

steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0

- uses: ./.github/actions/ensure-rustup

- name: Install Rust toolchain
run: |
rustup toolchain install stable --profile minimal --component rustfmt,clippy
Expand Down Expand Up @@ -59,10 +65,13 @@ jobs:
# would then be wrong rather than protective.
msrv:
name: check (msrv 1.94.1)
runs-on: ubuntu-latest
# Same routing as above.
runs-on: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository && 'ubuntu-latest' || format('codebuild-github-actions-runner-{0}-{1}', github.run_id, github.run_attempt) }}
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0

- uses: ./.github/actions/ensure-rustup

- name: Install Rust toolchain
run: |
rustup toolchain install 1.94.1 --profile minimal
Expand All @@ -78,10 +87,13 @@ jobs:
# direct-dependency allowlist that keeps the production graph closed.
dependencies:
name: dependencies
runs-on: ubuntu-latest
# Same routing as above.
runs-on: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository && 'ubuntu-latest' || format('codebuild-github-actions-runner-{0}-{1}', github.run_id, github.run_attempt) }}
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0

- uses: ./.github/actions/ensure-rustup

- name: Install Rust toolchain
run: rustup toolchain install stable --profile minimal && rustup default stable

Expand All @@ -103,10 +115,13 @@ jobs:
# two together.
external-types:
name: public API (external types)
runs-on: ubuntu-latest
# Same routing as above.
runs-on: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository && 'ubuntu-latest' || format('codebuild-github-actions-runner-{0}-{1}', github.run_id, github.run_attempt) }}
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0

- uses: ./.github/actions/ensure-rustup

- name: Install Rust toolchain
run: |
rustup toolchain install nightly-2026-03-20 --profile minimal
Expand Down Expand Up @@ -145,14 +160,17 @@ jobs:
semver:
name: public API (semver)
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
# Same routing as above.
runs-on: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository && 'ubuntu-latest' || format('codebuild-github-actions-runner-{0}-{1}', github.run_id, github.run_attempt) }}
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
# cargo-semver-checks materializes the baseline from git history;
# the default depth-1 checkout does not contain the base branch.
fetch-depth: 0

- uses: ./.github/actions/ensure-rustup

- name: Install Rust toolchain
run: rustup toolchain install stable --profile minimal && rustup default stable

Expand All @@ -170,10 +188,13 @@ jobs:
# build rather than assumed to (issue #39).
feature-powerset:
name: feature powerset
runs-on: ubuntu-latest
# Same routing as above.
runs-on: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository && 'ubuntu-latest' || format('codebuild-github-actions-runner-{0}-{1}', github.run_id, github.run_attempt) }}
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0

- uses: ./.github/actions/ensure-rustup

- name: Install Rust toolchain
run: rustup toolchain install stable --profile minimal && rustup default stable

Expand All @@ -191,7 +212,8 @@ jobs:
# CI still compiles both so a public API change cannot silently break them.
workspaces:
name: build (${{ matrix.workspace }})
runs-on: ubuntu-latest
# Same routing as above.
runs-on: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository && 'ubuntu-latest' || format('codebuild-github-actions-runner-{0}-{1}', github.run_id, github.run_attempt) }}
strategy:
fail-fast: false
matrix:
Expand All @@ -203,9 +225,14 @@ jobs:
# (the examples build has died linking with "no space left"); reclaim
# the space taken by preinstalled toolchains we never use.
- uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be # v1.3.1
# Only the hosted image is short on disk; the CodeBuild image has a large
# volume and a different package set.
if: runner.environment == 'github-hosted'
with:
tool-cache: false

- uses: ./.github/actions/ensure-rustup

- name: Install Rust toolchain
run: rustup toolchain install stable --profile minimal --component clippy && rustup default stable

Expand Down
8 changes: 7 additions & 1 deletion .github/workflows/cloud-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@ permissions:
jobs:
example-tests:
name: example-tests
runs-on: ubuntu-latest
# CodeBuild-hosted runner in the Rust testing account (EC2 compute); the label
# must match the CodeBuild project name. Pull requests from forks are routed
# to GitHub-hosted runners. This is routing, not a trust boundary: the fork
# boundary is the repository's approval setting for fork workflows.
runs-on: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository && 'ubuntu-latest' || format('codebuild-github-actions-runner-{0}-{1}', github.run_id, github.run_attempt) }}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This moves the job to a CodeBuild project with a stated 60-minute limit, but the next line retains timeout-minutes: 90. CodeBuild can terminate the runner before GitHub cancels the job. The if: always() teardown then cannot run, which can leave the three test stacks deployed. Please set the GitHub timeout below 60 minutes with enough cleanup time, move cleanup into a separate if: always() job, or raise the CodeBuild timeout above 90 minutes.

timeout-minutes: 90
permissions:
contents: read
Expand All @@ -42,6 +46,8 @@ jobs:
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0

- uses: ./.github/actions/ensure-rustup

- name: Install Rust toolchain
run: |
rustup toolchain install stable --profile minimal
Expand Down
8 changes: 7 additions & 1 deletion .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,16 @@ permissions:
jobs:
analyze:
name: Analyze Rust
runs-on: ubuntu-latest
# CodeBuild-hosted runner in the Rust testing account (EC2 compute); the label
# must match the CodeBuild project name. Pull requests from forks are routed
# to GitHub-hosted runners. This is routing, not a trust boundary: the fork
# boundary is the repository's approval setting for fork workflows.
runs-on: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository && 'ubuntu-latest' || format('codebuild-github-actions-runner-{0}-{1}', github.run_id, github.run_attempt) }}
steps:
- uses: actions/checkout@v4

- uses: ./.github/actions/ensure-rustup

- name: Install Rust toolchain
run: rustup toolchain install stable --profile minimal && rustup default stable

Expand Down
14 changes: 11 additions & 3 deletions .github/workflows/conformance-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,11 @@ permissions:
jobs:
discover_suites:
name: discover conformance suites
runs-on: ubuntu-latest
# CodeBuild-hosted runner in the Rust testing account (EC2 compute); the label
# must match the CodeBuild project name. Pull requests from forks are routed
# to GitHub-hosted runners. This is routing, not a trust boundary: the fork
# boundary is the repository's approval setting for fork workflows.
runs-on: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository && 'ubuntu-latest' || format('codebuild-github-actions-runner-{0}-{1}', github.run_id, github.run_attempt) }}
outputs:
suites: ${{ steps.discover.outputs.suites }}
steps:
Expand All @@ -72,13 +76,16 @@ jobs:

build_handlers:
name: build conformance handlers
runs-on: ubuntu-latest
# Same routing as above.
runs-on: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository && 'ubuntu-latest' || format('codebuild-github-actions-runner-{0}-{1}', github.run_id, github.run_attempt) }}
defaults:
run:
working-directory: conformance
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0

- uses: ./.github/actions/ensure-rustup

- name: Install Rust toolchain
run: |
rustup toolchain install stable --profile minimal
Expand Down Expand Up @@ -124,7 +131,8 @@ jobs:
conformance:
name: conformance (${{ matrix.suite }})
needs: [discover_suites, build_handlers]
runs-on: ubuntu-latest
# Same routing as above.
runs-on: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository && 'ubuntu-latest' || format('codebuild-github-actions-runner-{0}-{1}', github.run_id, github.run_attempt) }}
permissions:
contents: read
id-token: write # Required for AWS OIDC credentials
Expand Down
4 changes: 2 additions & 2 deletions src/combinator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1864,7 +1864,7 @@ mod tests {
assert_eq!(second.error_type(), Some("TypeB"));
assert_eq!(second.error_message(), Some("boom-b"));
assert_eq!(second.error_data(), None);
assert!(second.stack_trace().is_empty());
assert_eq!(second.stack_trace(), Vec::<String>::new());
}

/// An `AllFailed` record WITHOUT a losers payload (written before the
Expand Down Expand Up @@ -2095,7 +2095,7 @@ mod tests {
assert_eq!(err.status(), Some("FAILED"));
let wire = err.wire().expect("live EmptyInput carries wire record");
assert_eq!(wire.error_type(), Some("CombinatorError.EmptyInput"));
assert!(!wire.stack_trace().is_empty());
assert_ne!(wire.stack_trace(), Vec::<String>::new());
}

#[tokio::test]
Expand Down
2 changes: 1 addition & 1 deletion src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2089,7 +2089,7 @@ mod tests {
// wire name because the top error is an OperationError.
assert_eq!(wire.error_type(), Some("StepError"));
// A fresh failure captures a stack trace.
assert!(!wire.stack_trace().is_empty());
assert_ne!(wire.stack_trace(), Vec::<String>::new());
// No error_data anywhere in the chain: none synthesized.
assert_eq!(wire.error_data(), None);
}
Expand Down
6 changes: 3 additions & 3 deletions src/map_parallel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4928,7 +4928,7 @@ mod tests {
assert_eq!(result.status(), BatchStatus::Succeeded);
assert_eq!(result.success_count(), 2);
assert_eq!(result.failure_count(), 0);
assert!(result.errors().is_empty());
assert_eq!(result.errors(), []);
}

/// `BatchStatus` must render exactly the strings the old string-typed
Expand Down Expand Up @@ -5096,7 +5096,7 @@ mod tests {
assert_eq!(ok.result, Some(7));
assert!(ok.error_type.is_none());
assert!(ok.error_data.is_none());
assert!(ok.stack_trace.is_empty());
assert_eq!(ok.stack_trace, Vec::<String>::new());
}

/// A failed item that recorded no error type (a live item defaults to
Expand Down Expand Up @@ -5144,7 +5144,7 @@ mod tests {
// A payload written before `errData`/`stackTrace` existed replays
// with an empty failure payload rather than a fabricated one.
assert_eq!(item.error_data, None);
assert!(item.stack_trace.is_empty());
assert_eq!(item.stack_trace, Vec::<String>::new());
assert_eq!(replayed.errors()[0].error_type, None);
}

Expand Down
12 changes: 9 additions & 3 deletions src/test_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2056,6 +2056,11 @@ impl ExecutionClient for Backend {
// calls through, reject BEFORE touching any state, so the
// rejected write persists nothing, exactly like a service-side
// rejection.
//
// `fetch_update` is deprecated since Rust 1.99 in favour of
// `try_update`, which is only stable from 1.95; switch once the
// MSRV (1.94.1) moves past it.
#[allow(deprecated)]
let skipped = self
.checkpoint_failures_skip
.fetch_update(
Expand All @@ -2064,16 +2069,17 @@ impl ExecutionClient for Backend {
|remaining| remaining.checked_sub(1),
)
.is_ok();
if !skipped
#[allow(deprecated)]
let remaining_failure = !skipped
&& self
.checkpoint_failures_remaining
.fetch_update(
std::sync::atomic::Ordering::SeqCst,
std::sync::atomic::Ordering::SeqCst,
|remaining| remaining.checked_sub(1),
)
.is_ok()
{
.is_ok();
if remaining_failure {
let retryable = self
.checkpoint_failures_retryable
.load(std::sync::atomic::Ordering::SeqCst);
Expand Down
Loading