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
1 change: 1 addition & 0 deletions .claude/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ cargo fmt && cargo clippy # Run after changes pass tests
- **`crates/icp-cli`**: Main CLI binary (`icp`) with command implementations
- **`crates/icp`**: Core library with project model, manifest loading, canister management, network configuration
- **`crates/icp-canister-interfaces`**: Canister interface definitions for ICP system canisters
- **`crates/icp-events`**: Progress and user-facing notices as data (`Event`, `Reporter`, `Task`, `EventSink`), so operations can report without depending on the terminal. serde + futures only
- **`crates/schema-gen`**: JSON schema generation for manifest validation

### Command Structure
Expand Down
42 changes: 42 additions & 0 deletions .claude/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,48 @@ These constants are defined in `crates/icp/src/prelude.rs` as `LOCAL` and `IC` a

Store management is in `crates/icp/src/store_id.rs`.

## Progress & User-Facing Output

Operations in `crates/icp-cli/src/operations/` report progress as data, not as terminal
calls — an inversion that is partway done, so `build.rs`, `sync.rs` and
`snapshot_transfer.rs` still render directly. `crates/icp-events` defines the vocabulary
(`Event`, `Reporter`, `Task`, `EventSink`, `CancelToken`) and depends only on serde and
futures — never on `icp`, an async runtime, or anything terminal-shaped.
`crates/icp-cli/src/events.rs` holds `IndicatifSink`, the only place that maps events onto
`indicatif` bars.

- New or converted operations take a `&Reporter`, never a `debug: bool` and never
`crate::progress` directly. Callers build one per operation with
`events::indicatif_reporter(ctx.debug)`.
- `crates/icp-cli/src/progress.rs` is the pre-inversion renderer. Do not add users. Whether
it is removable is a question for the compiler — delete it and run
`cargo check -p icp-cli --all-targets`; no grep is the gate. To survey the call sites,
search for the symbols, not the module path, because a nested `use crate::{ …,
progress::{…} }` never spells `crate::progress` (which is exactly how `commands/deploy.rs`
hides from that search):

```bash
grep -rlE 'ProgressManager|MultiStepProgressBar|RollingLines|_style\(|indicatif' crates/icp-cli/src
```

That covers commands as well as operations, and both kinds of user: those going through
`progress.rs` and those driving `indicatif` themselves. Styles shared by the two renderers
(spinner styles, `STEADY_TICK`, `byte_style`) live in `progress.rs` so they cannot drift
while both exist — `operations/snapshot_transfer.rs`, for one, takes `byte_style` from
there but still builds the bar with `indicatif` directly.
- The event model is deliberately not semver-stable: `publish = false`, `0.x`, all enums
`#[non_exhaustive]`, `TaskKind` closed.
- Events do not drive `--json`. `--json` means the command's final result; progress never
appears in it.
- `tracing` at INFO level is product output here, not logging — `logging.rs` installs a
`UserLayer` that prints `Level::INFO` to stderr unprefixed. `Event::Notice` is the event
model's equivalent; the `info!`/`warn!`/`error!` calls inside `operations/` have not been
converted yet.

Operations are unit-tested by running them against `RecordingSink` and asserting on the
resulting `Vec<Event>`; see `operations/test_support.rs`. `events.rs` additionally compares
`IndicatifSink`'s rendered frames against `ProgressManager`'s to catch output regressions.

## Telemetry

Anonymous usage telemetry implementation. User-facing documentation is in `docs/telemetry.md`.
Expand Down
10 changes: 10 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ ic-management-canister-types = { version = "0.8.0" }
ic-utils = { version = "0.49.1" }
icp = { path = "crates/icp" }
icp-canister-interfaces = { path = "crates/icp-canister-interfaces" }
icp-events = { path = "crates/icp-events" }
icp-sync-plugin = { path = "crates/icp-sync-plugin" }
ic-identity-hsm = "0.49.1"
icrc-ledger-types = "0.1.10"
Expand Down
1 change: 1 addition & 0 deletions crates/icp-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ ic-ledger-types.workspace = true
ic-management-canister-types.workspace = true
ic-utils.workspace = true
icp-canister-interfaces.workspace = true
icp-events.workspace = true
icp = { workspace = true, features = ["clap"] }
icrc-ledger-types.workspace = true
indicatif.workspace = true
Expand Down
9 changes: 5 additions & 4 deletions crates/icp-cli/src/commands/deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use tracing::info;
use crate::options::EnvironmentOpt;
use crate::{
commands::{args::ArgsOpt, canister::create},
events::indicatif_reporter,
operations::{
binding_env_vars::set_binding_env_vars_many,
build::build_many_with_progress_bar,
Expand Down Expand Up @@ -325,7 +326,7 @@ pub(crate) async fn exec(ctx: &Context, args: &DeployArgs) -> Result<(), anyhow:
&env.name,
target_canisters.clone(),
canister_list.clone(),
ctx.debug,
&indicatif_reporter(ctx.debug),
)
.await
.map_err(|e| anyhow!(e))?;
Expand All @@ -335,7 +336,7 @@ pub(crate) async fn exec(ctx: &Context, args: &DeployArgs) -> Result<(), anyhow:
args.proxy,
target_canisters,
canister_list,
ctx.debug,
&indicatif_reporter(ctx.debug),
)
.await
.map_err(|e| anyhow!(e))?;
Expand Down Expand Up @@ -385,7 +386,7 @@ pub(crate) async fn exec(ctx: &Context, args: &DeployArgs) -> Result<(), anyhow:
.iter()
.map(|(name, cid, mode, _, _)| (&**name, *cid, *mode)),
ctx.artifacts.clone(),
ctx.debug,
&indicatif_reporter(ctx.debug),
)
.await
.map_err(|e| anyhow!(e))?;
Expand All @@ -398,7 +399,7 @@ pub(crate) async fn exec(ctx: &Context, args: &DeployArgs) -> Result<(), anyhow:
args.proxy,
canisters,
ctx.artifacts.clone(),
ctx.debug,
&indicatif_reporter(ctx.debug),
)
.await?;

Expand Down
Loading
Loading