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
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ bump. Currently experimental: project bundling

# Unreleased

* feat: Projects can now depend on other `icp` projects vendored into them (e.g. as git submodules) via a top-level `dependencies:` block in `icp.yaml`.
* `icp deploy` deploys the dependency alongside your project and injects its canister IDs.
* Running `icp` from inside a vendored sub-project resolves up to the workspace root, so the whole workspace shares one network and one set of canister IDs.
* `:` is now reserved in canister names as the dependency namespace separator.
* See the [Project Dependencies](docs/concepts/project-dependencies.md) concept guide for details.
* feat: `icp canister delete` will now send the canister's remaining cycles to the caller

# v1.0.2
Expand Down
4 changes: 4 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

31 changes: 24 additions & 7 deletions crates/icp-cli/src/commands/deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use icp_canister_interfaces::candid_ui::MAINNET_CANDID_UI_CID;
use serde::Serialize;
use std::collections::BTreeMap;
use std::time::Duration;
use tracing::info;
use tracing::{info, warn};

use crate::{
commands::{args::ArgsOpt, canister::create},
Expand Down Expand Up @@ -98,12 +98,29 @@ pub(crate) async fn exec(ctx: &Context, args: &DeployArgs) -> Result<(), anyhow:

let env = ctx.get_environment(&environment_selection).await?;

let cnames = match args.names.is_empty() {
// No canisters specified
true => env.canisters.keys().cloned().collect(),

// Individual canisters specified
false => args.names.clone(),
let cnames: Vec<String> = if args.names.is_empty() {
// No canisters specified: default to the whole environment, unless the
// command is run inside a vendored member — then scope to that member's
// canisters and announce the resolved workspace root.
let project = ctx.project.load().await?;
let member_dir = ctx.project.member_dir();
match icp::project::member_scoped_canisters(&project.dir, member_dir.as_deref(), &env) {
Some(scoped) => {
if let Some(member) = &member_dir {
warn!(
"Running inside sub-project '{member}'; resolved workspace root '{}'. \
Deploying only this member's canisters into environment '{}'.",
project.dir,
environment_selection.name(),
);
}
scoped
}
None => env.canisters.keys().cloned().collect(),
}
} else {
// Individual canisters specified.
args.names.clone()
};

// Skip doing any work if no canisters are targeted
Expand Down
4 changes: 3 additions & 1 deletion crates/icp-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,11 @@ mod heading {
)]
struct Cli {
/// Directory to use as your project root directory.
/// If not specified the directory structure is traversed up until an icp.yaml file is found
/// If not specified the directory structure is traversed up to the workspace
/// root (the top-most project that declares the one you are in as a dependency).
#[arg(
long,
env = "ICP_PROJECT_ROOT",
global = true,
help_heading = heading::GLOBAL_PARAMETERS
)]
Expand Down
24 changes: 18 additions & 6 deletions crates/icp-cli/src/operations/binding_env_vars.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,22 +116,34 @@ pub(crate) async fn set_binding_env_vars_many(
.fail();
}

let binding_vars = canister_list
.iter()
.map(|(n, p)| (format!("PUBLIC_CANISTER_ID:{n}"), p.to_text()))
.collect::<Vec<(_, _)>>();

let mut futs = FuturesOrdered::new();
let progress_manager = ProgressManager::new(ProgressManagerSettings { hidden: debug });

for (cid, info) in target_canisters {
let pb = progress_manager.create_progress_bar(&info.name);
let canister_name = info.name.clone();

// Each canister receives only the ids it is wired to (its own project's
// canisters by their local names, plus any declared dependencies under
// their aliases), resolved to the ids that exist in this environment.
// A project without dependencies wires every canister to every sibling,
// reproducing the previous flat behavior.
let binding_vars: Vec<(String, String)> = info
.bindings
.iter()
.filter_map(|(env_name, referenced_key)| {
canister_list.get(referenced_key).map(|principal| {
(
format!("PUBLIC_CANISTER_ID:{env_name}"),
principal.to_text(),
)
})
})
.collect();

let settings_fn = {
let agent = agent.clone();
let pb = pb.clone();
let binding_vars = binding_vars.clone();

async move {
pb.set_message("Updating environment variables...");
Expand Down
4 changes: 4 additions & 0 deletions crates/icp-cli/src/operations/bundle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,10 @@ pub(crate) async fn create_bundle(

let bundle_manifest = ProjectManifest {
canisters: canister_items,
// A bundle flattens every consolidated canister (including any pulled in
// from dependencies) into `canisters` as pre-built entries, so no external
// dependency references remain.
dependencies: vec![],
networks,
environments,
};
Expand Down
Loading
Loading