From 029f3b59f30314921dd42a090f315f0f4d2bdb2a Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Thu, 5 Feb 2026 15:08:30 +0100 Subject: [PATCH] fix: improve recipe templates and documentation accuracy --- .claude/CLAUDE.md | 82 +++++++++++++++++++++++++++++ README.md | 26 +++++----- recipes/asset-canister/README.md | 70 +++++++++++++------------ recipes/motoko/README.md | 89 +++++++++++++++++--------------- recipes/motoko/recipe.hbs | 5 +- recipes/prebuilt/README.md | 88 +++++++++++++++++-------------- recipes/prebuilt/recipe.hbs | 17 ++++++ recipes/rust/README.md | 83 ++++++++++++++--------------- recipes/rust/recipe.hbs | 2 +- 9 files changed, 288 insertions(+), 174 deletions(-) create mode 100644 .claude/CLAUDE.md diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md new file mode 100644 index 0000000..62c4734 --- /dev/null +++ b/.claude/CLAUDE.md @@ -0,0 +1,82 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +`icp-cli-recipes` contains official build recipe templates for Internet Computer (ICP) canisters. Recipes provide standardized, reusable build configurations using Handlebars templates. + +## Repository Structure + +``` +recipes/ +├── rust/ # Rust canister recipe +│ ├── recipe.hbs # Handlebars template +│ └── README.md # Documentation +├── motoko/ # Motoko canister recipe +├── prebuilt/ # Pre-built WASM recipe +└── asset-canister/ # Asset canister recipe +``` + +## Recipe Template Structure + +Each recipe is a Handlebars template (`.hbs`) that generates YAML build/sync configuration: + +```handlebars +{{! Documentation comments for parameters }} +{{! `param: type` Description }} + +build: + steps: + - type: script + commands: + - command using {{ param }} + + {{#if optional_param}} + - type: script + commands: + - conditional command + {{/if}} +``` + +## Key Patterns + +### Required vs Optional Parameters + +- **Required parameters**: Used directly as `{{ param }}` - will cause strict mode error if missing +- **Optional parameters**: Wrapped in conditionals `{{#if param}}{{ param }}{{/if}}` + +### Common Configuration Options + +Most recipes support these optional parameters: +- `shrink: boolean` - Optimize WASM with ic-wasm shrink +- `compress: boolean` - Gzip compress the WASM +- `metadata: array` - Custom metadata key-value pairs + +## Documentation Verification + +**IMPORTANT**: When modifying recipes, always verify: + +1. **Template matches README**: Every parameter in `recipe.hbs` must be documented in the README +2. **Required vs Optional**: Parameters used directly (not in `{{#if}}`) are required - document accordingly +3. **Config option descriptions**: Each parameter must accurately describe what it does, verified against the actual behavior in `recipe.hbs`. For example, if `shrink` runs `ic-wasm shrink`, the description should reflect what that command does ("Remove unused functions and debug info to reduce file size") +4. **YAML syntax in examples**: Use `canisters: - name:` array syntax, not `canister:` +5. **Recipe type format**: Use `@dfinity/`, not just `` +6. **Prerequisites accuracy**: List actual dependencies (mops vs moc, ic-wasm requirements, etc.) +7. **Build process accuracy**: Document what the recipe actually does, step by step + +### Cross-Repository Verification + +This repository is used by `icp-cli`. When making changes: + +1. Check `icp-cli/docs/guides/using-recipes.md` for recipe usage examples +2. Check `icp-cli/examples/` for example projects using recipes +3. Ensure documentation in both repos stays in sync + +## Testing Changes + +Since recipes are Handlebars templates, test by: + +1. Creating a test project with `icp.yaml` using the recipe +2. Running `icp project show` to see the expanded configuration +3. Running `icp build` to verify the build works diff --git a/README.md b/README.md index 37f5c17..5d5092b 100644 --- a/README.md +++ b/README.md @@ -20,13 +20,13 @@ Official build recipe templates for Internet Computer (ICP) canisters. Recipes p Reference a recipe in your `icp.yaml` file: ```yaml -canister: - name: backend - recipe: - type: "@dfinity/rust" - configuration: - package: my-canister - shrink: true +canisters: + - name: backend + recipe: + type: "@dfinity/rust" + configuration: + package: my-canister + shrink: true ``` ### Recipe Naming Convention @@ -42,12 +42,12 @@ Recipes follow the `@dfinity/` naming pattern: To pin to a specific recipe version, append `@` to the recipe type: ```yaml -canister: - name: backend - recipe: - type: "@dfinity/rust@v3.0.0" - configuration: - package: my-canister +canisters: + - name: backend + recipe: + type: "@dfinity/rust@v3.0.0" + configuration: + package: my-canister ``` Without a version specified, the latest stable version is used. View available versions in the [releases page](https://github.com/dfinity/icp-cli-recipes/releases). diff --git a/recipes/asset-canister/README.md b/recipes/asset-canister/README.md index 5534eed..05e9051 100644 --- a/recipes/asset-canister/README.md +++ b/recipes/asset-canister/README.md @@ -7,61 +7,63 @@ Download and configure the official IC assets canister with asset synchronizatio Example of how to reference this recipe in an `icp.yaml` file: ```yaml -canister: - name: frontend - recipe: - type: "@dfinity/asset-canister" - configuration: - version: 0.30.2 - dir: dist +canisters: + - name: frontend + recipe: + type: "@dfinity/asset-canister" + configuration: + version: 0.30.2 + dir: dist ``` ## Configuration Parameters | Parameter | Type | Required | Description | Default | |-----------|------|----------|-------------|---------| -| version | string | No | SDK version tag to download assets canister from | latest | -| dir | string | Yes | Directory containing frontend assets to synchronize | - | -| build | array | No | Commands to build the frontend assets | [] | -| metadata | array | No | Array of key-value pairs for custom metadata | [] | +| version | string | No | SDK version tag to download the assets canister from (e.g., `0.30.2`) | latest | +| dir | string | Yes | Directory containing frontend assets to synchronize to the canister | - | +| build | array | No | Shell commands to build the frontend assets before deployment (e.g., `npm run build`) | [] | +| metadata | array | No | Array of key-value pairs for custom metadata to inject into the WASM | [] | ## Prerequisites -- `ic-wasm` tool must be installed for metadata injection and optimization -- Frontend assets must either exist in the specified directory or be built using provided commands +- Frontend assets must either exist in the specified directory or be built using provided `build` commands - Internet connection required to download the assets canister WASM +- `ic-wasm` (included with icp-cli installation) - only required if using `metadata` option + +> **Note:** If you followed the [icp-cli installation guide](https://github.com/dfinity/icp-cli#installation), `ic-wasm` is already installed. ## Examples ### Basic Example ```yaml -canister: - name: website - recipe: - type: "@dfinity/asset-canister" - configuration: - dir: build +canisters: + - name: website + recipe: + type: "@dfinity/asset-canister" + configuration: + dir: build ``` ### Advanced Example ```yaml -canister: - name: spa-frontend - recipe: - type: "@dfinity/asset-canister" - configuration: - version: 0.30.2 - build: - - npm install - - npm run build - dir: dist - metadata: - - name: "frontend:framework" - value: "react" - - name: "frontend:version" - value: "1.0.0" +canisters: + - name: spa-frontend + recipe: + type: "@dfinity/asset-canister" + configuration: + version: 0.30.2 + build: + - npm install + - npm run build + dir: dist + metadata: + - name: "frontend:framework" + value: "react" + - name: "frontend:version" + value: "1.0.0" ``` ## Build Process diff --git a/recipes/motoko/README.md b/recipes/motoko/README.md index 08e5b0d..4e0326c 100644 --- a/recipes/motoko/README.md +++ b/recipes/motoko/README.md @@ -7,78 +7,81 @@ Compile Motoko source code using the moc compiler to create IC canisters. Example of how to reference this recipe in an `icp.yaml` file: ```yaml -canister: - name: backend - recipe: - type: "@dfinity/motoko" - configuration: - entry: src/main.mo - shrink: true +canisters: + - name: backend + recipe: + type: "@dfinity/motoko" + configuration: + main: src/main.mo + shrink: true ``` ## Configuration Parameters -| Parameter | Type | Required | Description | Default | -|-----------|---------|----------|----------------------------------------------|---------------------------| -| entry | string | Yes | Path to the main Motoko source file | (autogenerated interface) | -| candid | string | No | Path to the Candid interface file | | -| metadata | array | No | Array of key-value pairs for custom metadata | [] | -| shrink | boolean | No | Enable WASM optimization to reduce file size | false | -| compress | boolean | No | Enable gzip compression of WASM file | false | +| Parameter | Type | Required | Description | Default | +|-----------|---------|----------|----------------------------------------------|---------| +| main | string | Yes | Path to the main Motoko source file | | +| args | string | No | Additional arguments to pass to moc compiler (e.g., `--incremental-gc`) | | +| candid | string | No | Path to a custom Candid interface file. If not provided, the interface is auto-generated by the Motoko compiler | (auto-generated) | +| metadata | array | No | Array of key-value pairs for custom metadata | [] | +| shrink | boolean | No | Remove unused functions and debug info to reduce file size | false | +| compress | boolean | No | Gzip compress the WASM file | false | ## Prerequisites -- `moc` (Motoko compiler) must be installed -- `ic-wasm` tool must be installed for metadata injection and optimization -- Motoko source files must be available in your project +- `mops` (Motoko package manager) - this recipe uses `mops toolchain` to manage the Motoko compiler +- `ic-wasm` (included with icp-cli installation) -### Installation +> **Note:** If you followed the [icp-cli installation guide](https://github.com/dfinity/icp-cli#installation), both `mops` and `ic-wasm` are already installed. -To install the Motoko compiler, see: +### Additional Installation + +If mops is not installed, see: ## Examples ### Basic Example ```yaml -canister: - name: hello-world - recipe: - type: "@dfinity/motoko" - configuration: - entry: src/main.mo +canisters: + - name: hello-world + recipe: + type: "@dfinity/motoko" + configuration: + main: src/main.mo ``` ### Advanced Example ```yaml -canister: - name: complex-backend - recipe: - type: "@dfinity/motoko" - configuration: - entry: src/backend.mo - shrink: true - compress: true - metadata: - - name: "canister:type" - value: "backend" - - name: "language:version" - value: "0.10.0" +canisters: + - name: complex-backend + recipe: + type: "@dfinity/motoko" + configuration: + main: src/backend.mo + args: --incremental-gc + shrink: true + compress: true + metadata: + - name: "canister:type" + value: "backend" + - name: "language:version" + value: "0.10.0" ``` ## Build Process When this recipe is executed: -1. Checks if the Motoko compiler (`moc`) is installed -2. Compiles the specified Motoko entry file using `moc` -3. Moves the resulting WASM file to the output location +1. Checks if `mops` is installed (used for Motoko toolchain management) +2. Compiles the specified Motoko entry file using `moc` via `mops toolchain` +3. Handles Candid interface: auto-generates from Motoko source, or uses the provided `candid` file 4. Injects compiler version metadata ("moc:version") 5. Injects template type metadata ("template:type" = "motoko") 6. Injects any custom metadata specified in the configuration -7. Optionally optimizes the WASM file if `shrink` is enabled -8. Optionally compresses the WASM file if `compress` is enabled +7. Optionally removes unused functions if `shrink` is enabled +8. Optionally gzip compresses the WASM file if `compress` is enabled ## Project Structure diff --git a/recipes/motoko/recipe.hbs b/recipes/motoko/recipe.hbs index 0a658a9..7ab4ee7 100644 --- a/recipes/motoko/recipe.hbs +++ b/recipes/motoko/recipe.hbs @@ -1,8 +1,9 @@ {{! A recipe for building a Motoko canister }} {{! `main: string` Motoko file which contains the main actor }} {{! `args: string` Additional arguments to pass to the moc compiler }} +{{! `candid: string` Optional, the path to the Candid interface file }} {{! `shrink: boolean` Optimizes the wasm with ic-wasm }} -{{! `compressed: boolean` determins whether the was should be gzipped }} +{{! `compress: boolean` determines whether the wasm should be gzipped }} {{! `metadata: [name: string, value: string]`: An array of name/value pairs that get injected into the wasm metadata section }} build: @@ -10,7 +11,7 @@ build: - type: script commands: - sh -c 'command -v mops >/dev/null 2>&1 || { echo >&2 "'mops' not found on path. To install Mops, see https://mops.one/docs/install.\n"; exit 1; }' - - sh -c '$(mops toolchain bin moc) "{{ main }}" {{ args }} {{#if candid}}--omit-metadata{{else}}--public-metadata{{/if}} candid:service $(mops sources) -o "$ICP_WASM_OUTPUT_PATH"' + - sh -c '$(mops toolchain bin moc) "{{ main }}" {{#if args}}{{ args }} {{/if}}{{#if candid}}--omit-metadata{{else}}--public-metadata{{/if}} candid:service $(mops sources) -o "$ICP_WASM_OUTPUT_PATH"' - type: script commands: diff --git a/recipes/prebuilt/README.md b/recipes/prebuilt/README.md index 35d3e5f..8c18b7d 100644 --- a/recipes/prebuilt/README.md +++ b/recipes/prebuilt/README.md @@ -7,18 +7,18 @@ Use pre-compiled WASM files with optional optimization and metadata injection. Example of how to reference this recipe in an `icp.yaml` file: ```yaml -canister: - name: my-canister - recipe: - type: "@dfinity/pre-built" - configuration: - path: dist/canister.wasm - sha256: 17a05e36278cd04c7ae6d3d3226c136267b9df7525a0657521405e22ec96be7a - shrink: true - compress: false - metadata: - - name: "custom:version" - value: "1.0.0" +canisters: + - name: my-canister + recipe: + type: "@dfinity/prebuilt" + configuration: + path: dist/canister.wasm + sha256: 17a05e36278cd04c7ae6d3d3226c136267b9df7525a0657521405e22ec96be7a + shrink: true + compress: true + metadata: + - name: "custom:version" + value: "1.0.0" ``` ## Configuration Parameters @@ -26,46 +26,53 @@ canister: | Parameter | Type | Required | Description | Default | |-----------|------|----------|-------------|---------| | path | string | Yes | Local path to the pre-built WASM file | - | -| sha256 | string | No | SHA256 hash for integrity verification | - | -| metadata | array | No | Array of key-value pairs for custom metadata | [] | +| sha256 | string | No | SHA256 hash for integrity verification. Generate with `sha256sum .wasm` | - | +| shrink | boolean | No | Remove unused functions and debug info to reduce file size | false | +| compress | boolean | No | Gzip compress the WASM file | false | +| metadata | array | No | Array of key-value pairs for custom metadata to inject into the WASM | [] | ## Prerequisites -- `ic-wasm` tool must be installed for metadata injection and optimization - Pre-compiled WASM file must exist at the specified path -- SHA256 hash must be provided for integrity verification +- `ic-wasm` (included with icp-cli installation) - only required if using `metadata` or `shrink` options + +> **Note:** If you followed the [icp-cli installation guide](https://github.com/dfinity/icp-cli#installation), `ic-wasm` is already installed. + +> **Note:** Providing a SHA256 hash is optional but recommended for integrity verification. ## Examples ### Basic Example ```yaml -canister: - name: simple-canister - recipe: - type: "@dfinity/pre-built" - configuration: - path: target/wasm32-unknown-unknown/release/my_canister.wasm - sha256: abc123def456... +canisters: + - name: simple-canister + recipe: + type: "@dfinity/prebuilt" + configuration: + path: target/wasm32-unknown-unknown/release/my_canister.wasm + sha256: abc123def456... ``` ### Advanced Example ```yaml -canister: - name: optimized-canister - recipe: - type: "@dfinity/pre-built" - configuration: - path: dist/production-canister.wasm - sha256: xyz789abc123... - metadata: - - name: "build:version" - value: "2.1.0" - - name: "build:environment" - value: "production" - - name: "build:timestamp" - value: "2024-01-01T00:00:00Z" +canisters: + - name: optimized-canister + recipe: + type: "@dfinity/prebuilt" + configuration: + path: dist/production-canister.wasm + sha256: xyz789abc123... + shrink: true + compress: true + metadata: + - name: "build:version" + value: "2.1.0" + - name: "build:environment" + value: "production" + - name: "build:timestamp" + value: "2024-01-01T00:00:00Z" ``` ## Build Process @@ -73,9 +80,10 @@ canister: When this recipe is executed: 1. Copies the pre-built WASM file to the output location -2. Verifies file integrity using the provided SHA256 hash -3. Injects template type metadata ("template:type" = "pre-built") -4. Injects any custom metadata specified in the configuration +2. Verifies file integrity using the provided SHA256 hash (if specified) +3. Injects any custom metadata specified in the configuration (if specified) +4. Optionally optimizes the WASM file if `shrink` is enabled +5. Optionally compresses the WASM file if `compress` is enabled ## Common Issues diff --git a/recipes/prebuilt/recipe.hbs b/recipes/prebuilt/recipe.hbs index 438b87e..2aca2dd 100644 --- a/recipes/prebuilt/recipe.hbs +++ b/recipes/prebuilt/recipe.hbs @@ -1,6 +1,8 @@ {{! A recipe for deploying a pre-built canister }} {{! `path: string` path to the wasm }} {{! `sha256: string` Optional, the hash of the wasm to deploy}} +{{! `shrink: boolean` Optimizes the wasm with ic-wasm }} +{{! `compress: boolean` determines whether the wasm should be gzipped }} {{! `metadata: [name: string, value: string]`: An array of name/value pairs that get injected into the wasm metadata section }} build: @@ -22,3 +24,18 @@ build: {{/each}} {{/if}} + {{#if shrink}} + - type: script + commands: + - command -v ic-wasm >/dev/null 2>&1 || { echo >&2 "ic-wasm not found. To install ic-wasm, see https://github.com/dfinity/ic-wasm \n"; exit 1; } + - ic-wasm "$ICP_WASM_OUTPUT_PATH" -o "${ICP_WASM_OUTPUT_PATH}" shrink --keep-name-section + {{/if}} + + {{#if compress}} + - type: script + commands: + - command -v gzip >/dev/null 2>&1 || { echo >&2 "gzip not found. Please install gzip to compress build output. \n"; exit 1; } + - gzip --no-name "$ICP_WASM_OUTPUT_PATH" + - mv "${ICP_WASM_OUTPUT_PATH}.gz" "$ICP_WASM_OUTPUT_PATH" + {{/if}} + diff --git a/recipes/rust/README.md b/recipes/rust/README.md index 6b78415..a430ac0 100644 --- a/recipes/rust/README.md +++ b/recipes/rust/README.md @@ -7,13 +7,13 @@ Build Rust canisters using Cargo with WASM target for the Internet Computer. Example of how to reference this recipe in an `icp.yaml` file: ```yaml -canister: - name: backend - recipe: - type: "@dfinity/rust" - configuration: - package: my-canister - shrink: true +canisters: + - name: backend + recipe: + type: "@dfinity/rust" + configuration: + package: my-canister + shrink: true ``` ## Configuration Parameters @@ -21,28 +21,28 @@ canister: | Parameter | Type | Required | Description | Default | |-----------|---------|----------|----------------------------------------------|---------------------------| | package | string | Yes | Name of the Rust package to build | - | -| candid | string | No | Path to the Candid interface file | (autogenerated interface) | +| candid | string | No | Path to a custom Candid interface file. If not provided, the interface is auto-extracted from the WASM using `candid-extractor` | (auto-extracted) | | metadata | array | No | Array of key-value pairs for custom metadata | [] | -| shrink | boolean | No | Enable WASM optimization to reduce file size | false | -| compress | boolean | No | Enable gzip compression of WASM file | false | +| shrink | boolean | No | Remove unused functions and debug info to reduce file size | false | +| compress | boolean | No | Gzip compress the WASM file | false | ## Prerequisites -- Rust toolchain with `wasm32-unknown-unknown` target installed -- `cargo` build system -- `ic-wasm` tool must be installed for metadata injection and optimization +- Rust toolchain with `wasm32-unknown-unknown` target +- `ic-wasm` (included with icp-cli installation) +- `candid-extractor` (only required if not providing a custom `candid` file) -### Installation +> **Note:** If you followed the [icp-cli installation guide](https://github.com/dfinity/icp-cli#installation), `ic-wasm` is already installed. You only need to install `candid-extractor` if you want auto-extraction of Candid interfaces. + +### Additional Installation ```bash -# Install Rust +# Install Rust (if not already installed) curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh - -# Add WASM target rustup target add wasm32-unknown-unknown -# Install ic-wasm -cargo install ic-wasm +# Install candid-extractor (only needed if not providing candid file) +cargo install candid-extractor ``` ## Examples @@ -50,30 +50,30 @@ cargo install ic-wasm ### Basic Example ```yaml -canister: - name: hello-rust - recipe: - type: "@dfinity/rust" - configuration: - package: hello-rust +canisters: + - name: hello-rust + recipe: + type: "@dfinity/rust" + configuration: + package: hello-rust ``` ### Advanced Example ```yaml -canister: - name: dapp-backend - recipe: - type: "@dfinity/rust" - configuration: - package: dapp-backend - shrink: true - compress: true - metadata: - - name: "crate:version" - value: "1.0.0" - - name: "build:profile" - value: "release" +canisters: + - name: dapp-backend + recipe: + type: "@dfinity/rust" + configuration: + package: dapp-backend + shrink: true + compress: true + metadata: + - name: "crate:version" + value: "1.0.0" + - name: "build:profile" + value: "release" ``` ## Build Process @@ -85,9 +85,10 @@ When this recipe is executed: 3. Handles package name conversion (hyphens to underscores for the WASM filename) 4. Injects Cargo version metadata ("cargo:version") 5. Injects template type metadata ("template:type" = "rust") -6. Injects any custom metadata specified in the configuration -7. Optionally optimizes the WASM file if `shrink` is enabled -8. Optionally compresses the WASM file if `compress` is enabled +6. Injects Candid interface: uses the provided `candid` file, or auto-extracts it from the WASM using `candid-extractor` +7. Injects any custom metadata specified in the configuration +8. Optionally removes unused functions if `shrink` is enabled +9. Optionally gzip compresses the WASM file if `compress` is enabled ## Project Structure diff --git a/recipes/rust/recipe.hbs b/recipes/rust/recipe.hbs index d447f41..51552c3 100644 --- a/recipes/rust/recipe.hbs +++ b/recipes/rust/recipe.hbs @@ -2,7 +2,7 @@ {{! `package: string` The package to build }} {{! `candid: string` The path to the Candid interface file }} {{! `shrink: boolean` Optimizes the wasm with ic-wasm }} -{{! `compressed: boolean` determins whether the was should be gzipped }} +{{! `compress: boolean` determines whether the wasm should be gzipped }} {{! `metadata: [name: string, value: string]`: An array of name/value pairs that get injected into the wasm metadata section }} build: