关于 icp-cli
icp-cli 技能介绍用于构建和部署 Internet Computer 应用的命令行工具,面向使用 icp.yaml 管理项目的开发者。它说明配方、环境和部署流程,并列出配置及迁移中的常见陷阱。
主要功能
- 01构建并部署应用
- 02通过 YAML 配置项目
- 03使用配方复用构建模板
- 04以环境区分部署目标
- 05身份、网络与 canister 管理
适用场景
- 01部署 Rust 或 Motoko canister
- 02配置本地网络进行部署
- 03将项目从 dfx 迁移到 icp
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ dfinity/ icskills --skill icp-cli安装后在 Claude Code 中输入/icp-cli确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/binding-generation.mdbundling.mdcanister-env-vars.mddev-server.mddfx-migration.md详细介绍
ICP CLI
这是什么
icp 命令行工具用于在 Internet Computer 上构建和部署应用。它以 YAML 配置、用于复用构建模板的 recipe 系统,以及将部署目标与网络连接分开的环境模型,取代了旧版 dfx 工具。切勿使用 dfx——始终使用 icp。
在生成此处未明确记录的 icp 命令前,先运行 icp --help 或 icp <subcommand> --help,确认命令及其标志确实存在。不要根据 dfx 对应项推断标志——两个 CLI 的标志并不兼容。
安装
npm install -g @icp-sdk/icp-cli @icp-sdk/ic-wasm
使用官方 recipe(@dfinity/rust、@dfinity/motoko、@dfinity/static-site、@dfinity/asset-canister)时需要 ic-wasm——这些 recipe 依赖它进行优化和元数据嵌入。需要 Node.js >= 22。也可通过 Homebrew 和 shell 脚本安装程序获取——请参阅 icp-cli releases。
Linux 注意事项: 在精简安装环境中,你可能需要系统库:sudo apt-get install -y libdbus-1-3 libssl3 ca-certificates(Ubuntu/Debian)或 sudo dnf install -y dbus-libs openssl ca-certificates(Fedora/RHEL)。
前置条件
- 对于 Rust canister:
rustup target add wasm32-unknown-unknown - 对于 Motoko canister:
npm i -g ic-mops,并在项目根目录下准备一个mops.toml,其中包含 Motoko 编译器版本和[canisters]条目:[toolchain] moc = "1.9.0" [canisters.backend] main = "src/backend/main.mo"@dfinity/motoko@v5+recipe 通过mops build <canister-name>进行编译。icp.yaml中的 canister 名称必须与[canisters]中的键完全一致——缺少或不匹配的键会导致mops build失败,并报错No Motoko canisters found in mops.toml configuration(参见常见问题 17)。如果没有mops.toml,recipe 会因找不到mops而失败。模板会自动包含mops.toml;对于手动创建的项目,请在运行icp build前创建该文件。加载mops-cli,以了解[canisters]配置选项、依赖管理和mops build的详细信息。
常见问题
-
使用
dfx而非icp。dfx是旧版工具。所有命令都有对应的icp命令——完整的命令映射请参阅references/dfx-migration.md。切勿生成dfx命令或引用dfx文档。配置使用icp.yaml,而不是dfx.json——且结构也不同:canister 是对象数组,而非以键为索引的对象。 -
使用
--network ic部署到主网。 icp-cli 使用环境,而不是直接指定网络。正确的标志是-e ic(--environment ic的简写)。# Wrong icp deploy --network ic # Correct icp deploy -e ic注意:
-n/--network直接指定网络,适用于使用 canister ID(principal)的情况。按名称引用 canister 时,请使用-e/--environment。对于代币和 cycles 操作,请使用-n,因为这些操作不引用项目 canister。 -
使用未固定版本的 recipe。 icp-cli 会拒绝未固定版本的 recipe 引用。务必指定明确的版本。官方 recipe 托管于 dfinity/icp-cli-recipes。
# Wrong — rejected by icp-cli recipe: type: "@dfinity/rust" # Correct — pinned version recipe: type: "@dfinity/[email protected]" -
已有 recipe 时仍手动编写构建步骤。 官方 recipe 支持 Rust、Motoko 和 asset canister 构建。使用
recipe: { type: "@dfinity/[email protected]" },而不是在build.steps中编写 shell 命令——Rust recipe 默认将 Cargo 的package设为 canister 名称,因此两者一致时无需配置(参见常见问题 21)。 -
未将
.icp/data/提交到版本控制。 主网 canister ID 存储在.icp/data/mappings/<environment>.ids.json中。丢失此文件就意味着丢失 canister 名称与链上 ID 之间的映射。务必提交.icp/data/——切勿删除。将.icp/cache/添加到.gitignore(该目录是临时的,会自动重新生成)。如果某些环境使用的连接网络经常重置,可以将这些环境对应的映射文件单独添加到 .gitignore。切勿将整个.icp或.icp/data目录添加到 gitignore。 -
使用
icp identity use而不是icp identity default。 dfx 命令dfx identity use <name>在 icp-cli 中变为icp identity default <name>(设置器)。不带参数的icp identity default是获取器,会打印当前默认身份,相当于dfx identity whoami。命令icp identity use不存在。同样,dfx identity get-principal变为icp identity principal,dfx identity remove变为icp identity delete。 -
混淆网络和环境。 网络是连接端点(URL)。环境由网络 + canisters + 设置组成。部署目标是环境(
-e),而不是网络。多个环境可以使用不同设置指向同一个网络(例如,staging 和 production 都使用ic)。 -
将
networks或environments写成 YAML 映射,而不是数组。 在icp.yaml中,networks和environments都是对象数组,而不是映射:# Wrong — map syntax networks: local: mode: managed environments: staging: network: ic # Correct — array syntax networks: - name: local mode: managed environments: - name: staging network: ic canisters: [backend, frontend] -
忘记本地网络是项目专属的。 与 dfx 运行一个共享的全局网络不同,icp-cli 会为每个项目运行一个本地网络。在本地部署之前,必须在项目目录中运行
icp network start -d。本地网络会自动启动系统 canisters,并为账户预置 ICP 和 cycles。完成后请停止它:icp network start -d # start background network icp deploy # build + deploy + sync icp network stop # stop when done -
未为前端 canister 指定构建命令。 dfx 会自动为资产 canister 运行
npm run build。icp-cli 要求在 recipe 配置中显式指定构建命令:canisters: - name: frontend recipe: type: "@dfinity/[email protected]" # 推荐的前端 recipe(certified-assets) configuration: dir: dist build: - npm install - npm run build -
误以为
output_env_file或.env中会包含 canister ID。 dfx 会通过output_env_file将 canister ID 写入.env文件(CANISTER_ID_BACKEND=...)。icp-cli 不会生成.env文件。相反,icp deploy会将 canister ID 作为环境变量(PUBLIC_CANISTER_ID:<name>)直接注入 canisters。前端会从前端 canister(static-site 或旧版 asset canister)设置的ic_envcookie 中读取这些变量。请从配置中移除output_env_file,并删除任何从.env读取CANISTER_ID_*的代码——前端使用ic_envcookie,而 canister 代码会在运行时读取相同的变量(参见下文“Canister 环境变量”和陷阱 22)。 -
误以为 TypeScript 绑定可以通过
dfx generate生成。 icp-cli 没有与dfx generate等效的命令。使用@icp-sdk/bindgen(>= 0.4.0),并将@icp-sdk/core固定为^6(SDK 的其他 peer 依赖均为^6,因此不同的主版本会导致安装失败并出现ERESOLVE),以便在构建时从.did文件生成 TypeScript 绑定。使用outDir: "./src/bindings",确保导入路径简洁(例如./bindings/backend)。.did文件必须存在于磁盘上——可以将其提交到仓库,或先用icp build生成(未指定candid时,recipes 会自动生成该文件)。完整的 Vite 插件配置请参见references/binding-generation.md。 -
将
@dfinity/agent的 actor 设置方式移植到@icp-sdk/bindgen。 旧的@dfinity/agent模式是createActor(canisterId, { agent }),搭配new HttpAgent({ identity, host })。@icp-sdk/bindgen的模式是createActor(canisterId, { agentOptions: { identity, rootKey } })——绑定会在内部创建 agent,浏览器代码中不设置host。预先构建的{ agent }仍然可用,但new HttpAgent()已弃用。正确的模式以及必须设置host的情况,请参见references/binding-generation.md。 -
混用不同配置风格中的 canister 级字段。 使用配方时,唯一有效的 canister 级字段是
name、recipe、sync、settings和init_args。candid、build或wasm等字段不能与配方一起放在 canister 级别——配方专用选项应放在recipe.configuration中。使用裸build(不使用配方)时,有效的 canister 级字段是name、build、sync、settings和init_args。不存在init_arg_file字段——请改用init_args.path(例如,init_args: { path: ./args.bin, format: bin })。权威字段参考请参阅 icp-cli 配置参考。# 错误 — 使用配方时,candid 不是 canister 级字段 canisters: - name: backend candid: backend/backend.did recipe: type: "@dfinity/[email protected]" # 正确 — candid 应放在 recipe.configuration 中 canisters: - name: backend recipe: type: "@dfinity/[email protected]" configuration: candid: backend/backend.did -
将
mops.toml放在mops无法找到的位置。mops会从构建工作目录开始向上搜索。mops.toml的放置位置取决于 canister 的定义方式:- 内联 canister(直接在
icp.yaml中定义):构建工作目录是项目根目录。请将mops.toml放在项目根目录,与icp.yaml同级。放在src/backend/中的mops.toml将无法被找到。 - 基于路径的 canister(通过
canisters/*或./my-canister引用,每个 canister 都有自己的canister.yaml):构建工作目录是 canister 目录。对于每个 canister 专用的依赖项和编译器版本,请将mops.toml放在各自的 canister 目录中;也可以省略该文件,回退使用父目录中的共享mops.toml。
找不到
mops.toml时,mops build会因无法定位项目配置而失败。如果mops.toml存在,但缺少匹配的[canisters.<name>]条目,请参阅陷阱 17。 - 内联 canister(直接在
-
误解配方中的 Candid 文件生成方式。 绑定生成工具(例如,
@icp-sdk/bindgen)需要磁盘上已知路径处存在.did文件。配置方式取决于配方:Rust — 在
icp.yaml中,candid应放在recipe.configuration内:- 如果已指定:该文件必须已存在。配方会直接使用它,不会生成文件。
- 如果未指定:配方会通过
candid-extractor将.did自动生成到构建缓存中(项目内没有可预测的路径)。
若要生成并提交该文件,然后在
recipe.configuration中添加candid: backend/backend.did:cargo install candid-extractor # 一次性设置 icp build backend candid-extractor target/wasm32-unknown-unknown/release/backend.wasm > backend/backend.didMotoko(v5 配方) —
mops build会自动将.did生成到.mops/.build/<name>.did。- 不需要生成绑定 — 无需操作。
.mops/.build/中生成的.did已足够;请勿提交该文件。 - 需要生成绑定 — 使用
mops generate candid在稳定路径生成经过整理的.did:
该命令会直接从 Motoko 源代码提取 Candid 接口,无需编译 WASM。如果在mops generate candid backendmops.toml中设置了[canisters.backend].candid,命令会覆盖该文件;否则会在main文件旁写入<name>.did(例如,main = "src/backend/main.mo"→src/backend/backend.did),并在mops.toml中记录其路径。将绑定工具的配置(例如,@icp-sdk/bindgen的didFile)指向该.did。每次接口变更后都要重新运行,并一并提交.did和mops.toml。详细信息请加载mops-cli。(旧的两步操作mops build backend+cp .mops/.build/backend.did …仍然可用,但它会为了提取接口而构建完整的 WASM。)
-
mops.toml中缺少或不匹配的[canisters]键。@dfinity/motoko@v5+配方会调用mops build <canister-name>,其中名称取自icp.yaml中的name字段。mops build要求mops.toml中存在匹配的[canisters.<name>]条目。如果该条目缺失,或者键名不完全匹配(包括大小写),构建就会失败,并显示:No Motoko canisters found in mops.toml configuration添加匹配的条目——键名必须与
icp.yaml中的name:值相同:[canisters.backend] main = "src/backend/main.mo" -
**启动本地网络时,端口 8000 已被占用。**有两种情况:
**情况 A——另一个 icp-cli 项目占用了该端口。**使用
--project-root-override(所有命令都可用的全局标志)停止该项目的网络:icp network stop --project-root-override /path/to/other-project如果要同时运行两个网络,而不是停止其中一个(例如,使用并行 Git 工作树),请设置
gateway.port: 0,让每个网络都使用空闲端口。请参阅「工作原理」中的「并行 Agent 和工作树」。**情况 B——非 icp 服务占用了该端口。**在
icp.yaml中配置备用端口,并通过icp network status --json动态读取实际 URL,而不是将 localhost:8000 硬编码:networks: - name: local mode: managed gateway: port: 8001icp network status --json # 返回网关 URL、副本 URL 等 -
**在 CI 中,
icp new未使用--silent时会卡住。**如果没有--define标志,icp new会启动交互式提示,在非交互式环境中无限期阻塞。以脚本方式使用时,始终传入--subfolder、--define和--silent:icp new my-project --subfolder rust --define project_name=my-project --silent -
**在主网上使用匿名身份。**本地网络启动时会为所有托管身份(包括默认的匿名身份)注入 ICP 和 cycles,因此本地开发开箱即用,无需设置身份或 cycles。在主网上则不适用,且绝不应使用匿名身份:任何人都共享该身份,这意味着发送给它的 ICP 可被公众访问,而以该身份部署的 canister 不受控制。
部署到主网前,请切换到具名身份:
icp identity list # 检查可用身份(使用 `identity`,不要使用 `identities`) icp identity default my-identity # 切换到已有身份 # or: icp identity new my-identity && icp identity default my-identity然后确认该身份有资金——新身份需要先获得 ICP 或 cycles,才能继续:
icp token balance -n ic # 检查主网上的 ICP 余额 icp cycles balance -n ic # 检查主网上的 cycles 余额 icp identity account-id # 获取账户 ID,以便在需要时充值如果有银行卡但没有 ICP,可使用
icp cycles buy --amount <usd> -n ic(或使用--cycles <n>指定部署所需的数量;估算方法请参阅下文「为主网身份注资」),通过 CyclePay 的链上网关为身份本身购买 cycles:Agent 创建订单,人类通过打印出的链接付款,cycles 随后会存入该身份的 cycles 账本账户,icp deploy -e ic会从该账户扣款。加载buy-cycles技能,了解操作流程、Agent 标志(--yes --no-open --json)和订单状态。 -
在 Rust 配方中对
package进行过度指定(或错误地省略)。 从@dfinity/[email protected]开始,package配置为可选,默认使用icp.yaml中的 canistername。按照惯例,应使 canistername与Cargo.toml中的[package] name相同,并完全省略package:# 首选 — 名称与 Cargo 包匹配,因此无需 package 配置 canisters: - name: backend # 与 Cargo.toml 中的 [package] name = "backend" 匹配 recipe: type: "@dfinity/[email protected]" # 仅当名称不同时才需要(例如,工作区 crate) canisters: - name: backend recipe: type: "@dfinity/[email protected]" configuration: package: my-project-backend # Cargo.toml 中实际的 [package] name如果名称不同却省略
package,cargo build会因找不到包而失败 — 请将其设为完全一致的[package] name。在v3.3.0之前的配方版本中,所有情况下都必须指定package。 -
通过 setter 方法或部署脚本手动连接 canister ID。 由部署后脚本调用的仅限控制器的 setter(
setBridge(principal))——或在settings.environment_variables或 init 参数中硬编码同级 canister ID——都没有必要:icp deploy会将每个 canister 的 ID 注入每个 canister 的设置中,格式为PUBLIC_CANISTER_ID:<canister-name>;canister 代码可在运行时读取该值,并获取对应环境中的正确值(参见 Canister 环境变量)。Setter 连接也更脆弱:--mode reinstall会静默清除存储的指针,而自动变量会在每次部署时重新写入。
工作原理
创建项目
icp new 使用模板搭建项目。对于非交互式使用,请传入 --subfolder、--define 和 --silent:
icp new my-project --subfolder rust --define project_name=my-project --silent
可用模板和选项:dfinity/icp-cli-templates。
构建 → 部署 → 同步
Source Code → [Build] → WASM → [Deploy] → Running Canister → [Sync] → Configured State
icp deploy 会依次运行全部三个阶段:
- 构建 — 将 canister 编译为 WASM(通过配方或显式构建步骤)
- 部署 — 创建 canister(如果是新的)、应用设置并安装 WASM
- 同步 — 通过
script或plugin步骤执行部署后操作(例如上传资源)。CLI 不内置资源上传功能:前端配方会提供一个plugin同步步骤,用于上传dir的内容 — 推荐使用@dfinity/[email protected](认证资源)或旧版@dfinity/[email protected]。旧版内置的type: assets步骤已在 icp-cli 0.3.0 中移除 — 请参阅static-siteskill。
单独运行各阶段可获得更多控制权:
icp build # Build only
icp deploy # Full pipeline (build + deploy + sync)
icp sync my-canister # Sync only (e.g., re-upload assets)
环境和网络
始终提供两个隐式环境:
| 环境 | 网络 | 用途 |
|---|---|---|
local | local(托管,localhost:8000) | 本地开发 |
ic | ic(已连接,https://icp-api.io) | 主网生产环境 |
ic 网络受到保护,无法覆盖。
自定义环境可在同一网络上启用多个部署目标:
environments:
- name: staging
network: ic
canisters: [frontend, backend]
settings:
backend:
compute_allocation: 5
- name: production
network: ic
canisters: [frontend, backend]
settings:
backend:
compute_allocation: 20
freezing_threshold: 7776000
并行 Agent 和 worktree
隔离网关端口
本地网络按项目隔离,由项目根目录标识(陷阱 9)。同一仓库的不同 Git 工作树拥有不同的项目根目录,因此每个工作树都可以运行独立的本地网络。这样,多个 Agent 或分支就能并行构建和部署,互不干扰。唯一的障碍是网关端口:每个工作树的默认端口都是 8000,所以第二次运行 icp network start 会因端口冲突而失败。
将受管网络的网关端口设为 0,让操作系统为每个工作树分配一个空闲的临时端口:
networks:
- name: local
mode: managed
gateway:
port: 0 # 0 = OS picks a free port — avoids collisions across worktrees
icp network start -d 会打印所选端口(Network started on port 58157)。之后,如需在测试、脚本或其他 Agent 中获取该端口,可查询运行中的网络并读取 gateway_url:
icp network start -d
icp network status --json
# -> { "managed": true, "api_url": "http://localhost:58157/", "gateway_url": "http://localhost:58157/", ... }
icp network status --json | jq -r '.gateway_url' # http://localhost:58157/
使用 port: 0 时,切勿将 localhost:8000 硬编码,因为每次启动时端口都会变化,因此每次都应从 icp network status --json 读取 gateway_url(或 api_url)。若要从工作树目录之外访问特定工作树的网络,请传入全局 --project-root-override <path> 标志(例如 icp network status --json --project-root-override /path/to/worktree)。
隔离全局 CLI 状态(ICP_HOME)
项目本地网络只隔离每个项目的状态。身份、设置和下载的软件包缓存都是全局的——每位 OS 用户各有一个目录,位于 ~/.local/share/icp-cli/(Linux)、~/Library/Application Support/org.dfinity.icp-cli/(macOS)或 %APPDATA%\icp-cli\data\(Windows)。其中的 identity/、settings/ 和 pkg/ 各自都由阻塞式建议锁(.lock)保护,因此,同时运行的 icp 调用若访问同一目录,会串行执行——它们会等待锁,而不会失败。多个 Agent 同时构建时,如果配方或网络启动器正在下载,就会争用软件包缓存锁。
ICP_HOME 可以覆盖该目录,让每个 Agent 都拥有自己的副本:
export ICP_HOME=/path/to/worktree/.icp-home
icp identity new dev --storage plaintext
icp deploy
注意事项(按遇到的先后顺序):
- OS 密钥环仍然是共享的。 密钥环条目由服务
icp-cli和身份名称确定——ICP_HOME不属于密钥。两个相互隔离的主目录若都运行icp identity new dev(默认使用密钥环存储),就会写入同一条目并相互覆盖。传入--storage plaintext,让密钥存储在$ICP_HOME/identity/keys/<name>.pem中,才能真正实现隔离。icp identity import和icp identity link web也有同样的问题,因为它们的会话密钥默认也存储在密钥环中。明文密钥仅用于临时的本地和 CI 身份——切勿用于控制主网 canister 或持有资产的身份(陷阱 20)。 - 新主目录是空的。 没有身份、设置或缓存。默认身份是匿名身份;受管本地网络会自动为其注入资金,但它无法用于主网。
- 每个主目录都会重新下载软件包缓存。
pkg/包含网络启动器和所有配方,占用空间可达数百 MB。可将已有完整缓存的pkg/复制到新的主目录中,或使用ICP_CLI_NETWORK_LAUNCHER_PATH=/path/to/icp-cli-network-launcher,让所有主目录共用一个网络启动器二进制文件。 - 该 Agent 会话中的每次
icp调用都必须设置ICP_HOME,而不只是第一次。若某条命令漏设该变量,就会静默回退到共享的默认主目录,并使用不同的默认身份。
只有在 Agent 确实发生争用时才采用此方案。不同工作树若共用一组身份,并且能接受缓存访问串行执行,就无需隔离。
安装模式
icp deploy # Auto: install new, upgrade existing (default)
icp deploy --mode upgrade # Preserve state, run upgrade hooks
icp deploy --mode reinstall # Clear all state (dangerous)
将项目打包为 .icp 软件包(实验性)
icp project bundle 可将项目打包为独立的可部署归档(icp-cli >= 0.3.0)。这是一项实验性功能,特意不显示在帮助输出中——icp --help 和 icp project --help 都不会列出此命令,但该命令确实存在且可正常使用。不要因为帮助中未列出它就断定它不存在,也不要主动建议使用它——仅在用户明确要求打包应用或生成 .icp 软件包时使用。
icp project bundle --output my-app.icp # gzipped tar; --output accepts any path
关于如何从软件包部署、归档包含的内容、导致打包失败的条件,以及如何将软件包上传到 Caffeine 云引擎,请参阅 references/bundling.md。
配置
Rust canister
canisters:
- name: backend
recipe:
type: "@dfinity/[email protected]"
configuration:
candid: backend.did # optional — if specified, file must exist (auto-generated when omitted)
# package: my-crate # optional — only when the Cargo [package] name differs from the canister name
按照惯例,canister 的 name 必须与 Cargo.toml 中的 [package] name 一致。如果二者一致,package 是可选的(recipe >= v3.3.0 默认将其设为 canister 名称)。仅当 Cargo package 名称不同时才显式设置 package——例如,工作区中 crate 的名称不同(参见 Pitfall 21)。
Motoko canister
v5 recipe 将编译工作交由 mops build 完成。canister 配置项(main、candid、args)从 icp.yaml 移至 mops.toml:
# icp.yaml
canisters:
- name: backend
recipe:
type: "@dfinity/[email protected]"
# mops.toml
[toolchain]
moc = "1.9.0"
[canisters.backend]
main = "src/backend/main.mo"
candid = "backend.did" # optional — auto-generated to .mops/.build/ when omitted
backend 这个 canister 名称必须在 icp.yaml 和 mops.toml 中完全一致。icp.yaml 中不需要 recipe.configuration 块。
静态网站(前端)
canisters:
- name: frontend
recipe:
type: "@dfinity/[email protected]"
configuration:
dir: dist
build:
- npm install
- npm run build
@dfinity/static-site(认证资产 canister)是推荐的前端 recipe。旧版 @dfinity/[email protected] recipe 仍受现有项目支持——两者的用法及迁移方法请参阅 static-site 技能。
对于多 canister 项目,请在同一个 canisters 数组中列出所有 canister。icp-cli 会并行构建它们。没有 dependencies 字段——请使用 Canister 环境变量进行 canister 间通信。
自定义构建步骤(不使用 recipe)
不使用 recipe 时,canister 级别仅允许 name、build、sync、settings 和 init_args 字段。没有 wasm、candid 或 metadata 字段——请改为在构建脚本中处理这些内容:
- WASM 输出:将最终的 WASM 复制到
$ICP_WASM_OUTPUT_PATH - Candid 元数据:使用
ic-wasm嵌入candid:service元数据 - Candid 文件:
.did文件仅在ic-wasm命令中引用,不作为 YAML 字段使用
canisters:
- name: backend
build:
steps:
- type: script
commands:
- cargo build --target wasm32-unknown-unknown --release
- cp target/wasm32-unknown-unknown/release/backend.wasm "$ICP_WASM_OUTPUT_PATH"
- ic-wasm "$ICP_WASM_OUTPUT_PATH" -o "$ICP_WASM_OUTPUT_PATH" metadata candid:service -f backend/backend.did -v public --keep-name-section
可用的 recipe
| Recipe | 类型字符串 | 必需配置 | 可选配置 |
|---|---|---|---|
| Rust | @dfinity/[email protected] | — | package(默认为 canister 名称)、candid、locked、shrink、compress、metadata |
| Motoko | @dfinity/[email protected] | — | shrink、compress、metadata |
| 静态网站(前端) | @dfinity/[email protected] | dir | build、presync、metadata |
| Asset(旧版前端) | @dfinity/[email protected] | dir | build、version |
| 预构建 | @dfinity/[email protected] | path | sha256、shrink、compress、metadata |
@dfinity/static-site 是托管前端的推荐方式;@dfinity/asset-canister 是旧版资产 canister(参见 static-site 技能)。
请在 dfinity/icp-cli-recipes releases 查看最新的 recipe 版本。使用 icp project show 查看 recipe 展开后的有效配置。
Canister 环境变量
在执行 icp deploy 时,icp-cli 会将环境中每个 canister 的 ID注入每个 canister 的设置,变量名为 PUBLIC_CANISTER_ID:<canister-name>(主体的文本形式)。这些是 canister 设置环境变量,可供 canister 代码在运行时读取;每次部署都会刷新各环境的变量值,因此无需任何配置,同一份代码即可在本地、预发布环境和主网上正确运行。
请使用这些变量连接 canister 间的调用,不要使用 setter 方法、初始化参数或部署后脚本(陷阱 22)。在调用时再读取这些变量:Motoko:Runtime.envVar<system>(...)(motoko-core v2.1.0+);Rust:ic_cdk::api::env_var_value(...)。代码及首次安装/重新安装时的行为,请参阅 references/canister-env-vars.md。
前端(JavaScript):前端 canister(静态站点或旧版 asset canister)会通过 ic_env cookie 暴露这些变量,并在所有 HTML 响应中设置该 cookie。使用 @icp-sdk/core 读取:
import { safeGetCanisterEnv } from "@icp-sdk/core/agent/canister-env";
const backendId = safeGetCanisterEnv()?.["PUBLIC_CANISTER_ID:backend"];
注意事项:
- 变量仅在部署时更新。添加 canister 后,请运行
icp deploy(不指定 canister 名称),确保每个 canister 都获取完整的 ID 集合。 - 用户定义的变量(
icp.yaml中的settings.environment_variables)是跨环境共享的静态 YAML 字符串:执行 create-class 安装(首次安装或使用--mode reinstall)时会重新应用这些变量,覆盖通过icp canister settings update --add-environment-variable设置的值。执行此类部署后,请重新设置特定环境的值(例如生产环境的来源)。 - replica 将环境变量的值限制为最多 128 个字符。
完整参考:icp-cli 环境变量。
Web 身份流程
有两个专门的技能涵盖 icp identity link web。请根据任务加载合适的技能——两者都会说明 stdin 中的“Press Enter”阻塞、浏览器登录步骤及各自标志的注意事项:
deploy-to-cloud-engine— 使用--auth <console-origin>将 CLI 连接到云引擎控制台,然后部署到该引擎的子网agent-web-identity— 使用--app <domain>获取特定于应用的主体的委托,然后以用户身份调用 canister
为主网提供身份资金
新身份既没有 ICP,也没有 cycles。此时运行 icp deploy -e ic 会失败,并提示 Insufficient cycles. Requested: <n> cycles, available balance: 0 cycles.(或 cycles 账本返回 insufficient liquid cycles balance):该部署身份在主网上的 cycles 账本余额为空。CLI 不会事先估算或发出警告,因此请在部署前自行处理,而不是等失败后再处理:
- 统计尚待创建的 canister 数量。
icp deploy会创建当前环境中所有在.icp/data/mappings/<env>.ids.json里没有 ID 的 canister(这是一个name: id映射;首次部署前该文件不存在,因此届时所有 canister 都会创建)。运行icp deploy --no-create -e ic可在不访问网络的情况下列出这些 canister:它会先构建,然后因提示the following canisters do not exist: ...而停止。 - 估算所需资金。 每次创建都会从身份余额中扣除
--cycles指定的数量(默认 2T;后缀为k/m/b/t),以及 cycles 账本收取的 100M 手续费,因此 N 个 canister 需要N × (2T + 100M)。网络创建费(13 节点子网上为 500B)和安装费从每个 canister 的 2T 中扣除,而非从身份余额中扣除;请再加上约 10% 的余量,以便之后可以充值。 - 对比将要执行部署的身份的余额:
icp cycles balance -n ic --identity "$ID"(陷阱 20;-q会输出不带其他内容的数字)。 - 如果余额不足,不要猜测:告诉用户差额,并提供两种补足方式。
- 持有 ICP(通过
icp token balance -n ic确认余额充足):运行icp cycles mint --cycles <gap> -n ic,只兑换所需差额(参见cycles-management)。 - 有银行卡但没有 ICP:运行
icp cycles buy --cycles <gap> -n ic --identity "$ID" --no-open --yes --json,以最低金额购买足以补足差额的 cycles;加载buy-cycles技能,了解操作流程、链接交接方式和订单状态。由用户付款;Agent 等待icp cycles balance -n ic达到估算值。
- 持有 ICP(通过
- 使用同一身份进行部署:
icp deploy -e ic --identity "$ID"。
补充参考资料
完整的 CLI 和配置架构请参阅 icp-cli 文档索引。
特定主题的详细指南请按需参阅以下参考文件:
references/binding-generation.md— 使用@icp-sdk/bindgen生成 TypeScript 绑定(Vite 插件、CLI、actor 设置)references/bundling.md— 完整介绍icp project bundle:归档内容、导致打包失败的四种情况,以及如何将.icp包上传至 Caffeine 云引擎references/canister-env-vars.md— 在 canister 代码运行时读取PUBLIC_CANISTER_ID:<name>(Motoko/Rust 示例、延迟读取模式)references/dev-server.md— 配置 Vite 开发服务器,以便在本地模拟ic_envcookie。重要:请用command === "serve"条件包裹getDevServerConfig(),确保它只在vite dev期间运行,而不是在vite build期间运行。references/dfx-migration.md— 完整的 dfx → icp 迁移指南(命令映射、配置映射、身份/canister ID 迁移、前端包迁移、迁移后验证清单)