Skip to content

Commit fd61d96

Browse files
chore: Replace assert_env_contains_canister with a getter (#204)
1 parent 95e2788 commit fd61d96

File tree

6 files changed

+54
-61
lines changed

6 files changed

+54
-61
lines changed

crates/icp-cli/src/commands/build.rs

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
use anyhow::anyhow;
21
use clap::Args;
2+
use futures::future::try_join_all;
33
use icp::context::{Context, EnvironmentSelection, GetEnvironmentError};
44

55
use crate::{
@@ -24,6 +24,9 @@ pub(crate) enum CommandError {
2424
#[error(transparent)]
2525
GetEnvironment(#[from] GetEnvironmentError),
2626

27+
#[error(transparent)]
28+
GetEnvCanister(#[from] icp::context::GetEnvCanisterError),
29+
2730
#[error(transparent)]
2831
Project(#[from] icp::LoadError),
2932

@@ -35,9 +38,6 @@ pub(crate) async fn exec(ctx: &Context, args: &BuildArgs) -> Result<(), CommandE
3538
// Get environment selection
3639
let environment_selection: EnvironmentSelection = args.environment.clone().into();
3740

38-
// Load the project manifest
39-
let p = ctx.project.load().await?;
40-
4141
// Load target environment
4242
let env = ctx.get_environment(&environment_selection).await?;
4343

@@ -50,26 +50,19 @@ pub(crate) async fn exec(ctx: &Context, args: &BuildArgs) -> Result<(), CommandE
5050
false => args.canisters.clone(),
5151
};
5252

53-
// Validate all specified canisters exist in project and environment
54-
for name in &cnames {
55-
ctx.assert_env_contains_canister(name, &environment_selection)
56-
.await
57-
.map_err(|e| anyhow!(e))?;
58-
}
59-
6053
// Skip doing any work if no canisters are targeted
6154
if cnames.is_empty() {
6255
return Ok(());
6356
}
6457

58+
let canisters_to_build = try_join_all(
59+
cnames
60+
.iter()
61+
.map(|name| ctx.get_canister_and_path_for_env(name, &environment_selection)),
62+
)
63+
.await?;
6564
// Build the selected canisters
6665
let _ = ctx.term.write_line("Building canisters:");
67-
let canisters_to_build = p
68-
.canisters
69-
.iter()
70-
.filter(|(k, _)| cnames.contains(k))
71-
.map(|(_, (path, canister))| (path.clone(), canister.clone()))
72-
.collect::<Vec<_>>();
7366

7467
build_many_with_progress_bar(
7568
canisters_to_build,

crates/icp-cli/src/commands/deploy/mod.rs

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use clap::Args;
33
use futures::{StreamExt, future::try_join_all, stream::FuturesOrdered};
44
use ic_agent::export::Principal;
55
use icp::{
6-
context::{Context, EnvironmentSelection},
6+
context::{Context, EnvironmentSelection, GetEnvCanisterError},
77
identity::IdentitySelection,
88
};
99
use std::sync::Arc;
@@ -57,6 +57,9 @@ pub(crate) enum CommandError {
5757
#[error("project does not contain an environment named '{name}'")]
5858
EnvironmentNotFound { name: String },
5959

60+
#[error(transparent)]
61+
GetEnvCanister(#[from] GetEnvCanisterError),
62+
6063
#[error(transparent)]
6164
Create(#[from] create::CommandError),
6265

@@ -93,25 +96,20 @@ pub(crate) async fn exec(ctx: &Context, args: &DeployArgs) -> Result<(), Command
9396
false => args.names.clone(),
9497
};
9598

96-
for name in &cnames {
97-
ctx.assert_env_contains_canister(name, &environment_selection)
98-
.await
99-
.map_err(|e| anyhow!(e))?;
100-
}
101-
10299
// Skip doing any work if no canisters are targeted
103100
if cnames.is_empty() {
104101
return Ok(());
105102
}
106103

104+
let canisters_to_build = try_join_all(
105+
cnames
106+
.iter()
107+
.map(|name| ctx.get_canister_and_path_for_env(name, &environment_selection)),
108+
)
109+
.await?;
110+
107111
// Build the selected canisters
108112
let _ = ctx.term.write_line("Building canisters:");
109-
let canisters_to_build = p
110-
.canisters
111-
.iter()
112-
.filter(|(k, _)| cnames.contains(k))
113-
.map(|(_, (path, canister))| (path.clone(), canister.clone()))
114-
.collect::<Vec<_>>();
115113

116114
build_many_with_progress_bar(
117115
canisters_to_build,

crates/icp-cli/src/commands/sync.rs

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@ pub(crate) enum CommandError {
3030
#[error(transparent)]
3131
Project(#[from] icp::LoadError),
3232

33+
#[error(transparent)]
34+
GetEnvCanister(#[from] icp::context::GetEnvCanisterError),
35+
36+
#[error(transparent)]
37+
GetEnvCanisterId(#[from] icp::context::GetCanisterIdForEnvError),
38+
3339
#[error(transparent)]
3440
Unexpected(#[from] anyhow::Error),
3541
}
@@ -54,13 +60,6 @@ pub(crate) async fn exec(ctx: &Context, args: &SyncArgs) -> Result<(), CommandEr
5460
false => args.canisters.clone(),
5561
};
5662

57-
// Validate all specified canisters exist in project and environment
58-
for name in &cnames {
59-
ctx.assert_env_contains_canister(name, &environment_selection)
60-
.await
61-
.map_err(|e| anyhow!(e))?;
62-
}
63-
6463
// Skip doing any work if no canisters are targeted
6564
if cnames.is_empty() {
6665
return Ok(());
@@ -73,19 +72,14 @@ pub(crate) async fn exec(ctx: &Context, args: &SyncArgs) -> Result<(), CommandEr
7372
.map_err(|e| anyhow!(e))?;
7473

7574
// Prepare list of canisters with their info for syncing
76-
let env_canisters = &env.canisters;
77-
let sync_canisters = try_join_all(cnames.iter().map(|name| {
78-
let environment_selection = environment_selection.clone();
79-
async move {
80-
let cid = ctx
81-
.get_canister_id_for_env(name, &environment_selection)
82-
.await
83-
.map_err(|e| anyhow!(e))?;
84-
let (canister_path, info) = env_canisters
85-
.get(name)
86-
.ok_or_else(|| anyhow!("Canister id exists but no canister info"))?;
87-
Ok::<_, anyhow::Error>((cid, canister_path.clone(), info.clone()))
88-
}
75+
let sync_canisters = try_join_all(cnames.iter().map(|name| async {
76+
let (canister_path, info) = ctx
77+
.get_canister_and_path_for_env(name, &environment_selection)
78+
.await?;
79+
let cid = ctx
80+
.get_canister_id_for_env(name, &environment_selection)
81+
.await?;
82+
Ok::<_, anyhow::Error>((cid, canister_path.clone(), info.clone()))
8983
}))
9084
.await?;
9185

crates/icp-cli/tests/sync_tests.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,17 +261,23 @@ fn sync_with_valid_principal() {
261261
steps:
262262
- type: script
263263
command: echo syncing
264+
{NETWORK_RANDOM_PORT}
265+
{ENVIRONMENT_RANDOM_PORT}
264266
"#};
265267

266268
write_string(&project_dir.join("icp.yaml"), &pm).expect("failed to write project manifest");
267269

270+
// Start network
271+
let _g = ctx.start_network_in(&project_dir, "my-network");
272+
ctx.ping_until_healthy(&project_dir, "my-network");
273+
268274
// Valid principal
269275
let principal = "aaaaa-aa";
270276

271277
// Try to sync with principal (should fail)
272278
ctx.icp()
273279
.current_dir(&project_dir)
274-
.args(["sync", principal])
280+
.args(["sync", principal, "--environment", "my-environment"])
275281
.assert()
276282
.failure()
277283
.stderr(contains("project does not contain a canister named"));

crates/icp/src/context/mod.rs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@ use std::sync::Arc;
33
use url::Url;
44

55
use crate::{
6+
Canister,
67
agent::CreateAgentError,
78
canister::{build::Build, sync::Synchronize},
89
directories,
910
identity::IdentitySelection,
1011
network::{Configuration as NetworkConfiguration, access::NetworkAccess},
12+
prelude::*,
1113
project::DEFAULT_LOCAL_ENVIRONMENT_NAME,
1214
store_id::IdMapping,
1315
};
@@ -152,26 +154,26 @@ impl Context {
152154
}
153155
}
154156

155-
pub async fn assert_env_contains_canister(
157+
pub async fn get_canister_and_path_for_env(
156158
&self,
157159
canister_name: &str,
158160
environment: &EnvironmentSelection,
159-
) -> Result<(), AssertEnvContainsCanisterError> {
161+
) -> Result<(PathBuf, Canister), GetEnvCanisterError> {
160162
let p = self.project.load().await?;
161-
if !p.contains_canister(canister_name) {
162-
return Err(AssertEnvContainsCanisterError::CanisterNotFoundInProject {
163+
let Some((path, canister)) = p.get_canister(canister_name) else {
164+
return Err(GetEnvCanisterError::CanisterNotFoundInProject {
163165
canister_name: canister_name.to_owned(),
164166
});
165-
}
167+
};
166168

167169
let env = self.get_environment(environment).await?;
168170
if !env.contains_canister(canister_name) {
169-
return Err(AssertEnvContainsCanisterError::CanisterNotInEnv {
171+
return Err(GetEnvCanisterError::CanisterNotInEnv {
170172
canister_name: canister_name.to_owned(),
171173
environment_name: environment.name().to_owned(),
172174
});
173175
}
174-
Ok(())
176+
Ok((path.clone(), canister.clone()))
175177
}
176178

177179
/// Gets the canister ID for a given canister name in a specified environment.
@@ -599,7 +601,7 @@ pub enum GetIdsByEnvironmentError {
599601
}
600602

601603
#[derive(Debug, Snafu)]
602-
pub enum AssertEnvContainsCanisterError {
604+
pub enum GetEnvCanisterError {
603605
#[snafu(transparent)]
604606
ProjectLoad { source: crate::LoadError },
605607

crates/icp/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,8 @@ pub struct Project {
9393
}
9494

9595
impl Project {
96-
pub fn contains_canister(&self, canister_name: &str) -> bool {
97-
self.canisters.contains_key(canister_name)
96+
pub fn get_canister(&self, canister_name: &str) -> Option<&(PathBuf, Canister)> {
97+
self.canisters.get(canister_name)
9898
}
9999
}
100100

0 commit comments

Comments
 (0)