Skip to content

Commit ce1b0e0

Browse files
Merge branch 'main' into spofford/windows
2 parents e1f90ff + f53cfcd commit ce1b0e0

31 files changed

+4076
-1091
lines changed

.claude/CLAUDE.md

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,9 @@ These constants are defined in `crates/icp/src/prelude.rs` as `LOCAL` and `IC` a
125125
#### Identity & Canister IDs
126126

127127
- **Identities**: Stored in `~/.config/icp/identity/` as PEM files (Secp256k1 or Ed25519)
128-
- **Canister IDs**: Persisted in `.icp/data/<network-name>/canister_ids.json` within project directories
128+
- **Canister IDs**: Persisted in `.icp/{cache,data}/mappings/<environment>.ids.json` within project directories
129+
- Managed networks (local) use `.icp/cache/mappings/`
130+
- Connected networks (mainnet) use `.icp/data/mappings/`
129131

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

@@ -193,8 +195,18 @@ The project includes JSON schemas for manifest validation:
193195

194196
### Docs generation
195197

196-
- The cli reference is generated in `docs/cli-reference.md`.
197-
- Regenerate the cli reference when commands changes by running: `./scripts/generate-cli-docs.sh`
198+
- The CLI reference is generated in `docs/reference/cli.md`.
199+
- Regenerate the CLI reference when commands change by running: `./scripts/generate-cli-docs.sh`
200+
201+
### Documentation Structure
202+
203+
Documentation follows the Diátaxis framework:
204+
205+
- `docs/tutorial.md` — Learning-oriented first deployment guide
206+
- `docs/guides/` — Task-oriented how-to guides
207+
- `docs/concepts/` — Understanding-oriented explanations
208+
- `docs/reference/` — Information-oriented technical specifications
209+
- `docs/migration/` — Migration guides (e.g., from dfx)
198210

199211
### Paths
200212

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44
* feat: `icp canister metadata <canister> <metadata section>` now fetches metadata sections from specified canisters
55
* fix: Validate explicit canister paths and throw an error if `canister.yaml` is not found
66
* feat!: Rename the implicit "mainnet" network to "ic"
7-
* The corresponding environment "ic" is defined implicitly which can be overwritten by user configuration
7+
* The corresponding environment "ic" is defined implicitly which can be overwritten by user configuration.
88
* The `--mainnet` and `--ic` flags are removed. Use `-n/--network ic`, `-e/--environment ic` instead.
9+
* feat: Allow overriding the implicit `local` network and environment.
910

1011
# v0.1.0-beta.3
1112

README.md

Lines changed: 48 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,112 +1,83 @@
11
# icp-cli
22

3-
A command-line interface for developing and deploying applications on the Internet Computer Protocol (ICP).
3+
A command-line tool for building and deploying applications on the Internet Computer.
44

5-
## Usage
5+
## Quick Start
66

7-
See the [command line reference](docs/cli-reference.md).
7+
```bash
8+
# Install via Homebrew (macOS)
9+
brew install dfinity/tap/icp-cli
810

9-
## Installing
11+
# Create and deploy a project
12+
icp new my-project && cd my-project
13+
icp network start -d
14+
icp deploy
1015

11-
For now, you have to build icp-cli locally in order to use it.
16+
# Show the status of your canisters
17+
icp canister status
1218

13-
### Prerequisites
19+
# Call a function on your canister
20+
# icp canister call <canister-name> greet '("World")'
21+
# The ones generated from the templates are typically called `backend`
1422

15-
- **Rust**: Install Rust using [rustup](https://rustup.rs/). The project uses Rust 2024 edition.
16-
- **mops**: Required if you want to build Motoko canisters. See [mops.one](https://cli.mops.one/).
23+
icp canister call backend greet '("World")'
24+
```
1725

18-
### Building
26+
See the [Installation Guide](docs/guides/installation.md) for all installation methods including building from source.
1927

20-
```bash
21-
# Build all crates in the workspace
22-
cargo build
28+
## For dfx Users
2329

24-
# Add target directory to your path
25-
export PATH=$(pwd)/target/debug:$PATH
30+
If you're coming from dfx (the previous Internet Computer SDK), see the **[Migration Guide](docs/migration/from-dfx.md)** for command mappings, workflow differences, and how to migrate existing projects.
2631

27-
# Check that you can run
28-
icp help
29-
```
30-
31-
### [Optional] Add motoko tools to the path
32+
## Documentation
3233

33-
You might also need the Motoko compiler if you plan on building canisters with Motoko. The best way
34-
is to install mops, the motoko package manager, see: https://cli.mops.one/
34+
- **[Tutorial](docs/tutorial.md)** — Deploy your first canister
35+
- **[Guides](docs/guides/index.md)** — How to accomplish common tasks
36+
- **[Concepts](docs/concepts/index.md)** — Understand how icp-cli works
37+
- **[Reference](docs/reference/index.md)** — Complete CLI and configuration reference
3538

36-
Reminder, when mops is installed the first time, you must initialize the toolchain with:
39+
## Examples
3740

38-
```bash
39-
mops toolchain init
40-
```
41+
The [`examples/`](examples/) directory contains example projects to help you get started:
4142

42-
### Examples
43+
- `icp-motoko/` — Motoko canister
44+
- `icp-rust/` — Rust canister
45+
- `icp-static-assets/` — Static website
46+
- `icp-environments/` — Multi-environment setup
4347

44-
The `examples/` directory contains various project templates and configurations that demonstrate how to use the CLI with different project types:
48+
[View all examples](examples/)
4549

46-
- `icp-motoko/` - Motoko canister example
47-
- `icp-rust/` - Rust canister example
48-
- `icp-static-assets/` - Static website deployment
49-
- `icp-multi-canister/` - Multi-canister project setup
50-
- And many more...
50+
## Prerequisites
5151

52-
## Development
52+
**Language-specific toolchains** (install for the languages you'll use):
53+
- **Rust canisters**[Rust](https://rustup.rs/) and `rustup target add wasm32-unknown-unknown`
54+
- **Motoko canisters**[mops](https://cli.mops.one/) and `mops toolchain init`
5355

54-
### Prerequisites
56+
## Getting Help
5557

56-
- **Rust**: Install Rust using [rustup](https://rustup.rs/). The project uses Rust 2024 edition.
58+
- **[Documentation](docs/index.md)** — Guides, concepts, and reference
59+
- **[GitHub Issues](https://github.com/dfinity/icp-cli/issues)** — Bug reports and feature requests
60+
- **[Developer Forum](https://forum.dfinity.org/)** — Questions and discussions
61+
- **[Discord](https://discord.internetcomputer.org)** — Real-time community chat in #dx-feedback
5762

58-
### Building
63+
## Contributing
5964

60-
This is a Rust workspace with multiple crates. To build the project:
65+
Contributions are welcome! See [CONTRIBUTING.md](.github/CONTRIBUTING.md) for guidelines.
6166

6267
```bash
63-
# Build all crates in the workspace
68+
# Build
6469
cargo build
6570

66-
# Build in release mode for better performance
67-
cargo build --release
68-
69-
# Build only the CLI binary
70-
cargo build --bin icp
71-
```
72-
73-
The compiled binary will be available at `target/debug/icp` (or `target/release/icp` for release builds).
74-
75-
### Running Tests
76-
77-
```bash
71+
# Test
7872
cargo test
79-
```
80-
81-
The network launcher binary is automatically downloaded on first test run. Some tests launch local networks and require available ports.
82-
83-
### Generating CLI Documentation
8473

85-
The project includes automatic CLI documentation generation using `clap_markdown`. To generate comprehensive documentation for all commands:
86-
87-
```bash
88-
# Run the documentation generation script
74+
# Generate CLI docs
8975
./scripts/generate-cli-docs.sh
90-
```
9176

92-
This will:
93-
- Build the CLI in release mode
94-
- Generate complete markdown documentation at `docs/cli-reference.md`
95-
96-
You can also generate documentation manually:
97-
98-
```bash
99-
# Build the CLI first
100-
cargo build --release
101-
102-
# Generate markdown documentation
103-
./target/release/icp --markdown-help > docs/cli-reference.md
77+
# Update the yaml file schemas
78+
./scripts/config-schemas.sh
10479
```
10580

106-
## Contributing
107-
108-
Contributions are welcome! Please see the [contribution guide](./.github/CONTRIBUTING.md) for more information.
109-
11081
## License
11182

112-
This project is licensed under the [Apache-2.0](./LICENSE) license.
83+
[Apache-2.0](LICENSE)

crates/icp/src/lib.rs

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,3 +510,169 @@ impl ProjectLoad for NoProjectLoader {
510510
Ok(false)
511511
}
512512
}
513+
514+
#[cfg(test)]
515+
mod tests {
516+
use super::*;
517+
use crate::canister::recipe::{Resolve, ResolveError};
518+
use crate::manifest::{
519+
ProjectRootLocate, ProjectRootLocateError,
520+
canister::{BuildSteps, SyncSteps},
521+
recipe::Recipe,
522+
};
523+
use camino_tempfile::Utf8TempDir;
524+
use indoc::indoc;
525+
526+
struct MockProjectRootLocate {
527+
path: PathBuf,
528+
}
529+
530+
impl MockProjectRootLocate {
531+
fn new(path: PathBuf) -> Self {
532+
Self { path }
533+
}
534+
}
535+
536+
impl ProjectRootLocate for MockProjectRootLocate {
537+
fn locate(&self) -> Result<PathBuf, ProjectRootLocateError> {
538+
Ok(self.path.clone())
539+
}
540+
}
541+
542+
struct MockRecipeResolver;
543+
544+
#[async_trait]
545+
impl Resolve for MockRecipeResolver {
546+
async fn resolve(&self, _recipe: &Recipe) -> Result<(BuildSteps, SyncSteps), ResolveError> {
547+
use crate::manifest::adapter::prebuilt::{
548+
Adapter as PrebuiltAdapter, LocalSource, SourceField,
549+
};
550+
use crate::manifest::canister::BuildStep;
551+
552+
// Create a minimal BuildSteps with a dummy prebuilt step
553+
let build_steps = BuildSteps {
554+
steps: vec![BuildStep::Prebuilt(PrebuiltAdapter {
555+
source: SourceField::Local(LocalSource {
556+
path: "dummy.wasm".into(),
557+
}),
558+
sha256: None,
559+
})],
560+
};
561+
562+
Ok((build_steps, SyncSteps::default()))
563+
}
564+
}
565+
566+
#[tokio::test]
567+
async fn test_load_minimal_project() {
568+
// Create temp directory with icp.yaml
569+
let temp_dir = Utf8TempDir::new().unwrap();
570+
let project_dir = temp_dir.path();
571+
572+
// Write a minimal icp.yaml
573+
let manifest_content = indoc! {r#"
574+
canisters:
575+
- name: backend
576+
build:
577+
steps:
578+
- type: pre-built
579+
path: backend.wasm
580+
"#};
581+
std::fs::write(project_dir.join("icp.yaml"), manifest_content).unwrap();
582+
583+
// Create ProjectLoadImpl with mocks
584+
let loader = ProjectLoadImpl {
585+
project_root_locate: Arc::new(MockProjectRootLocate::new(project_dir.to_path_buf())),
586+
recipe: Arc::new(MockRecipeResolver),
587+
};
588+
589+
// Call load
590+
let result = loader.load().await;
591+
592+
// Assert success and check project contents
593+
assert!(result.is_ok());
594+
let project = result.unwrap();
595+
assert_eq!(project.dir, project_dir);
596+
assert!(
597+
project.canisters.contains_key("backend"),
598+
"The backend canister was not found"
599+
);
600+
assert!(
601+
project.environments.contains_key("local"),
602+
"The default `local` environment was not injected"
603+
);
604+
assert!(
605+
project.environments.contains_key("ic"),
606+
"The default `ic` environment was not injected"
607+
);
608+
assert!(
609+
project.networks.contains_key("local"),
610+
"The default `local` network was not injected"
611+
);
612+
assert!(
613+
project.networks.contains_key("ic"),
614+
"The default `ic` network was not injected"
615+
);
616+
}
617+
618+
#[tokio::test]
619+
async fn test_load_project_local_override() {
620+
// Create temp directory with icp.yaml
621+
let temp_dir = Utf8TempDir::new().unwrap();
622+
let project_dir = temp_dir.path();
623+
624+
// Write a minimal icp.yaml
625+
let manifest_content = indoc! {r#"
626+
networks:
627+
- name: test-network
628+
mode: connected
629+
url: https://somenetwork.icp
630+
environments:
631+
- name: local
632+
network: test-network
633+
canisters:
634+
- name: backend
635+
build:
636+
steps:
637+
- type: pre-built
638+
path: backend.wasm
639+
"#};
640+
std::fs::write(project_dir.join("icp.yaml"), manifest_content).unwrap();
641+
642+
// Create ProjectLoadImpl with mocks
643+
let loader = ProjectLoadImpl {
644+
project_root_locate: Arc::new(MockProjectRootLocate::new(project_dir.to_path_buf())),
645+
recipe: Arc::new(MockRecipeResolver),
646+
};
647+
648+
// Call load
649+
let result = loader.load().await;
650+
651+
// Assert success and check project contents
652+
assert!(result.is_ok(), "The project did not load: {:?}", result);
653+
let project = result.unwrap();
654+
assert_eq!(project.dir, project_dir);
655+
assert!(
656+
project.canisters.contains_key("backend"),
657+
"The backend canister was not found"
658+
);
659+
assert!(
660+
project.environments.contains_key("local"),
661+
"The default `local` environment was not injected"
662+
);
663+
let e = project.environments.get("local").unwrap();
664+
assert_eq!(e.network.name, "test-network");
665+
assert!(
666+
project.environments.contains_key("ic"),
667+
"The default `ic` environment was not injected"
668+
);
669+
assert!(
670+
project.networks.contains_key("local"),
671+
"The default `local` network was not injected"
672+
);
673+
assert!(
674+
project.networks.contains_key("ic"),
675+
"The default `ic` network was not injected"
676+
);
677+
}
678+
}

0 commit comments

Comments
 (0)