diff --git a/Cargo.toml b/Cargo.toml index d11117f4d..778e5b2c2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,16 +25,16 @@ rust-version = "1.88.0" license = "Apache-2.0" [workspace.dependencies] -ic-cdk = { path = "ic-cdk", version = "0.20.0" } +# Crates of this workspace +ic-cdk = { path = "ic-cdk", version = "0.20.1" } ic-cdk-bindgen = { path = "ic-cdk-bindgen", version = "0.2.0" } ic-cdk-bitcoin-canister = { path = "ic-cdk-bitcoin-canister", version = "0.2.0" } ic-cdk-executor = { path = "ic-cdk-executor", version = "2.0.0" } -ic-cdk-macros = { path = "ic-cdk-macros", version = "=0.20.0" } +ic-cdk-macros = { path = "ic-cdk-macros", version = "=0.20.1" } ic-cdk-management-canister = { path = "ic-cdk-management-canister", version = "0.1.1" } ic-cdk-timers = { path = "ic-cdk-timers", version = "1.0.0" } ic-management-canister-types = "0.7.1" -# Crates of this workspace -ic0 = { path = "ic0", version = "1.0.1" } +ic0 = { path = "ic0", version = "1.1.0" } # Regular dependencies ## sync candid version with the doc comment in ic-cdk/README.md @@ -66,7 +66,7 @@ escargot = "0.5.15" futures = "0.3" ic-vetkd-utils = { git = "https://github.com/dfinity/ic", rev = "95231520" } lazy_static = "1.5.0" -pocket-ic = { git = "https://github.com/dfinity/ic", tag = "release-2026-03-02_11-09-base" } +pocket-ic = { git = "https://github.com/dfinity/ic", tag = "release-2026-04-16_04-20-base" } prost = "0.14.3" prost-build = "0.14.3" reqwest = "0.13.2" diff --git a/e2e-tests/src/bin/api.rs b/e2e-tests/src/bin/api.rs index a253035d2..157c71412 100644 --- a/e2e-tests/src/bin/api.rs +++ b/e2e-tests/src/bin/api.rs @@ -53,6 +53,23 @@ fn call_msg_reply() { msg_reply(vec![42]); } +/// Returns the caller info data bytes provided in the `sender_info`. +/// Returns empty bytes if no `sender_info` was provided. +#[unsafe(export_name = "canister_update call_msg_caller_info_data")] +fn call_msg_caller_info_data() { + msg_reply(msg_caller_info_data()); +} + +/// Returns the caller info signer as raw principal bytes. +/// Returns empty bytes if no `sender_info` was provided. +#[unsafe(export_name = "canister_update call_msg_caller_info_signer")] +fn call_msg_caller_info_signer() { + let signer_bytes = msg_caller_info_signer() + .map(|p| p.as_slice().to_vec()) + .unwrap_or_default(); + msg_reply(signer_bytes); +} + #[unsafe(export_name = "canister_update call_msg_reject")] fn call_msg_reject() { msg_reject("e2e test reject"); diff --git a/e2e-tests/tests/api.rs b/e2e-tests/tests/api.rs index f71c3e3d4..36fe7d922 100644 --- a/e2e-tests/tests/api.rs +++ b/e2e-tests/tests/api.rs @@ -1,6 +1,6 @@ use candid::Principal; use ic_cdk_management_canister::{CanisterSettings, EnvironmentVariable, UpdateSettingsArgs}; -use pocket_ic::ErrorCode; +use pocket_ic::{ErrorCode, common::rest::RawSenderInfo}; mod test_utilities; use test_utilities::{cargo_build_canister, pic_base, update}; @@ -8,8 +8,8 @@ use test_utilities::{cargo_build_canister, pic_base, update}; #[test] fn call_api() { let wasm = cargo_build_canister("api"); - // with_ii_subnet is required for testing the ic0.cost_sign_with_* API with pre-defined key name. - let pic = pic_base().with_ii_subnet().build(); + // with_test_threshold_keys_subnet is required for testing the ic0.cost_sign_with_* API with pre-defined key name. + let pic = pic_base().with_test_threshold_keys_subnet().build(); let canister_id = pic.create_canister(); pic.add_cycles(canister_id, 100_000_000_000_000); pic.install_canister(canister_id, wasm, vec![], None); @@ -25,6 +25,43 @@ fn call_api() { // Unlike the other entry points, `call_msg_dealine_caller` was implemented with the `#[update]` macro. // So we use the update method which assumes candid let _: () = update(&pic, canister_id, "call_msg_deadline_caller", ()).unwrap(); + let info_data = b"test_caller_info_data"; + let sender_info = RawSenderInfo { + info: info_data.to_vec(), + signer: canister_id.as_slice().to_vec(), + }; + // With sender_info: data should equal the info bytes that were sent. + let res = pic + .update_call_with_sender_info( + canister_id, + sender, + "call_msg_caller_info_data", + vec![], + sender_info.clone(), + ) + .unwrap(); + assert_eq!(res, info_data); + // Without sender_info: data should be empty. + let res = pic + .update_call(canister_id, sender, "call_msg_caller_info_data", vec![]) + .unwrap(); + assert!(res.is_empty()); + // With sender_info: signer should be the canister_id principal bytes. + let res = pic + .update_call_with_sender_info( + canister_id, + sender, + "call_msg_caller_info_signer", + vec![], + sender_info, + ) + .unwrap(); + assert_eq!(res, canister_id.as_slice()); + // Without sender_info: signer should be None, returning empty bytes. + let res = pic + .update_call(canister_id, sender, "call_msg_caller_info_signer", vec![]) + .unwrap(); + assert!(res.is_empty()); // `msg_reject_code` and `msg_reject_msg` can't be tested here. // They are invoked in the reply/reject callback of inter-canister calls. // So the `call.rs` test covers them. diff --git a/e2e-tests/tests/management_canister.rs b/e2e-tests/tests/management_canister.rs index 7e2e13f76..4e294a59f 100644 --- a/e2e-tests/tests/management_canister.rs +++ b/e2e-tests/tests/management_canister.rs @@ -4,8 +4,10 @@ use test_utilities::{cargo_build_canister, pic_base, update}; #[test] fn test_management_canister() { let wasm = cargo_build_canister("management_canister"); - // Setup pocket-ic with an II subnet which is required by the "provisional" test. - let pic = pic_base().with_ii_subnet().build(); + let pic = pic_base() + .with_ii_subnet() // II subnet is required by the "provisional" test. + .with_test_threshold_keys_subnet() // threshold keys subnet is required for testing ecdsa/schnorr signing APIs with pre-defined key name. + .build(); let canister_id = pic.create_canister(); let subnet_id = pic.get_subnet(canister_id).unwrap(); diff --git a/e2e-tests/tests/test_utilities.rs b/e2e-tests/tests/test_utilities.rs index 7d3f22639..4658dc714 100644 --- a/e2e-tests/tests/test_utilities.rs +++ b/e2e-tests/tests/test_utilities.rs @@ -114,7 +114,7 @@ fn check_pocket_ic_server() -> PathBuf { .iter() .find(|m| m.name.as_ref() == "ic-cdk-e2e-tests") .expect("ic-cdk-e2e-tests not found in Cargo.toml"); - let pocket_ic_tag = e2e_tests_package + let source_repr = &e2e_tests_package .dependencies .iter() .find(|d| d.name == "pocket-ic") @@ -122,22 +122,28 @@ fn check_pocket_ic_server() -> PathBuf { .source .as_ref() .expect("pocket-ic source not found in Cargo.toml") - .repr - .split_once("tag=") - .expect("`tag=` not found in pocket-ic source") - .1; + .repr; + // Source URL is e.g. `git+https://...?rev=#` or `git+https://...?tag=#`. + // Extract the value after `rev=` or `tag=`, stopping at `#` or `&`. + let pocket_ic_ref = if let Some((_, rest)) = source_repr.split_once("rev=") { + rest.split_once(['#', '&']).map_or(rest, |(v, _)| v) + } else if let Some((_, rest)) = source_repr.split_once("tag=") { + rest.split_once(['#', '&']).map_or(rest, |(v, _)| v) + } else { + panic!("neither `rev=` nor `tag=` found in pocket-ic source: {source_repr}") + }; let target_dir = metadata.target_directory; let artifact_dir = target_dir.join("e2e-tests-artifacts"); let tag_path = artifact_dir.join("pocket-ic-tag"); let server_binary_path = artifact_dir.join("pocket-ic"); - if let Ok(tag) = std::fs::read_to_string(&tag_path) - && tag == pocket_ic_tag + if let Ok(cached) = std::fs::read_to_string(&tag_path) + && cached == pocket_ic_ref && server_binary_path.exists() { return server_binary_path.into(); } panic!( - "pocket-ic server not found or tag mismatch, please run `scripts/download_pocket_ic_server.sh` in the project root" + "pocket-ic server not found or version mismatch, please run `scripts/download_pocket_ic_server.sh` in the project root" ); } diff --git a/ic-cdk-macros/Cargo.toml b/ic-cdk-macros/Cargo.toml index d1db27bdc..80ab86a99 100644 --- a/ic-cdk-macros/Cargo.toml +++ b/ic-cdk-macros/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ic-cdk-macros" -version = "0.20.0" # sync with ic-cdk +version = "0.20.1" # sync with ic-cdk authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ic-cdk-timers/src/global_timer.rs b/ic-cdk-timers/src/global_timer.rs index 143741950..0a5cf31f4 100644 --- a/ic-cdk-timers/src/global_timer.rs +++ b/ic-cdk-timers/src/global_timer.rs @@ -108,31 +108,28 @@ fn do_timer( interval, concurrent_calls, .. - } => { - if *concurrent_calls >= 5 { - ic0::debug_print( - format!( - "[ic-cdk-timers] canister_global_timer: \ - too many concurrent calls for single timer ({}), \ - rescheduling for next possible execution time", - concurrent_calls - ) - .as_bytes(), - ); - // Reschedule based on `now`, not `timer_scheduled_time`, - // intentionally skipping intermediate executions. - reschedule_timer(timers, task_id, now, *interval); - return true; // skip - } + } if *concurrent_calls >= 5 => { + ic0::debug_print( + format!( + "[ic-cdk-timers] canister_global_timer: \ + too many concurrent calls for single timer ({}), \ + rescheduling for next possible execution time", + concurrent_calls + ) + .as_bytes(), + ); + // Reschedule based on `now`, not `timer_scheduled_time`, + // intentionally skipping intermediate executions. + reschedule_timer(timers, task_id, now, *interval); + true // skip } // 3. Check serial timer is available Task::RepeatedSerialBusy { interval } => { reschedule_timer(timers, task_id, timer_scheduled_time, *interval); - return true; // skip + true // skip } - _ => (), + _ => false, // do not skip } - false // do not skip }); if skip { return ControlFlow::Continue(()); diff --git a/ic-cdk/CHANGELOG.md b/ic-cdk/CHANGELOG.md index c4fc5b0a4..f8dccd901 100644 --- a/ic-cdk/CHANGELOG.md +++ b/ic-cdk/CHANGELOG.md @@ -6,6 +6,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [unreleased] +## [0.20.1] - 2026-04-20 + +### Added + +- Added `msg_caller_info_data` and `msg_caller_info_signer` to `ic_cdk::api`, exposing the caller's identity attribute data and the signing canister ID respectively. + ## [0.20.0] - 2026-03-05 ### Changed diff --git a/ic-cdk/Cargo.toml b/ic-cdk/Cargo.toml index 284497ef9..2ab14577b 100644 --- a/ic-cdk/Cargo.toml +++ b/ic-cdk/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ic-cdk" -version = "0.20.0" # sync with ic-cdk-macros +version = "0.20.1" # sync with ic-cdk-macros authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ic-cdk/src/api.rs b/ic-cdk/src/api.rs index 567fd60a0..8ee108b5a 100644 --- a/ic-cdk/src/api.rs +++ b/ic-cdk/src/api.rs @@ -41,6 +41,54 @@ pub fn msg_caller() -> Principal { Principal::try_from(&buf).unwrap() } +/// Gets auxiliary data about the caller as provided by the canister with which the caller's identity is associated. +/// +/// This only returns non-empty data if the caller is a self-authenticating principal authenticated +/// by canister signatures (e.g. Internet Identity). Returns empty bytes when the caller is another canister. +/// +/// The data is guaranteed to be signed by the canister returned from [`msg_caller_info_signer`], +/// so the signer should be checked before trusting the payload. +/// +/// ```rust,no_run +/// use ic_cdk::api::{msg_caller_info_data, msg_caller_info_signer}; +/// +/// if msg_caller_info_signer().is_some() { +/// let data = msg_caller_info_data(); +/// // Decode per the signer's documented format (e.g. identity attributes). +/// } +/// ``` +pub fn msg_caller_info_data() -> Vec { + let len = ic0::msg_caller_info_data_size(); + let mut buf = vec![0u8; len]; + ic0::msg_caller_info_data_copy(&mut buf, 0); + buf +} + +/// Gets the canister ID of the canister that provided the caller's canister signature. +/// +/// Returns `None` if the caller is not a self-authenticating principal authenticated by canister +/// signatures (e.g. when the caller is another canister or no sender info was provided). +/// +/// ```rust,no_run +/// use ic_cdk::api::msg_caller_info_signer; +/// use candid::Principal; +/// +/// let trusted_issuer = Principal::from_text("rdmx6-jaaaa-aaaaa-aaadq-cai").unwrap(); +/// if msg_caller_info_signer() == Some(trusted_issuer) { +/// // Caller's identity was attested by the trusted issuer (e.g. Internet Identity). +/// } +/// ``` +pub fn msg_caller_info_signer() -> Option { + let len = ic0::msg_caller_info_signer_size(); + if len == 0 { + return None; + } + let mut buf = vec![0u8; len]; + ic0::msg_caller_info_signer_copy(&mut buf, 0); + // Trust that the system always returns a valid principal when non-empty. + Some(Principal::try_from(&buf).expect("msg_caller_info_signer must be a valid principal")) +} + /// Returns the reject code, if the current function is invoked as a reject callback. pub fn msg_reject_code() -> u32 { ic0::msg_reject_code() diff --git a/ic0/CHANGELOG.md b/ic0/CHANGELOG.md index 616acd9f5..9de489250 100644 --- a/ic0/CHANGELOG.md +++ b/ic0/CHANGELOG.md @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [unreleased] +## [1.1.0] - 2026-04-20 + +### Added + +- Added `msg_caller_info_data_size`, `msg_caller_info_data_copy`, `msg_caller_info_signer_size`, and `msg_caller_info_signer_copy` API bindings. + ## [1.0.1] - 2025-09-11 ### Added diff --git a/ic0/Cargo.toml b/ic0/Cargo.toml index 2f8072598..f3e0f91ea 100644 --- a/ic0/Cargo.toml +++ b/ic0/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ic0" -version = "1.0.1" +version = "1.1.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ic0/ic0.txt b/ic0/ic0.txt index 4b0629e02..bf3b3c729 100644 --- a/ic0/ic0.txt +++ b/ic0/ic0.txt @@ -2,6 +2,10 @@ ic0.msg_arg_data_copy : (dst : I, offset : I, size : I) -> (); // I U RQ NRQ CQ Ry CRy F ic0.msg_caller_size : () -> I; // * ic0.msg_caller_copy : (dst : I, offset : I, size : I) -> (); // * + ic0.msg_caller_info_data_size : () -> I; // U RQ NRQ CQ Ry Rt CRy CRt C CC F + ic0.msg_caller_info_data_copy : (dst : I, offset : I, size : I) -> (); // U RQ NRQ CQ Ry Rt CRy CRt C CC F + ic0.msg_caller_info_signer_size : () -> I; // U RQ NRQ CQ Ry Rt CRy CRt C CC F + ic0.msg_caller_info_signer_copy : (dst : I, offset : I, size : I) -> (); // U RQ NRQ CQ Ry Rt CRy CRt C CC F ic0.msg_reject_code : () -> i32; // Ry Rt CRy CRt ic0.msg_reject_msg_size : () -> I ; // Rt CRt ic0.msg_reject_msg_copy : (dst : I, offset : I, size : I) -> (); // Rt CRt diff --git a/ic0/manual_safety_comments.txt b/ic0/manual_safety_comments.txt index 50fc4d4da..f7de05714 100644 --- a/ic0/manual_safety_comments.txt +++ b/ic0/manual_safety_comments.txt @@ -9,6 +9,14 @@ ic0.msg_caller_size : () -> I; Always safe to call ic0.msg_caller_copy : (dst : I, offset : I, size : I) -> (); // * `dst` must be a pointer to a writable sequence of bytes with size `size`. The `offset` parameter does not affect safety. +ic0.msg_caller_info_data_size : () -> I; // U RQ NRQ CQ Ry Rt CRy CRt C CC F + Always safe to call +ic0.msg_caller_info_data_copy : (dst : I, offset : I, size : I) -> (); // U RQ NRQ CQ Ry Rt CRy CRt C CC F + `dst` must be a pointer to a writable sequence of bytes with size `size`. The `offset` parameter does not affect safety. +ic0.msg_caller_info_signer_size : () -> I; // U RQ NRQ CQ Ry Rt CRy CRt C CC F + Always safe to call +ic0.msg_caller_info_signer_copy : (dst : I, offset : I, size : I) -> (); // U RQ NRQ CQ Ry Rt CRy CRt C CC F + `dst` must be a pointer to a writable sequence of bytes with size `size`. The `offset` parameter does not affect safety. ic0.msg_reject_code : () -> i32; // Ry Rt CRy CRt Always safe to call ic0.msg_reject_msg_size : () -> I ; // Rt CRt diff --git a/ic0/src/lib.rs b/ic0/src/lib.rs index 5115e3a6d..a7e5f2e80 100644 --- a/ic0/src/lib.rs +++ b/ic0/src/lib.rs @@ -66,6 +66,52 @@ pub fn msg_caller_copy_uninit(dst: &mut [MaybeUninit], offset: usize) { unsafe { sys::msg_caller_copy(dst.as_mut_ptr() as usize, offset, dst.len()) } } +#[inline] +pub fn msg_caller_info_data_size() -> usize { + // SAFETY: ic0.msg_caller_info_data_size is always safe to call + unsafe { sys::msg_caller_info_data_size() } +} + +#[inline] +pub fn msg_caller_info_data_copy(dst: &mut [u8], offset: usize) { + // SAFETY: dst is a writable sequence of bytes and therefore safe to pass as ptr and len to ic0.msg_caller_info_data_copy + // The offset parameter does not affect safety + unsafe { sys::msg_caller_info_data_copy(dst.as_mut_ptr() as usize, offset, dst.len()) } +} + +/// # Safety +/// +/// This function will fully initialize `dst` (or trap if it cannot). +#[inline] +pub fn msg_caller_info_data_copy_uninit(dst: &mut [MaybeUninit], offset: usize) { + // SAFETY: dst is a writable sequence of bytes and therefore safe to pass as ptr and len to ic0.msg_caller_info_data_copy + // The offset parameter does not affect safety + unsafe { sys::msg_caller_info_data_copy(dst.as_mut_ptr() as usize, offset, dst.len()) } +} + +#[inline] +pub fn msg_caller_info_signer_size() -> usize { + // SAFETY: ic0.msg_caller_info_signer_size is always safe to call + unsafe { sys::msg_caller_info_signer_size() } +} + +#[inline] +pub fn msg_caller_info_signer_copy(dst: &mut [u8], offset: usize) { + // SAFETY: dst is a writable sequence of bytes and therefore safe to pass as ptr and len to ic0.msg_caller_info_signer_copy + // The offset parameter does not affect safety + unsafe { sys::msg_caller_info_signer_copy(dst.as_mut_ptr() as usize, offset, dst.len()) } +} + +/// # Safety +/// +/// This function will fully initialize `dst` (or trap if it cannot). +#[inline] +pub fn msg_caller_info_signer_copy_uninit(dst: &mut [MaybeUninit], offset: usize) { + // SAFETY: dst is a writable sequence of bytes and therefore safe to pass as ptr and len to ic0.msg_caller_info_signer_copy + // The offset parameter does not affect safety + unsafe { sys::msg_caller_info_signer_copy(dst.as_mut_ptr() as usize, offset, dst.len()) } +} + #[inline] pub fn msg_reject_code() -> u32 { // SAFETY: ic0.msg_reject_code is always safe to call diff --git a/ic0/src/sys.rs b/ic0/src/sys.rs index 4460cc12a..6be373523 100644 --- a/ic0/src/sys.rs +++ b/ic0/src/sys.rs @@ -12,6 +12,14 @@ unsafe extern "C" { #[doc = "# Safety\n\n`dst` must be a pointer to a writable sequence of bytes with size `size`. The `offset` parameter does not affect safety."] pub fn msg_caller_copy(dst: usize, offset: usize, size: usize); #[doc = "# Safety\n\nAlways safe to call"] + pub fn msg_caller_info_data_size() -> usize; + #[doc = "# Safety\n\n`dst` must be a pointer to a writable sequence of bytes with size `size`. The `offset` parameter does not affect safety."] + pub fn msg_caller_info_data_copy(dst: usize, offset: usize, size: usize); + #[doc = "# Safety\n\nAlways safe to call"] + pub fn msg_caller_info_signer_size() -> usize; + #[doc = "# Safety\n\n`dst` must be a pointer to a writable sequence of bytes with size `size`. The `offset` parameter does not affect safety."] + pub fn msg_caller_info_signer_copy(dst: usize, offset: usize, size: usize); + #[doc = "# Safety\n\nAlways safe to call"] pub fn msg_reject_code() -> u32; #[doc = "# Safety\n\nAlways safe to call"] pub fn msg_reject_msg_size() -> usize; @@ -164,6 +172,22 @@ mod non_wasm { panic!("msg_caller_copy should only be called inside canisters."); } #[doc = "# Safety\n\nAlways safe to call"] + pub unsafe fn msg_caller_info_data_size() -> usize { + panic!("msg_caller_info_data_size should only be called inside canisters."); + } + #[doc = "# Safety\n\n`dst` must be a pointer to a writable sequence of bytes with size `size`. The `offset` parameter does not affect safety."] + pub unsafe fn msg_caller_info_data_copy(dst: usize, offset: usize, size: usize) { + panic!("msg_caller_info_data_copy should only be called inside canisters."); + } + #[doc = "# Safety\n\nAlways safe to call"] + pub unsafe fn msg_caller_info_signer_size() -> usize { + panic!("msg_caller_info_signer_size should only be called inside canisters."); + } + #[doc = "# Safety\n\n`dst` must be a pointer to a writable sequence of bytes with size `size`. The `offset` parameter does not affect safety."] + pub unsafe fn msg_caller_info_signer_copy(dst: usize, offset: usize, size: usize) { + panic!("msg_caller_info_signer_copy should only be called inside canisters."); + } + #[doc = "# Safety\n\nAlways safe to call"] pub unsafe fn msg_reject_code() -> u32 { panic!("msg_reject_code should only be called inside canisters."); } diff --git a/scripts/download_pocket_ic_server.sh b/scripts/download_pocket_ic_server.sh index cb663d426..0ed781ec7 100755 --- a/scripts/download_pocket_ic_server.sh +++ b/scripts/download_pocket_ic_server.sh @@ -2,23 +2,54 @@ set -euo pipefail -uname_sys=$(uname -s | tr '[:upper:]' '[:lower:]') -echo "uname_sys: $uname_sys" +case $(uname -s) in + Linux*) os="linux";; + Darwin*) os="darwin";; + *) echo "Unsupported OS $(uname -s)"; exit 1;; +esac + +case $(uname -m) in + x86_64*) arch="x86_64";; + arm64*) arch="arm64";; + aarch64*) arch="arm64";; + *) echo "Unsupported architecture $(uname -m)"; exit 1;; +esac + +echo "os: $os, arch: $arch" SCRIPTS_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" cd "$SCRIPTS_DIR/.." -# extract the tag from the workspace Cargo.toml -tag=$(grep -E 'pocket-ic.*tag' Cargo.toml | sed -n "s/.*tag *= *\"\([^\"]*\)\".*/\1/p") + +# Extract the source URL of the pocket-ic dependency via cargo metadata. +# With --no-deps only workspace packages are emitted; .dependencies[] still +# carries the declared source (e.g. git+https://...?rev=#). +pocket_ic_source=$(cargo metadata --format-version=1 --no-deps \ + | jq -r '.packages[].dependencies[] | select(.name=="pocket-ic") | .source' \ + | head -1) +echo "pocket-ic source: $pocket_ic_source" ARTIFACTS_DIR="$SCRIPTS_DIR/../target/e2e-tests-artifacts" mkdir -p "$ARTIFACTS_DIR" cd "$ARTIFACTS_DIR" -echo -n "$tag" > pocket-ic-tag -curl -sL "https://github.com/dfinity/ic/releases/download/$tag/pocket-ic-x86_64-$uname_sys.gz" --output pocket-ic.gz + +if [[ "$pocket_ic_source" == *"?rev="* ]]; then + rev=$(printf '%s' "$pocket_ic_source" | sed 's/.*?rev=\([^#&]*\).*/\1/') + echo "Using git rev: $rev" + echo -n "$rev" > pocket-ic-tag + curl -sL "https://download.dfinity.systems/ic/$rev/binaries/${arch}-${os}/pocket-ic.gz" --output pocket-ic.gz +elif [[ "$pocket_ic_source" == *"?tag="* ]]; then + tag=$(printf '%s' "$pocket_ic_source" | sed 's/.*?tag=\([^#&]*\).*/\1/') + echo "Using git tag: $tag" + echo -n "$tag" > pocket-ic-tag + curl -sL "https://github.com/dfinity/ic/releases/download/$tag/pocket-ic-${arch}-${os}.gz" --output pocket-ic.gz +else + echo "Error: unexpected pocket-ic source: $pocket_ic_source" + exit 1 +fi gzip -df pocket-ic.gz chmod a+x pocket-ic ./pocket-ic --version -if [[ "$uname_sys" == "darwin" ]]; then +if [[ "$os" == "darwin" ]]; then xattr -dr com.apple.quarantine pocket-ic fi