refactor: add icp-events and report progress through it in four operations - #709
Draft
raymondk wants to merge 5 commits into
Draft
refactor: add icp-events and report progress through it in four operations#709raymondk wants to merge 5 commits into
raymondk wants to merge 5 commits into
Conversation
…tions Progress reporting is what welds `crates/icp-cli/src/operations/` to the binary: six of those files import `crate::progress` and a seventh drives `indicatif` directly. This inverts the dependency so the rest of the crate split can proceed. New crate `crates/icp-events` expresses progress and user-facing notices as data: `Event`, `Reporter`, `Task`, `EventSink`, `CancelToken`. It depends only on serde and futures — not on `icp`, not on an async runtime, and on nothing terminal-shaped. `TaskKind` carries the three shapes the CLI uses today (spinner, multi-step with streamed command output, byte position) and `Event::Notice` carries the user-facing `info!`/`warn!`/`error!` output, so converting those later needs no redesign. `crates/icp-cli/src/events.rs` adds `IndicatifSink`, the one place that knows about both events and `indicatif`. It takes its styles and tick interval from `crate::progress` so the two renderers cannot drift while both exist. `install.rs`, `settings.rs`, `binding_env_vars.rs` and `candid_compat.rs` now take a `&Reporter` instead of `debug: bool`. `progress.rs` stays for `build.rs`, `sync.rs` and `snapshot_transfer.rs`, which a separate change owns. The event model is deliberately not semver-stable: `publish = false`, `0.x`, all enums `#[non_exhaustive]`, `TaskKind` closed. Events do not drive `--json`.
raymondk
force-pushed
the
fm/icp-split-inc1-events
branch
from
August 13, 2026 18:54
f754450 to
f8bf02e
Compare
Contributor
There was a problem hiding this comment.
Pull request overview
Introduces an event-based progress abstraction to decouple CLI operations from terminal rendering.
Changes:
- Adds
icp-eventswith events, reporting, recording, and cancellation APIs. - Adds an
indicatifevent sink with rendering-equivalence tests. - Migrates four deploy operations to
Reporterwith event-sequence tests.
Reviewed changes
Copilot reviewed 21 out of 22 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
.claude/CLAUDE.md |
Documents the new crate. |
.claude/architecture.md |
Documents progress architecture. |
Cargo.toml |
Registers icp-events. |
Cargo.lock |
Locks the new crate. |
crates/icp-events/Cargo.toml |
Configures crate dependencies. |
crates/icp-events/src/cancel.rs |
Implements cancellation tokens. |
crates/icp-events/src/event.rs |
Defines the event model. |
crates/icp-events/src/lib.rs |
Exposes the public API. |
crates/icp-events/src/reporter.rs |
Implements reporters and tasks. |
crates/icp-events/src/sink.rs |
Implements event sinks. |
crates/icp-cli/Cargo.toml |
Adds the crate dependency. |
crates/icp-cli/src/commands/deploy.rs |
Supplies reporters to operations. |
crates/icp-cli/src/events.rs |
Renders events with indicatif. |
crates/icp-cli/src/main.rs |
Registers the events module. |
crates/icp-cli/src/operations/binding_env_vars.rs |
Migrates environment-variable progress. |
crates/icp-cli/src/operations/candid_compat.rs |
Migrates compatibility-check progress. |
crates/icp-cli/src/operations/install.rs |
Migrates installation progress. |
crates/icp-cli/src/operations/mod.rs |
Registers test support. |
crates/icp-cli/src/operations/settings.rs |
Migrates settings progress. |
crates/icp-cli/src/operations/snapshot_transfer.rs |
Reuses byte-bar styling. |
crates/icp-cli/src/operations/test_support.rs |
Adds shared operation fixtures. |
crates/icp-cli/src/progress.rs |
Exposes shared progress styles. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Progress reporting is what welds
crates/icp-cli/src/operations/to the binary: six of those files importcrate::progressand a seventh drivesindicatifdirectly. Nothing else in the crate split can start until that dependency is inverted, so this goes first and the API here has to carry the remaining conversions.What
New crate
crates/icp-events— progress and user-facing notices as data. Depends on serde and futures only; not onicp, not on an async runtime, and not on anything terminal-shaped (indicatif,dialoguer,clap,console).Event—TaskStarted/TaskMessage/TaskPosition/TaskFinished,StepStarted/StepOutput/StepFinished, andNotice { level, message }TaskKind— closed enum carrying the three shapes that exist today:Spinner,Steps { output_label }(theMultiStepProgressBarshape),Bytes { total }(whatsnapshot_transfer.rsuses)Reporter/Task—Task::run(fut, success, error)replacesProgressManager::execute_with_progress, the ~6-line idiom the four converted operations shared. A droppedTaskstill emitsTaskFinished { Neutral }, so an early return can't leave a sink holding a bar that spins forever.EventSink,DiscardSink,RecordingSink,CancelTokenNotice,StepsandBytesare deliberately unused by the four operations converted here. They exist because the follow-up work items need them:Noticefor the 29 user-facinginfo!/warn!/error!calls inoperations/(INFO-leveltracingis product output in this CLI —logging.rsinstalls aUserLayerthat printsLevel::INFOto stderr unprefixed), andSteps/Bytesforbuild.rs/sync.rs/snapshot_transfer.rs. Designing them now avoids a redesign later.crates/icp-cli/src/events.rs—IndicatifSink, the only place that knows about both events andindicatif. Styles,STEADY_TICKandbyte_styleare hoisted intoprogress.rsand shared by both renderers so they cannot drift while both exist.Four operations converted —
install.rs,settings.rs,binding_env_vars.rs,candid_compat.rsnow take a&Reporterinstead ofdebug: bool. Eachdeploy.rscall site builds its own reporter withevents::indicatif_reporter(ctx.debug), mirroring the previous one-ProgressManager-per-operation lifetime; a single sharedMultiProgressacross the whole deploy would have kept finished bars live across theinfo!lines printed between operations and changed the output.Terminal output is unchanged — how that was verified
events.rshas arendering_equivalencemodule. A recordingindicatif::TermLikecaptures what indicatif actually draws; each scenario is driven through both the oldProgressManagerpath and the newIndicatifSinkpath against the same recording terminal, and the frame sequences are compared:candid_compatskip (which usedfinish_with_message, leaving the running style — soOutcome::Neutralmust callfinish_with_messagetoo, notset_message+finish, or it inserts an extra redraw)snapshot_transfer::create_transfer_progress_barOnly two things are normalized away: the blank padding row indicatif writes to fill the terminal line (a function of the frame it follows), and the timer-driven spinner animation glyph (advances on wall-clock time, so it differs run to run). Prefix, final tick glyph, colour codes and message are compared byte for byte.
These tests were mutation-checked — reverting the neutral-finish call sequence, and changing the label prefix from
[name]toname, each fail them — and run repeatedly to confirm they aren't flaky. Per-operation tests additionally pin the exact message strings and the exact multi-step message framing.Testability
Making the operations unit-testable was a primary goal, not a side effect. Each of the four is now tested by running it for real against a
RecordingSinkand asserting on the resultingVec<Event>— exact event sequences for the artifact-miss and skip paths, per-canister task ordering, and empty-input silence. Shared fixtures are inoperations/test_support.rs. No test touches the network; the whole unit suite runs in well under a second.Deliberately not in this PR
Each is owned by a separate work item:
build.rs,sync.rsandsnapshot_transfer.rsstill useprogress.rs/indicatifdirectlyprogress.rsis not deleted — it still has users, incommands/as well asoperations/tracingcalls insideoperations/are stilltracingcallscrates/icpchangedStability
The event model is intentionally not semver-stable:
publish = false,0.xin lockstep withicp-cli, all enums#[non_exhaustive],TaskKindclosed rather than an open string. The event stream does not drive--json—--jsonkeeps meaning the command's final result, and no flag or feature is added that would change that.Checks
cargo build,cargo fmt --check,cargo clippy --workspace --all-targets, and theicp-events(34 + doctest),icp-cli(85) andicp(236) unit suites are clean on the rebased branch. Integration suites run in CI.🤖 Generated with Claude Code