Skip to content
Merged
15 changes: 11 additions & 4 deletions .claude/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,18 @@ Two network types in `crates/icp/src/network/`:

The network launcher is automatically downloaded on first use. For development/debugging, you can override with `ICP_CLI_NETWORK_LAUNCHER_PATH`.

##### Network Overrides
##### Implicit Networks and Environments

- Users can override the "local" network definition in their `icp.yaml` to customize the local development environment
- The "mainnet" network is protected and cannot be overridden to prevent production deployment accidents
- If no "local" network is defined, a default managed network on `localhost:8000` is automatically added
The CLI provides two implicit networks and environments that are always available:

- **`local` network**: A default managed network on `localhost:8000`. Users can override this in their `icp.yaml` to customize the local development environment (e.g., different port or connecting to an existing network).
- **`ic` network**: The IC mainnet at `https://icp-api.io`. This network is **protected** and cannot be overridden to prevent accidental production deployment with incorrect settings.

Corresponding implicit environments are also provided:
- **`local` environment**: Uses the `local` network with all project canisters. This is the default environment when none is specified.
- **`ic` environment**: Uses the `ic` network with all project canisters.

These constants are defined in `crates/icp/src/prelude.rs` as `LOCAL` and `IC` and are used throughout the codebase.

#### Identity & Canister IDs

Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
* feat: Show `name` in `canister status` command
* feat: `icp canister metadata <canister> <metadata section>` now fetches metadata sections from specified canisters
* fix: Validate explicit canister paths and throw an error if `canister.yaml` is not found
* feat!: Rename the implicit "mainnet" network to "ic"
* The corresponding environment "ic" is defined implicitly which can be overwritten by user configuration
* The `--mainnet` and `--ic` flags are removed. Use `-n/--network ic`, `-e/--environment ic` instead.

# v0.1.0-beta.3

Expand Down
6 changes: 2 additions & 4 deletions crates/icp-cli/src/commands/network/args.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use clap::Args;
use icp::{context::NetworkOrEnvironmentSelection, project::DEFAULT_LOCAL_NETWORK_NAME};
use icp::{context::NetworkOrEnvironmentSelection, prelude::LOCAL};

#[derive(Args, Clone, Debug)]
pub(crate) struct NetworkOrEnvironmentArgs {
Expand Down Expand Up @@ -51,8 +51,6 @@ impl From<NetworkOrEnvironmentArgs> for Result<NetworkOrEnvironmentSelection, an
}

// Precedence 4: Default to "local" network (lowest)
Ok(NetworkOrEnvironmentSelection::Network(
DEFAULT_LOCAL_NETWORK_NAME.to_string(),
))
Ok(NetworkOrEnvironmentSelection::Network(LOCAL.to_string()))
}
}
36 changes: 3 additions & 33 deletions crates/icp-cli/src/options.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
use clap::{ArgGroup, Args};
use icp::context::{EnvironmentSelection, NetworkSelection};
use icp::identity::IdentitySelection;
use icp::project::{
DEFAULT_LOCAL_ENVIRONMENT_NAME, DEFAULT_MAINNET_ENVIRONMENT_NAME, DEFAULT_MAINNET_NETWORK_NAME,
};
use icp::prelude::LOCAL;
use url::Url;

mod heading {
Expand Down Expand Up @@ -47,38 +45,17 @@ pub(crate) struct EnvironmentOpt {
help_heading = heading::NETWORK_PARAMETERS,
)]
environment: Option<String>,

/// Shorthand for --environment=ic.
#[arg(
long,
global(true),
group = "environment-select",
group = "network-select",
help_heading = heading::NETWORK_PARAMETERS,
)]
ic: bool,
}

impl EnvironmentOpt {
#[allow(dead_code)]
pub(crate) fn name(&self) -> &str {
// Support --ic
if self.ic {
return DEFAULT_MAINNET_ENVIRONMENT_NAME;
}

// Otherwise, default to `local`
self.environment
.as_deref()
.unwrap_or(DEFAULT_LOCAL_ENVIRONMENT_NAME)
self.environment.as_deref().unwrap_or(LOCAL)
}
}

impl From<EnvironmentOpt> for EnvironmentSelection {
fn from(v: EnvironmentOpt) -> Self {
if v.ic {
return EnvironmentSelection::Named(DEFAULT_MAINNET_ENVIRONMENT_NAME.to_string());
}
match v.environment {
Some(name) => EnvironmentSelection::Named(name),
None => EnvironmentSelection::Default,
Expand All @@ -90,19 +67,12 @@ impl From<EnvironmentOpt> for EnvironmentSelection {
#[clap(group(ArgGroup::new("network-select").multiple(false)))]
pub(crate) struct NetworkOpt {
/// Name of the network to target, conflicts with environment argument
#[arg(long, env = "ICP_NETWORK", group = "network-select", help_heading = heading::NETWORK_PARAMETERS)]
#[arg(long, short = 'n', env = "ICP_NETWORK", group = "network-select", help_heading = heading::NETWORK_PARAMETERS)]
network: Option<String>,

/// Shorthand for --network=mainnet
#[arg(long, group = "network-select", help_heading = heading::NETWORK_PARAMETERS)]
mainnet: bool,
}

impl From<NetworkOpt> for NetworkSelection {
fn from(v: NetworkOpt) -> Self {
if v.mainnet {
return NetworkSelection::Named(DEFAULT_MAINNET_NETWORK_NAME.to_string());
}
match v.network {
Some(network) => match Url::parse(&network) {
Ok(url) => NetworkSelection::Url(url),
Expand Down
4 changes: 2 additions & 2 deletions crates/icp-cli/tests/common/clients/icp_cli.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use candid::Principal;
use icp::{prelude::*, project::DEFAULT_LOCAL_ENVIRONMENT_NAME};
use icp::prelude::*;

use crate::common::TestContext;

Expand All @@ -18,7 +18,7 @@ impl<'a> Client<'a> {
Self {
ctx,
current_dir,
environment: environment.unwrap_or(DEFAULT_LOCAL_ENVIRONMENT_NAME.to_string()),
environment: environment.unwrap_or(LOCAL.to_string()),
}
}

Expand Down
6 changes: 3 additions & 3 deletions crates/icp-cli/tests/cycles_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ async fn cycles_mint_with_explicit_network() {
}

#[tokio::test]
async fn cycles_mint_with_mainnet() {
async fn cycles_mint_on_ic() {
let ctx = TestContext::new();

// Setup project
Expand All @@ -156,10 +156,10 @@ async fn cycles_mint_with_mainnet() {
// Create identity
clients::icp(&ctx, &project_dir, None).use_new_random_identity();

// Run mint command with explicit --mainnet flag
// Run mint command with --network ic
ctx.icp()
.current_dir(&project_dir)
.args(["cycles", "mint", "--icp", "1", "--mainnet"])
.args(["cycles", "mint", "--icp", "1", "--network", "ic"])
.assert()
.stderr(contains(
"Error: Insufficient funds: 1.00010000 ICP required, 0 ICP available.",
Expand Down
12 changes: 6 additions & 6 deletions crates/icp-cli/tests/network_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -515,18 +515,18 @@ async fn override_local_network_with_custom_port() {
}

#[tokio::test]
async fn cannot_override_mainnet() {
async fn cannot_override_ic() {
let ctx = TestContext::new();
let project_dir = ctx.create_project_dir("override-mainnet");
let project_dir = ctx.create_project_dir("override-ic");

// Attempt to override mainnet
// Attempt to override ic
write_string(
&project_dir.join("icp.yaml"),
indoc! {r#"
networks:
- name: mainnet
- name: ic
mode: connected
url: http://fake-mainnet.local
url: http://fake-ic.local
"#},
)
.expect("failed to write project manifest");
Expand All @@ -537,7 +537,7 @@ async fn cannot_override_mainnet() {
.args(["build"])
.assert()
.failure()
.stderr(contains("`mainnet` is a reserved network name"));
.stderr(contains("`ic` is a reserved network name"));
}

#[tokio::test]
Expand Down
8 changes: 4 additions & 4 deletions crates/icp-cli/tests/project_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ fn explicit_path_with_subdirectory() {
}

#[test]
fn redefine_mainnet_network_disallowed() {
fn redefine_ic_network_disallowed() {
let ctx = TestContext::new();

// Setup project
Expand All @@ -277,9 +277,9 @@ fn redefine_mainnet_network_disallowed() {
&project_dir.join("icp.yaml"),
r#"
networks:
- name: mainnet
- name: ic
mode: connected
url: https://fake-mainnet.io
url: https://fake-ic.io
"#,
)
.expect("failed to write project manifest");
Expand All @@ -290,5 +290,5 @@ fn redefine_mainnet_network_disallowed() {
.args(["project", "show"])
.assert()
.failure()
.stderr(contains("`mainnet` is a reserved network name"));
.stderr(contains("`ic` is a reserved network name"));
}
11 changes: 4 additions & 7 deletions crates/icp/src/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@ use crate::{
identity::IdentitySelection,
network::{Configuration as NetworkConfiguration, access::NetworkAccess},
prelude::*,
project::{
DEFAULT_LOCAL_ENVIRONMENT_NAME, DEFAULT_MAINNET_NETWORK_NAME, DEFAULT_MAINNET_NETWORK_URL,
},
store_id::{IdMapping, LookupIdError},
};
use candid::Principal;
Expand Down Expand Up @@ -46,7 +43,7 @@ pub enum EnvironmentSelection {
impl EnvironmentSelection {
pub fn name(&self) -> &str {
match self {
EnvironmentSelection::Default => DEFAULT_LOCAL_ENVIRONMENT_NAME,
EnvironmentSelection::Default => LOCAL,
EnvironmentSelection::Named(name) => name,
}
}
Expand Down Expand Up @@ -160,12 +157,12 @@ impl Context {
name: network_name.to_owned(),
})?;
Ok(net.clone())
} else if network_name == DEFAULT_MAINNET_NETWORK_NAME {
} else if network_name == IC {
Ok(crate::Network {
name: DEFAULT_MAINNET_NETWORK_NAME.to_string(),
name: IC.to_string(),
configuration: crate::network::Configuration::Connected {
connected: crate::network::Connected {
url: DEFAULT_MAINNET_NETWORK_URL.to_string(),
url: IC_MAINNET_NETWORK_URL.to_string(),
root_key: None,
},
},
Expand Down
Loading