From 198e59c75008f0f7cea4d440764a8f3d22a5f55e Mon Sep 17 00:00:00 2001 From: Linwei Shang Date: Thu, 21 May 2026 10:46:40 -0400 Subject: [PATCH 01/12] feat(ic-agent): make rustls crypto provider selectable via cargo features Reqwest 0.13's `rustls` feature hardcodes aws-lc-rs as the crypto provider, which conflicts with workspaces (e.g. dfinity/ic) that still pull in reqwest 0.12 with ring: Cargo unifies rustls 0.23 with both `ring` and `aws_lc_rs` features active, and `CryptoProvider::from_crate_features()` returns None, breaking any direct rustls user that relies on the process-default builder pattern. Switch ic-agent's reqwest feature from `rustls` to `rustls-no-provider`, take a direct optional `rustls` dep, and expose two new features: - `tls-aws-lc-rs` (default): installs aws-lc-rs as the process-wide rustls default in the Agent::new default-client path - `tls-ring`: installs ring instead Features are additive: when both are on, aws-lc-rs wins (enabling `tls-ring` while the default is on never silently flips behavior). Downstreams who need ring opt out of defaults: `default-features = false, features = ["pem", "tls-ring"]`. Tests cover three scenarios in process-isolated integration test files: matching-provider install, idempotency when an application installs first, and the expected panic when neither feature is enabled. CI runs the default (aws-lc-rs), --all-features (both on, aws-lc-rs wins), tls-ring alone, and the no-TLS panic path; the previous blanket `--no-default-features` pass is dropped (no-op for crates without features, broken for ic-agent without a provider). Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/test.yml | 27 +++++++++++- Cargo.lock | 1 + Cargo.toml | 1 + ic-agent/Cargo.toml | 12 +++++- .../agent/http_transport/reqwest_transport.rs | 1 + ic-agent/src/agent/mod.rs | 31 +++++++++++++ ic-agent/tests/crypto_provider_default.rs | 40 +++++++++++++++++ ic-agent/tests/crypto_provider_idempotent.rs | 43 +++++++++++++++++++ ic-agent/tests/crypto_provider_neither.rs | 21 +++++++++ 9 files changed, 173 insertions(+), 4 deletions(-) create mode 100644 ic-agent/tests/crypto_provider_default.rs create mode 100644 ic-agent/tests/crypto_provider_idempotent.rs create mode 100644 ic-agent/tests/crypto_provider_neither.rs diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 312be1867..7626ec66a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -70,18 +70,41 @@ jobs: - name: Run Tests shell: bash run: | - # Test all features and no features for each package. + # Test all features for each package. # ref-tests is excluded here and run separately with --test-threads=1 # to avoid exhausting OS thread limits when pocket-ic spawns many threads. + # + # The previous `--no-default-features` pass was dropped: only ic-agent + # and ic-utils have features, ic-utils has no default features (so + # `--no-default-features` was a no-op), and ic-agent's lib tests panic + # without a TLS provider compiled in. Minimal-feature coverage for + # ic-agent is provided by the two dedicated steps below. for p in $(cargo metadata --no-deps --format-version 1 | jq -r '.packages[] | select(.name != "ref-tests") | .manifest_path'); do pushd $(dirname $p) cargo test --all-features --no-fail-fast - cargo test --no-default-features --no-fail-fast popd done env: RUST_BACKTRACE: 1 + - name: Run Tests (ic-agent tls-ring) + # Exercises tls-ring alone (the minimal-feature path dfinity/ic relies + # on to avoid rustls provider conflicts). The default test pass above + # covers aws-lc-rs alone and both providers together (--all-features). + shell: bash + run: | + cargo test -p ic-agent --no-default-features --features pem,tls-ring --no-fail-fast + + - name: Run Tests (ic-agent no-tls panic path) + # When no TLS feature is enabled, building the default reqwest client + # must panic with "No provider set". The integration test asserts this + # via #[should_panic]; run only that test, since the rest of ic-agent's + # lib tests would also panic for the same (expected) reason. + shell: bash + run: | + cargo test -p ic-agent --no-default-features --features pem \ + --test crypto_provider_neither --no-fail-fast + - name: Run Tests (WASM) if: ${{ matrix.os == 'ubuntu-latest' }} run: | diff --git a/Cargo.lock b/Cargo.lock index 59c300fd2..9c0f282ec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1451,6 +1451,7 @@ dependencies = [ "ref-tests", "reqwest 0.13.2", "ring", + "rustls", "sec1", "serde", "serde_bytes", diff --git a/Cargo.toml b/Cargo.toml index 3c16e20d2..2bad3b6be 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -88,6 +88,7 @@ rand = "0.10.1" rangemap = "1.7" reqwest = { version = "0.13.2", default-features = false } ring = "0.17" +rustls = { version = "0.23", default-features = false } sec1 = "0.7.2" semver = "1.0.7" serde = "1.0.215" diff --git a/ic-agent/Cargo.toml b/ic-agent/Cargo.toml index 38160d87b..4df34b1e5 100644 --- a/ic-agent/Cargo.toml +++ b/ic-agent/Cargo.toml @@ -19,9 +19,15 @@ targets = ["x86_64-unknown-linux-gnu", "wasm32-unknown-unknown"] features = ["wasm-bindgen"] [features] -default = ["pem"] +default = ["pem", "tls-aws-lc-rs"] pem = ["dep:pem", "pkcs8/pem"] ring = ["dep:ring"] +# rustls crypto provider selection. Features are additive: if both are enabled, +# aws-lc-rs is installed as the process-wide rustls default (the ring code is +# compiled in but unused). To use ring, set `default-features = false` and +# enable `tls-ring` explicitly. +tls-aws-lc-rs = ["dep:rustls", "rustls/aws-lc-rs"] +tls-ring = ["dep:rustls", "rustls/ring"] ic_ref_tests = ["default"] # Used to separate integration tests for ic-ref which need a server running. wasm-bindgen = [ "dep:js-sys", @@ -69,8 +75,9 @@ pem = { workspace = true, optional = true } pkcs8 = { workspace = true, features = ["std"] } rand = { workspace = true } rangemap = { workspace = true } -reqwest = { workspace = true, default-features = false, features = ["blocking", "json", "rustls", "stream"] } +reqwest = { workspace = true, default-features = false, features = ["blocking", "json", "rustls-no-provider", "stream"] } ring = { workspace = true, optional = true } +rustls = { workspace = true, default-features = false, features = ["std", "tls12"], optional = true } sec1 = { workspace = true, features = ["pem"] } serde = { workspace = true, features = ["derive"] } serde_bytes = { workspace = true } @@ -103,6 +110,7 @@ tracing-subscriber = { workspace = true } [target.'cfg(not(target_family = "wasm"))'.dev-dependencies] mockito = { workspace = true } +rustls = { workspace = true, features = ["aws-lc-rs", "ring"] } tokio = { workspace = true, features = ["full"] } [target.'cfg(target_family = "wasm")'.dev-dependencies] diff --git a/ic-agent/src/agent/http_transport/reqwest_transport.rs b/ic-agent/src/agent/http_transport/reqwest_transport.rs index 5876c281b..b7a16cd60 100644 --- a/ic-agent/src/agent/http_transport/reqwest_transport.rs +++ b/ic-agent/src/agent/http_transport/reqwest_transport.rs @@ -28,6 +28,7 @@ impl ReqwestTransport { pub fn create>(url: U) -> Result { #[cfg(not(target_family = "wasm"))] { + crate::agent::install_default_crypto_provider(); Self::create_with_client( url, Client::builder() diff --git a/ic-agent/src/agent/mod.rs b/ic-agent/src/agent/mod.rs index 284868a2f..1b378ddc3 100644 --- a/ic-agent/src/agent/mod.rs +++ b/ic-agent/src/agent/mod.rs @@ -211,6 +211,36 @@ impl fmt::Debug for Agent { } } +/// Install a process-wide rustls [`CryptoProvider`] if none is already installed. +/// +/// Called when [`Agent::new`] builds its default [`reqwest::Client`]. Reqwest's +/// `rustls-no-provider` feature defers the provider choice to whichever +/// `CryptoProvider` is registered as the process default; this function makes +/// the choice based on the active cargo features so users don't have to install +/// one themselves. +/// +/// The call is idempotent: `install_default` returns `Err` if a default was +/// already set, which we ignore. That means an application that installs its +/// own provider before constructing an `Agent` wins. +/// +/// Feature precedence: `tls-aws-lc-rs` wins over `tls-ring` when both are +/// enabled, preserving additivity (enabling `tls-ring` on top of the default +/// never silently flips the installed provider). +#[cfg(not(target_family = "wasm"))] +pub(crate) fn install_default_crypto_provider() { + #[cfg(feature = "tls-aws-lc-rs")] + { + let _ = rustls::crypto::aws_lc_rs::default_provider().install_default(); + } + #[cfg(all(feature = "tls-ring", not(feature = "tls-aws-lc-rs")))] + { + let _ = rustls::crypto::ring::default_provider().install_default(); + } + // If neither feature is enabled, do nothing. The user is expected to either + // call `with_http_client` or install a provider themselves before building + // the agent; otherwise reqwest will panic when constructing its TLS config. +} + impl Agent { /// Create an instance of an [`AgentBuilder`] for building an [`Agent`]. This is simpler than /// using the [`AgentConfig`] and [`Agent::new()`]. @@ -225,6 +255,7 @@ impl Agent { client: config.client.unwrap_or_else(|| { #[cfg(not(target_family = "wasm"))] { + install_default_crypto_provider(); Client::builder() .use_rustls_tls() .timeout(Duration::from_secs(360)) diff --git a/ic-agent/tests/crypto_provider_default.rs b/ic-agent/tests/crypto_provider_default.rs new file mode 100644 index 000000000..2662fc014 --- /dev/null +++ b/ic-agent/tests/crypto_provider_default.rs @@ -0,0 +1,40 @@ +//! Verify that `Agent::new` installs the rustls `CryptoProvider` matching the +//! active cargo features (`tls-aws-lc-rs` or `tls-ring`). +//! +//! In its own integration-test file for process isolation: `CryptoProvider`'s +//! process-wide default slot is one-shot, so sharing a binary with other tests +//! that pre-install a provider would make ordering matter. + +#![cfg(not(target_family = "wasm"))] +#![cfg(any(feature = "tls-ring", feature = "tls-aws-lc-rs"))] + +use ic_agent::Agent; +use rustls::crypto::CryptoProvider; + +/// Address of the static target behind a `&'static dyn Trait`. Each rustls +/// provider registers its own `&'static dyn SecureRandom` pointing at a +/// distinct singleton, so two providers compare unequal here. +fn secure_random_addr(p: &CryptoProvider) -> *const () { + (p.secure_random as *const dyn rustls::crypto::SecureRandom).cast() +} + +#[test] +fn default_client_installs_expected_provider() { + let _agent = Agent::builder() + .with_url("https://ic0.app") + .build() + .expect("Agent build should succeed with a provider feature enabled"); + + let installed = CryptoProvider::get_default().expect("a provider must be installed"); + + // aws-lc-rs wins when both features are on (additive rule). + #[cfg(feature = "tls-aws-lc-rs")] + let expected = rustls::crypto::aws_lc_rs::default_provider(); + #[cfg(all(feature = "tls-ring", not(feature = "tls-aws-lc-rs")))] + let expected = rustls::crypto::ring::default_provider(); + + assert!( + std::ptr::eq(secure_random_addr(installed), secure_random_addr(&expected)), + "installed provider does not match the one selected by active features" + ); +} diff --git a/ic-agent/tests/crypto_provider_idempotent.rs b/ic-agent/tests/crypto_provider_idempotent.rs new file mode 100644 index 000000000..5f6707895 --- /dev/null +++ b/ic-agent/tests/crypto_provider_idempotent.rs @@ -0,0 +1,43 @@ +//! Verify that `Agent::new` does not overwrite a `CryptoProvider` that the +//! application installed first, and does not panic in that case. +//! +//! In its own integration-test file for process isolation: the prior test in +//! `crypto_provider_default.rs` is the inverse setup (ic-agent installs first), +//! and `CryptoProvider`'s default slot is one-shot per process. + +#![cfg(not(target_family = "wasm"))] +#![cfg(any(feature = "tls-ring", feature = "tls-aws-lc-rs"))] + +use ic_agent::Agent; +use rustls::crypto::CryptoProvider; + +fn secure_random_addr(p: &CryptoProvider) -> *const () { + (p.secure_random as *const dyn rustls::crypto::SecureRandom).cast() +} + +#[test] +fn application_provider_wins() { + // Pick the *opposite* of what ic-agent would install, to detect overwrites. + #[cfg(feature = "tls-aws-lc-rs")] + let user_choice = rustls::crypto::ring::default_provider(); + #[cfg(all(feature = "tls-ring", not(feature = "tls-aws-lc-rs")))] + let user_choice = rustls::crypto::aws_lc_rs::default_provider(); + + let user_addr = secure_random_addr(&user_choice); + user_choice + .install_default() + .expect("test must run before any other provider is installed"); + + // ic-agent builds its default client; should not panic, and should not + // change the installed default. + let _agent = Agent::builder() + .with_url("https://ic0.app") + .build() + .expect("Agent build should succeed"); + + let installed = CryptoProvider::get_default().expect("provider still installed"); + assert!( + std::ptr::eq(secure_random_addr(installed), user_addr), + "ic-agent overwrote a previously installed CryptoProvider" + ); +} diff --git a/ic-agent/tests/crypto_provider_neither.rs b/ic-agent/tests/crypto_provider_neither.rs new file mode 100644 index 000000000..aeaf6bf9f --- /dev/null +++ b/ic-agent/tests/crypto_provider_neither.rs @@ -0,0 +1,21 @@ +//! Verify that building a default `Agent` panics when neither `tls-ring` nor +//! `tls-aws-lc-rs` is enabled, because reqwest's `rustls-no-provider` feature +//! requires a process-wide `CryptoProvider` to be installed and ic-agent's +//! helper is a no-op in this configuration. +//! +//! Only compiled when *neither* TLS feature is active. The companion CI step +//! exercises this with `--no-default-features --features pem`. + +#![cfg(not(target_family = "wasm"))] +#![cfg(not(any(feature = "tls-ring", feature = "tls-aws-lc-rs")))] + +use ic_agent::Agent; + +#[test] +#[should_panic] +fn default_client_panics_without_provider() { + // Reqwest panics with "No provider set" when it cannot find an installed + // CryptoProvider. `Agent::builder().build()` triggers `Client::builder()` + // which is where the panic surfaces. + let _ = Agent::builder().with_url("https://ic0.app").build(); +} From b669b612fd8ef3e72e107198430fda5ae3e39390 Mon Sep 17 00:00:00 2001 From: Linwei Shang Date: Thu, 21 May 2026 10:49:47 -0400 Subject: [PATCH 02/12] chore(ic-agent): drop deprecated http_transport module The `http_transport` module (`ReqwestTransport`, `AgentBuilder::with_transport`, `AgentBuilder::with_arc_transport`) has been deprecated since v0.38.0 in favor of the dedicated builder methods (`with_url`, `with_http_client`, `with_arc_route_provider`, `with_max_response_body_size`, `with_max_tcp_error_retries`). The original comment in `agent/mod.rs` already slated it for removal "after 0.40"; 0.48.0 is the natural breakage window alongside the rustls provider feature changes. No workspace or dfinity/sdk consumer references these symbols. Co-Authored-By: Claude Opus 4.7 (1M context) --- ic-agent/src/agent/http_transport/mod.rs | 8 -- .../agent/http_transport/reqwest_transport.rs | 117 ------------------ ic-agent/src/agent/mod.rs | 4 - 3 files changed, 129 deletions(-) delete mode 100644 ic-agent/src/agent/http_transport/mod.rs delete mode 100644 ic-agent/src/agent/http_transport/reqwest_transport.rs diff --git a/ic-agent/src/agent/http_transport/mod.rs b/ic-agent/src/agent/http_transport/mod.rs deleted file mode 100644 index bf9376675..000000000 --- a/ic-agent/src/agent/http_transport/mod.rs +++ /dev/null @@ -1,8 +0,0 @@ -//! This module has been deprecated in favor of builder methods on `AgentBuilder`. - -#[deprecated(since = "0.38.0", note = "use the AgentBuilder methods")] -#[doc(hidden)] -pub mod reqwest_transport; -#[doc(hidden)] -#[allow(deprecated)] -pub use reqwest_transport::ReqwestTransport; diff --git a/ic-agent/src/agent/http_transport/reqwest_transport.rs b/ic-agent/src/agent/http_transport/reqwest_transport.rs deleted file mode 100644 index b7a16cd60..000000000 --- a/ic-agent/src/agent/http_transport/reqwest_transport.rs +++ /dev/null @@ -1,117 +0,0 @@ -//! This module has been deprecated in favor of builder methods on `AgentBuilder`. -#![allow(deprecated)] -pub use reqwest; -use std::sync::Arc; - -use reqwest::Client; - -use crate::{ - agent::{ - route_provider::{RoundRobinRouteProvider, RouteProvider}, - AgentBuilder, - }, - AgentError, -}; - -/// A legacy configuration object. `AgentBuilder::with_transport` will apply these settings to the builder. -#[derive(Debug, Clone)] -pub struct ReqwestTransport { - route_provider: Arc, - client: Client, - max_response_body_size: Option, - max_tcp_error_retries: usize, -} - -impl ReqwestTransport { - /// Equivalent to [`AgentBuilder::with_url`]. - #[deprecated(since = "0.38.0", note = "Use AgentBuilder::with_url")] - pub fn create>(url: U) -> Result { - #[cfg(not(target_family = "wasm"))] - { - crate::agent::install_default_crypto_provider(); - Self::create_with_client( - url, - Client::builder() - .use_rustls_tls() - .timeout(std::time::Duration::from_secs(360)) - .build() - .expect("Could not create HTTP client."), - ) - } - #[cfg(all(target_family = "wasm", feature = "wasm-bindgen"))] - { - Self::create_with_client(url, Client::new()) - } - } - - /// Equivalent to [`AgentBuilder::with_url`] and [`AgentBuilder::with_http_client`]. - #[deprecated( - since = "0.38.0", - note = "Use AgentBuilder::with_url and AgentBuilder::with_http_client" - )] - pub fn create_with_client>(url: U, client: Client) -> Result { - let route_provider = Arc::new(RoundRobinRouteProvider::new(vec![url.into()])?); - Self::create_with_client_route(route_provider, client) - } - - /// Equivalent to [`AgentBuilder::with_http_client`] and [`AgentBuilder::with_route_provider`]. - #[deprecated( - since = "0.38.0", - note = "Use AgentBuilder::with_http_client and AgentBuilder::with_arc_route_provider" - )] - pub fn create_with_client_route( - route_provider: Arc, - client: Client, - ) -> Result { - Ok(Self { - route_provider, - client, - max_response_body_size: None, - max_tcp_error_retries: 0, - }) - } - - /// Equivalent to [`AgentBuilder::with_max_response_body_size`]. - #[deprecated( - since = "0.38.0", - note = "Use AgentBuilder::with_max_response_body_size" - )] - pub fn with_max_response_body_size(self, max_response_body_size: usize) -> Self { - ReqwestTransport { - max_response_body_size: Some(max_response_body_size), - ..self - } - } - - /// Equivalent to [`AgentBuilder::with_max_tcp_error_retries`]. - #[deprecated( - since = "0.38.0", - note = "Use AgentBuilder::with_max_tcp_error_retries" - )] - pub fn with_max_tcp_errors_retries(self, retries: usize) -> Self { - ReqwestTransport { - max_tcp_error_retries: retries, - ..self - } - } -} - -impl AgentBuilder { - #[doc(hidden)] - #[deprecated(since = "0.38.0", note = "Use the dedicated methods on AgentBuilder")] - pub fn with_transport(self, transport: ReqwestTransport) -> Self { - let mut builder = self - .with_arc_route_provider(transport.route_provider) - .with_http_client(transport.client) - .with_max_tcp_error_retries(transport.max_tcp_error_retries); - if let Some(max_size) = transport.max_response_body_size { - builder = builder.with_max_response_body_size(max_size); - } - builder - } - #[doc(hidden)] - #[deprecated(since = "0.38.0", note = "Use the dedicated methods on AgentBuilder")] - pub fn with_arc_transport(self, transport: Arc) -> Self { - self.with_transport((*transport).clone()) - } -} diff --git a/ic-agent/src/agent/mod.rs b/ic-agent/src/agent/mod.rs index 1b378ddc3..e53e1478e 100644 --- a/ic-agent/src/agent/mod.rs +++ b/ic-agent/src/agent/mod.rs @@ -2,10 +2,6 @@ pub(crate) mod agent_config; pub mod agent_error; pub(crate) mod builder; -// delete this module after 0.40 -#[doc(hidden)] -#[deprecated(since = "0.38.0", note = "use the AgentBuilder methods")] -pub mod http_transport; pub(crate) mod nonce; pub(crate) mod response_authentication; pub mod route_provider; From 4b5be20b1bf341738226aea08883680c5d152326 Mon Sep 17 00:00:00 2001 From: Linwei Shang Date: Thu, 21 May 2026 10:51:19 -0400 Subject: [PATCH 03/12] chore: prepare 0.48.0 release MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bump workspace version 0.47.3 → 0.48.0 and add a CHANGELOG entry covering: - new `tls-aws-lc-rs` (default) / `tls-ring` cargo features for rustls crypto provider selection - breaking: `default-features = false` now requires an explicit TLS feature (or `with_http_client`) to avoid a runtime panic - breaking: removal of the long-deprecated `http_transport` module Co-Authored-By: Claude Opus 4.7 (1M context) --- CHANGELOG.md | 14 ++++++++++++++ Cargo.lock | 14 +++++++------- Cargo.toml | 10 +++++----- 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 309d68435..05a796f8e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +## [0.48.0] - 2026-05-21 + +* `ic-agent`: Added cargo features `tls-aws-lc-rs` (default) and `tls-ring` to select the rustls crypto provider used by the default `reqwest::Client`. Features are additive: when both are enabled, aws-lc-rs is installed as the process-wide rustls default. Reqwest's `rustls` feature (which hardcoded aws-lc-rs) has been swapped for `rustls-no-provider`; ic-agent now installs the chosen provider via `CryptoProvider::install_default()` on the default-client path, idempotently (an application-installed provider is not overwritten). When the user supplies a client via `AgentBuilder::with_http_client`, ic-agent installs no provider. + +### Breaking Changes + +* `ic-agent`: + * Default feature set changed from `["pem"]` to `["pem", "tls-aws-lc-rs"]`. Stock-default users are unaffected (aws-lc-rs has been the only crypto provider available since 0.46.0). + * Consumers using `default-features = false` must now opt into a TLS feature, otherwise `Agent::new` panics with "No provider set" when constructing the default reqwest client. + * Migration: add `tls-aws-lc-rs` (matches previous behavior) or `tls-ring` (matches reqwest 0.12 behavior) to the feature list, or supply your own `reqwest::Client` via `AgentBuilder::with_http_client`. + * Example: `ic-agent = { version = "0.48", default-features = false, features = ["pem", "tls-ring"] }`. + * Removed the deprecated `http_transport` module (`ReqwestTransport`, `AgentBuilder::with_transport`, `AgentBuilder::with_arc_transport`), deprecated since 0.38.0. + * Migration: use the dedicated `AgentBuilder` methods (`with_url`, `with_http_client`, `with_arc_route_provider`, `with_max_response_body_size`, `with_max_tcp_error_retries`). + ## [0.47.3] - 2026-05-15 * `ic-agent`: Added the `EffectiveId` enum (`Canister(Principal)` | `Subnet(Principal)`) and widened `Agent::update_signed`, `query_signed`, `request_status_signed`, `request_status_raw`, `wait`, `wait_signed`, `read_state_raw`, `verify`, and `sign_request_status` to accept `impl Into`. Passing a bare `Principal` is unchanged (treated as `EffectiveId::Canister(_)`); passing `EffectiveId::Subnet(_)` routes to the subnet-scoped HTTP endpoints (`/api/v4/subnet//call`, `/api/v3/subnet//read_state`, `/api/v3/subnet//query`) introduced in IC interface spec 0.60.0. diff --git a/Cargo.lock b/Cargo.lock index 9c0f282ec..dd412e15f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1413,7 +1413,7 @@ dependencies = [ [[package]] name = "ic-agent" -version = "0.47.3" +version = "0.48.0" dependencies = [ "anyhow", "arc-swap", @@ -1436,7 +1436,7 @@ dependencies = [ "http-body-util", "ic-certification", "ic-ed25519", - "ic-transport-types 0.47.3", + "ic-transport-types 0.48.0", "ic-verify-bls-signature", "ic_principal", "js-sys", @@ -1504,7 +1504,7 @@ dependencies = [ [[package]] name = "ic-identity-hsm" -version = "0.47.3" +version = "0.48.0" dependencies = [ "hex", "ic-agent", @@ -1556,7 +1556,7 @@ dependencies = [ [[package]] name = "ic-transport-types" -version = "0.47.3" +version = "0.48.0" dependencies = [ "candid", "hex", @@ -1573,7 +1573,7 @@ dependencies = [ [[package]] name = "ic-utils" -version = "0.47.3" +version = "0.48.0" dependencies = [ "async-trait", "candid", @@ -1743,7 +1743,7 @@ dependencies = [ [[package]] name = "icx" -version = "0.47.3" +version = "0.48.0" dependencies = [ "anyhow", "candid", @@ -1762,7 +1762,7 @@ dependencies = [ [[package]] name = "icx-cert" -version = "0.47.3" +version = "0.48.0" dependencies = [ "anyhow", "base64 0.22.1", diff --git a/Cargo.toml b/Cargo.toml index 2bad3b6be..6c5854fc0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ members = [ ] [workspace.package] -version = "0.47.3" +version = "0.48.0" authors = ["DFINITY Stiftung "] edition = "2021" repository = "https://github.com/dfinity/agent-rs" @@ -30,10 +30,10 @@ license = "Apache-2.0" # a comment listing those crates). Otherwise, features are declared in the individual crate Cargo.toml. # # The path dependencies below ensure all workspace members use the same version of internal crates. -ic-agent = { path = "ic-agent", version = "0.47.3", default-features = false } -ic-identity-hsm = { path = "ic-identity-hsm", version = "0.47.3" } -ic-transport-types = { path = "ic-transport-types", version = "0.47.3" } -ic-utils = { path = "ic-utils", version = "0.47.3" } +ic-agent = { path = "ic-agent", version = "0.48.0", default-features = false } +ic-identity-hsm = { path = "ic-identity-hsm", version = "0.48.0" } +ic-transport-types = { path = "ic-transport-types", version = "0.48.0" } +ic-utils = { path = "ic-utils", version = "0.48.0" } ic-utils-bindgen = { path = "ic-utils-bindgen" } ref-tests = { path = "ref-tests" } From 80a15f163253c2d8026137952addde85d7056e3e Mon Sep 17 00:00:00 2001 From: Linwei Shang Date: Thu, 21 May 2026 10:56:26 -0400 Subject: [PATCH 04/12] perf(ic-agent): skip default_provider() construction when already installed `install_default_crypto_provider` was previously building a fresh `CryptoProvider` on every `Agent::new` only to discard it when `install_default()` returned `Err` (default already set). Fast-path the common warm case with `CryptoProvider::get_default().is_some()`. The check has a benign TOCTOU race: `install_default()` is itself atomic, so concurrent installers still produce a single winner. Co-Authored-By: Claude Opus 4.7 (1M context) --- ic-agent/src/agent/mod.rs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/ic-agent/src/agent/mod.rs b/ic-agent/src/agent/mod.rs index e53e1478e..683606b0a 100644 --- a/ic-agent/src/agent/mod.rs +++ b/ic-agent/src/agent/mod.rs @@ -224,12 +224,19 @@ impl fmt::Debug for Agent { /// never silently flips the installed provider). #[cfg(not(target_family = "wasm"))] pub(crate) fn install_default_crypto_provider() { - #[cfg(feature = "tls-aws-lc-rs")] + #[cfg(any(feature = "tls-aws-lc-rs", feature = "tls-ring"))] { + // Cheap fast-path: if a default is already installed (by us on a prior + // `Agent::new`, or by the application), skip constructing a provider. + // The check has a benign TOCTOU race — `install_default()` is itself + // atomic, so concurrent installers still produce a single winner and + // the others' `Err` is discarded. + if rustls::crypto::CryptoProvider::get_default().is_some() { + return; + } + #[cfg(feature = "tls-aws-lc-rs")] let _ = rustls::crypto::aws_lc_rs::default_provider().install_default(); - } - #[cfg(all(feature = "tls-ring", not(feature = "tls-aws-lc-rs")))] - { + #[cfg(all(feature = "tls-ring", not(feature = "tls-aws-lc-rs")))] let _ = rustls::crypto::ring::default_provider().install_default(); } // If neither feature is enabled, do nothing. The user is expected to either From fb4ea7356fb8412b01a6b89cbe41b634203fcb9b Mon Sep 17 00:00:00 2001 From: Linwei Shang Date: Thu, 21 May 2026 11:23:39 -0400 Subject: [PATCH 05/12] fix(ic-agent): make rustls dep target-conditional so wasm builds with default features The default `tls-aws-lc-rs` feature pulled in aws-lc-sys, which fails to cross-compile to wasm32-unknown-unknown. Move the rustls dep into the non-wasm target table so wasm consumers can use default features unchanged. The runtime crypto-provider install was already `cfg(not(target_family = "wasm"))`. Co-Authored-By: Claude Opus 4.7 (1M context) --- CHANGELOG.md | 4 ++-- ic-agent/Cargo.toml | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 05a796f8e..2b6713ad5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,8 +15,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Breaking Changes * `ic-agent`: - * Default feature set changed from `["pem"]` to `["pem", "tls-aws-lc-rs"]`. Stock-default users are unaffected (aws-lc-rs has been the only crypto provider available since 0.46.0). - * Consumers using `default-features = false` must now opt into a TLS feature, otherwise `Agent::new` panics with "No provider set" when constructing the default reqwest client. + * Default feature set changed from `["pem"]` to `["pem", "tls-aws-lc-rs"]`. Stock-default users are unaffected (aws-lc-rs has been the only crypto provider available since 0.46.0). The `rustls` dependency is declared `cfg(not(target_family = "wasm"))`, so wasm consumers using default features are also unaffected — aws-lc-sys (which does not cross-compile to wasm) is not pulled in on wasm targets. + * Consumers using `default-features = false` on a non-wasm target must now opt into a TLS feature, otherwise `Agent::new` panics with "No provider set" when constructing the default reqwest client. * Migration: add `tls-aws-lc-rs` (matches previous behavior) or `tls-ring` (matches reqwest 0.12 behavior) to the feature list, or supply your own `reqwest::Client` via `AgentBuilder::with_http_client`. * Example: `ic-agent = { version = "0.48", default-features = false, features = ["pem", "tls-ring"] }`. * Removed the deprecated `http_transport` module (`ReqwestTransport`, `AgentBuilder::with_transport`, `AgentBuilder::with_arc_transport`), deprecated since 0.38.0. diff --git a/ic-agent/Cargo.toml b/ic-agent/Cargo.toml index 4df34b1e5..9e8230bb8 100644 --- a/ic-agent/Cargo.toml +++ b/ic-agent/Cargo.toml @@ -77,7 +77,6 @@ rand = { workspace = true } rangemap = { workspace = true } reqwest = { workspace = true, default-features = false, features = ["blocking", "json", "rustls-no-provider", "stream"] } ring = { workspace = true, optional = true } -rustls = { workspace = true, default-features = false, features = ["std", "tls12"], optional = true } sec1 = { workspace = true, features = ["pem"] } serde = { workspace = true, features = ["derive"] } serde_bytes = { workspace = true } @@ -92,6 +91,12 @@ tracing = { workspace = true, optional = true } url = { workspace = true } [target.'cfg(not(target_family = "wasm"))'.dependencies] +# rustls is only used off-wasm: on wasm the reqwest client is the browser's +# fetch API, and the rustls crypto-provider install is gated +# `cfg(not(target_family = "wasm"))`. Keeping the dep target-conditional means +# wasm consumers can use the default features without pulling in aws-lc-sys +# (which doesn't cross-compile to wasm32-unknown-unknown). +rustls = { workspace = true, default-features = false, features = ["std", "tls12"], optional = true } tokio = { workspace = true, default-features = false, features = ["time", "sync"] } [target.'cfg(target_family = "wasm")'.dependencies] From 56a7e87b8d52ec8fa03012e2fd70b68ecf349306 Mon Sep 17 00:00:00 2001 From: Linwei Shang Date: Thu, 21 May 2026 11:26:43 -0400 Subject: [PATCH 06/12] fix(ic-agent): install rustls provider in dynamic-routing doctest The customized-instantiation example builds a reqwest::Client directly instead of going through Agent::new, so reqwest's rustls-no-provider mode panicked with "No provider set". Install the aws-lc-rs provider at the start of main and note why. Co-Authored-By: Claude Opus 4.7 (1M context) --- ic-agent/src/agent/route_provider/dynamic_routing/mod.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ic-agent/src/agent/route_provider/dynamic_routing/mod.rs b/ic-agent/src/agent/route_provider/dynamic_routing/mod.rs index e914bcd10..7e408a5dd 100644 --- a/ic-agent/src/agent/route_provider/dynamic_routing/mod.rs +++ b/ic-agent/src/agent/route_provider/dynamic_routing/mod.rs @@ -77,7 +77,11 @@ //! // Node::new("")?, //! ]; //! -//! // Build dynamic route provider with HTTP client +//! // Build dynamic route provider with HTTP client. Because the client is +//! // built directly (not via `Agent::new`), a rustls `CryptoProvider` must +//! // be installed before `reqwest::Client::new()` — `Agent::new` would +//! // normally do this for you. +//! let _ = rustls::crypto::aws_lc_rs::default_provider().install_default(); //! let http_client = Arc::new(reqwest::Client::new()); //! let route_provider = DynamicRouteProviderBuilder::new(seed_nodes, http_client, None) //! // Set how often to fetch the latest API boundary node topology From a17ce6cf7db0b37570bf421cd6226eb7aa01040c Mon Sep 17 00:00:00 2001 From: Linwei Shang Date: Thu, 21 May 2026 12:07:07 -0400 Subject: [PATCH 07/12] fix(ref-tests): enable tls-aws-lc-rs so rustls provider is installed ref-tests depended on ic-agent with only the pem feature, which compiles neither tls-aws-lc-rs nor tls-ring. install_default_crypto_provider then becomes a no-op, and Agent::new's reqwest client panics with "No provider set" because use_rustls_tls() builds the TLS config eagerly even though pocket-ic is reached over plain HTTP. Co-Authored-By: Claude Opus 4.7 (1M context) --- ref-tests/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ref-tests/Cargo.toml b/ref-tests/Cargo.toml index 16d540b32..c93996d95 100644 --- a/ref-tests/Cargo.toml +++ b/ref-tests/Cargo.toml @@ -8,7 +8,7 @@ publish = false [dependencies] candid = { workspace = true } -ic-agent = { workspace = true, features = ["pem"] } +ic-agent = { workspace = true, features = ["pem", "tls-aws-lc-rs"] } ic-certification = { workspace = true } ic-ed25519 = { workspace = true } ic-identity-hsm = { workspace = true } From 49eb43e2c4760307f4d31fa1ea10493f41e525d3 Mon Sep 17 00:00:00 2001 From: Linwei Shang Date: Thu, 21 May 2026 12:07:37 -0400 Subject: [PATCH 08/12] ci: restructure test workflow into named, single-purpose steps Reorganize the test matrix into discrete steps (workspace --all-features, ic-agent default, tls-ring only, no-TLS panic path, WASM, ref-tests, SoftHSM) with a header comment summarizing which step covers what. Switch ref-tests invocations from `cd ref-tests && cargo test` to `cargo test -p ref-tests` so they run from the workspace root. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/test.yml | 95 +++++++++++++++++++------------------- 1 file changed, 48 insertions(+), 47 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7626ec66a..1e390144c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -52,65 +52,68 @@ jobs: run: | ./scripts/download_reftest_assets.sh - - name: Run Tests (ref-tests) - # ref-tests are skipped on macOS CI: the GitHub Actions macOS runner has - # a low per-process thread limit (kern.maxthreadsperproc) that cannot be - # raised without root. pocket-ic spawns many OS threads per subnet, and - # tokio's blocking thread pool accumulates idle threads across sequential - # tests, exhausting the limit before all tests finish. This is a CI - # resource constraint — ref-tests work fine on a local macOS machine. - if: ${{ matrix.os == 'ubuntu-latest' }} + # --------------------------------------------------------------------- + # Test matrix + # --------------------------------------------------------------------- + # Native (Linux + macOS + Windows): + # - Test: workspace (--all-features) all crates except ref-tests + # - Test: ic-agent (default features) ship config smoke + # - Test: ic-agent (tls-ring only) alternate TLS provider + # - Test: ic-agent (no TLS, panic path) asserts the expected panic + # + # Linux-only (need extra system setup or have CI resource limits): + # - Test: WASM (ic-agent, wasm-bindgen) browser target + # - Test: ref-tests (pocket-ic) integration baseline + # - Test: ref-tests + SoftHSM HSM-backed identity + # --------------------------------------------------------------------- + + - name: Test - workspace (--all-features) + # Covers every crate except ref-tests with --all-features. For ic-agent + # this enables both TLS providers; the additivity rule selects the + # aws-lc-rs code path at runtime. shell: bash - run: | - cd ref-tests - cargo test --no-fail-fast -- --test-threads=1 - env: - RUST_BACKTRACE: 1 + run: cargo test --workspace --exclude ref-tests --all-features --no-fail-fast - - name: Run Tests + - name: Test - ic-agent (default features) + # Ship-config smoke: build & test with only the default feature set + # (pem + tls-aws-lc-rs), the configuration downstream users get by + # default. Catches cfg gates that compile under --all-features but + # break with defaults alone. shell: bash - run: | - # Test all features for each package. - # ref-tests is excluded here and run separately with --test-threads=1 - # to avoid exhausting OS thread limits when pocket-ic spawns many threads. - # - # The previous `--no-default-features` pass was dropped: only ic-agent - # and ic-utils have features, ic-utils has no default features (so - # `--no-default-features` was a no-op), and ic-agent's lib tests panic - # without a TLS provider compiled in. Minimal-feature coverage for - # ic-agent is provided by the two dedicated steps below. - for p in $(cargo metadata --no-deps --format-version 1 | jq -r '.packages[] | select(.name != "ref-tests") | .manifest_path'); do - pushd $(dirname $p) - cargo test --all-features --no-fail-fast - popd - done - env: - RUST_BACKTRACE: 1 + run: cargo test -p ic-agent --no-fail-fast - - name: Run Tests (ic-agent tls-ring) - # Exercises tls-ring alone (the minimal-feature path dfinity/ic relies - # on to avoid rustls provider conflicts). The default test pass above - # covers aws-lc-rs alone and both providers together (--all-features). + - name: Test - ic-agent (tls-ring only) + # Exercises the tls-ring code path (the minimal-feature config that + # dfinity/ic relies on to avoid rustls provider conflicts). shell: bash - run: | - cargo test -p ic-agent --no-default-features --features pem,tls-ring --no-fail-fast + run: cargo test -p ic-agent --no-default-features --features pem,tls-ring --no-fail-fast - - name: Run Tests (ic-agent no-tls panic path) + - name: Test - ic-agent (no TLS, panic path) # When no TLS feature is enabled, building the default reqwest client # must panic with "No provider set". The integration test asserts this # via #[should_panic]; run only that test, since the rest of ic-agent's # lib tests would also panic for the same (expected) reason. shell: bash - run: | - cargo test -p ic-agent --no-default-features --features pem \ + run: cargo test -p ic-agent --no-default-features --features pem \ --test crypto_provider_neither --no-fail-fast - - name: Run Tests (WASM) + - name: Test - WASM (ic-agent, wasm-bindgen) if: ${{ matrix.os == 'ubuntu-latest' }} - run: | - CARGO_TARGET_DIR=target/wasm wasm-pack test --chrome --headless ic-agent --features wasm-bindgen + run: CARGO_TARGET_DIR=target/wasm wasm-pack test --chrome --headless ic-agent --features wasm-bindgen + + - name: Test - ref-tests (pocket-ic) + # ref-tests are skipped on macOS CI: the GitHub Actions macOS runner has + # a low per-process thread limit (kern.maxthreadsperproc) that cannot be + # raised without root. pocket-ic spawns many OS threads per subnet, and + # tokio's blocking thread pool accumulates idle threads across sequential + # tests, exhausting the limit before all tests finish. This is a CI + # resource constraint — ref-tests work fine on a local macOS machine. + # --test-threads=1 keeps the thread footprint manageable on Linux too. + if: ${{ matrix.os == 'ubuntu-latest' }} + shell: bash + run: cargo test -p ref-tests --no-fail-fast -- --test-threads=1 - - name: Run Tests (SoftHSM) + - name: Test - ref-tests + SoftHSM if: ${{ matrix.os == 'ubuntu-latest' }} run: | set -ex @@ -118,10 +121,8 @@ jobs: # create key: pkcs11-tool -k --module $HSM_PKCS11_LIBRARY_PATH --login --slot-index $HSM_SLOT_INDEX -d $HSM_KEY_ID --key-type EC:prime256v1 --pin $HSM_PIN - cd ref-tests - cargo test --all-features --no-fail-fast -- --nocapture --test-threads=1 + cargo test -p ref-tests --all-features --no-fail-fast -- --nocapture --test-threads=1 env: - RUST_BACKTRACE: 1 HSM_PKCS11_LIBRARY_PATH: /usr/lib/softhsm/libsofthsm2.so HSM_SO_PIN: 123456 HSM_PIN: 1234 From e949b067a1d705504e1326779dfe2f38f1f0769c Mon Sep 17 00:00:00 2001 From: Linwei Shang Date: Thu, 21 May 2026 12:07:42 -0400 Subject: [PATCH 09/12] test(ic-agent): compare CryptoProvider identity via wide pointer The provider-identity assertions cast `&'static dyn SecureRandom` to `*const ()`, discarding the vtable. Two distinct ZST-backed impls would then alias on the data pointer alone. Compare the full wide pointer (data + vtable) so type identity is preserved. Co-Authored-By: Claude Opus 4.7 (1M context) --- ic-agent/tests/crypto_provider_default.rs | 12 ++++-------- ic-agent/tests/crypto_provider_idempotent.rs | 10 ++++------ 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/ic-agent/tests/crypto_provider_default.rs b/ic-agent/tests/crypto_provider_default.rs index 2662fc014..c2712b857 100644 --- a/ic-agent/tests/crypto_provider_default.rs +++ b/ic-agent/tests/crypto_provider_default.rs @@ -11,13 +11,6 @@ use ic_agent::Agent; use rustls::crypto::CryptoProvider; -/// Address of the static target behind a `&'static dyn Trait`. Each rustls -/// provider registers its own `&'static dyn SecureRandom` pointing at a -/// distinct singleton, so two providers compare unequal here. -fn secure_random_addr(p: &CryptoProvider) -> *const () { - (p.secure_random as *const dyn rustls::crypto::SecureRandom).cast() -} - #[test] fn default_client_installs_expected_provider() { let _agent = Agent::builder() @@ -33,8 +26,11 @@ fn default_client_installs_expected_provider() { #[cfg(all(feature = "tls-ring", not(feature = "tls-aws-lc-rs")))] let expected = rustls::crypto::ring::default_provider(); + // Compare the full `&'static dyn SecureRandom` wide pointer (data + vtable). + // Casting to `*const ()` would drop the vtable and could alias across + // ZST-backed impls; wide-pointer equality keeps the type identity. assert!( - std::ptr::eq(secure_random_addr(installed), secure_random_addr(&expected)), + std::ptr::eq(installed.secure_random, expected.secure_random), "installed provider does not match the one selected by active features" ); } diff --git a/ic-agent/tests/crypto_provider_idempotent.rs b/ic-agent/tests/crypto_provider_idempotent.rs index 5f6707895..5473a4440 100644 --- a/ic-agent/tests/crypto_provider_idempotent.rs +++ b/ic-agent/tests/crypto_provider_idempotent.rs @@ -11,10 +11,6 @@ use ic_agent::Agent; use rustls::crypto::CryptoProvider; -fn secure_random_addr(p: &CryptoProvider) -> *const () { - (p.secure_random as *const dyn rustls::crypto::SecureRandom).cast() -} - #[test] fn application_provider_wins() { // Pick the *opposite* of what ic-agent would install, to detect overwrites. @@ -23,7 +19,7 @@ fn application_provider_wins() { #[cfg(all(feature = "tls-ring", not(feature = "tls-aws-lc-rs")))] let user_choice = rustls::crypto::aws_lc_rs::default_provider(); - let user_addr = secure_random_addr(&user_choice); + let user_ptr: *const dyn rustls::crypto::SecureRandom = user_choice.secure_random; user_choice .install_default() .expect("test must run before any other provider is installed"); @@ -36,8 +32,10 @@ fn application_provider_wins() { .expect("Agent build should succeed"); let installed = CryptoProvider::get_default().expect("provider still installed"); + // Compare the full `&'static dyn SecureRandom` wide pointer (data + vtable); + // casting to `*const ()` would drop the vtable and could alias for ZSTs. assert!( - std::ptr::eq(secure_random_addr(installed), user_addr), + std::ptr::eq(installed.secure_random, user_ptr), "ic-agent overwrote a previously installed CryptoProvider" ); } From 627dbb7264fde848e3ce0062278a4e53430d543c Mon Sep 17 00:00:00 2001 From: Linwei Shang Date: Thu, 21 May 2026 12:20:27 -0400 Subject: [PATCH 10/12] fix(ci): collapse no-TLS step's run to one line MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The step used a backslash line-continuation inside a YAML plain scalar. Plain scalars fold newlines into spaces but don't interpret escapes, so the shell received a literal `\` followed by a space — escaping the space into a single literal-space word rather than continuing the command. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/test.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1e390144c..69594e383 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -94,8 +94,7 @@ jobs: # via #[should_panic]; run only that test, since the rest of ic-agent's # lib tests would also panic for the same (expected) reason. shell: bash - run: cargo test -p ic-agent --no-default-features --features pem \ - --test crypto_provider_neither --no-fail-fast + run: cargo test -p ic-agent --no-default-features --features pem --test crypto_provider_neither --no-fail-fast - name: Test - WASM (ic-agent, wasm-bindgen) if: ${{ matrix.os == 'ubuntu-latest' }} From 4dd17eef1c41e605ff0af5da9dbd3006ee6176c4 Mon Sep 17 00:00:00 2001 From: Linwei Shang Date: Thu, 21 May 2026 12:42:27 -0400 Subject: [PATCH 11/12] fix(ci): install Chrome via browser-actions/setup-chrome to avoid snap-store flakes The apt `chromium-chromedriver` package on Ubuntu 24.04 is a transitional shim that installs Chromium from the snap store, which intermittently times out fetching assertions from api.snapcraft.io and fails the job. Switch to browser-actions/setup-chrome, which downloads Chrome and a matching chromedriver directly without going through snap. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/test.yml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 69594e383..61c10a0a3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -25,11 +25,15 @@ jobs: target key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}-1 - - name: Install wasm-pack and chromedriver for WASM tests + - name: Install wasm-pack for WASM tests if: ${{ matrix.os == 'ubuntu-latest' }} - run: | - curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh - sudo apt-get install -y chromium-chromedriver + run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh + + - name: Install Chrome and chromedriver for WASM tests + if: ${{ matrix.os == 'ubuntu-latest' }} + uses: browser-actions/setup-chrome@2e1d749697dd1612b833dba4a722266286fbefcd # v2.1.2 + with: + install-chromedriver: true - name: Install node for SoftHSM tests if: ${{ matrix.os == 'ubuntu-latest' }} From 41db2fa8ab37751c64658b491beca40ea50e6418 Mon Sep 17 00:00:00 2001 From: Linwei Shang Date: Thu, 21 May 2026 13:05:39 -0400 Subject: [PATCH 12/12] Revert "fix(ci): install Chrome via browser-actions/setup-chrome to avoid snap-store flakes" This reverts commit 4dd17eef1c41e605ff0af5da9dbd3006ee6176c4. --- .github/workflows/test.yml | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 61c10a0a3..69594e383 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -25,15 +25,11 @@ jobs: target key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}-1 - - name: Install wasm-pack for WASM tests + - name: Install wasm-pack and chromedriver for WASM tests if: ${{ matrix.os == 'ubuntu-latest' }} - run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh - - - name: Install Chrome and chromedriver for WASM tests - if: ${{ matrix.os == 'ubuntu-latest' }} - uses: browser-actions/setup-chrome@2e1d749697dd1612b833dba4a722266286fbefcd # v2.1.2 - with: - install-chromedriver: true + run: | + curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh + sudo apt-get install -y chromium-chromedriver - name: Install node for SoftHSM tests if: ${{ matrix.os == 'ubuntu-latest' }}