feat: declare canister dependencies from another icp project#641
Draft
lwshang wants to merge 9 commits into
Draft
feat: declare canister dependencies from another icp project#641lwshang wants to merge 9 commits into
lwshang wants to merge 9 commits into
Conversation
Introduce a top-level `dependencies:` block in icp.yaml for declaring another icp project this project depends on. Each entry has a local alias (`name`), a `path` to the dependency's project dir, and an optional `canisters` exposure subset (omit = expose all). This commit adds only the manifest type + schema; consolidation and deploy behavior follow in later commits. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ject Extend consolidate_manifest to import declared dependency projects: - Recursively load each dependency's icp.yaml and import ALL of its canisters (deploy-all), keyed by their app-root-relative path (e.g. `umbrella/openemail:backend`). The `canisters` field is an exposure filter for env vars only, not a deployment filter. - De-duplicate instances by canonicalized directory, so a diamond dependency (e.g. two sibling submodules both depending on `../openemail`) deploys once. - Detect dependency cycles. - Compute per-project `PUBLIC_CANISTER_ID` bindings on each Canister so a dependency's canisters keep their standalone view (own siblings by bare name) while the parent sees only the exposed dependency canisters under `<alias>:`. - Reserve `:` in canister names and dependency aliases; validate alias uniqueness and alias/canister collisions. - Translate dependency canisters' name-based controller refs to store keys. The canonical `Canister` gains a `bindings` map consumed by the deploy step (next commit). For a project with no dependencies, bindings map every canister to itself, preserving today's flat env-var behavior. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…store namespaced-name safe - set_binding_env_vars_many now derives each canister's PUBLIC_CANISTER_ID env vars from its own `bindings` map instead of a single flat set, so a dependency's canisters keep the view their project expects and the parent sees only the exposed dependency canisters. Bindings are filtered to ids present in the environment; a dependency-free project is unaffected (every canister still sees every sibling). - The artifact store percent-encodes `/`, `\`, `:`, `%` in canister names so namespaced dependency canister keys (e.g. `vendor/openemail:backend`) are valid filenames on all platforms. Plain names are unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A dependency canister's namespaced name (e.g. `dep:backend`, `vendor/dep:backend`) is not a valid DNS label, so it cannot get a friendly `<name>.<env>.<domain>` subdomain. Skip the friendly host for such names and use the principal host, avoiding a malformed/incorrect URL when printing deployed canister URLs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Add examples/icp-project-dependency demonstrating an app that vendors an `openemail` dependency and exposes its `backend` canister. - Add an integration test that deploys an app + dependency to a managed network and asserts the app sees PUBLIC_CANISTER_ID:openemail:backend while the dependency's canisters keep their standalone view. - Document dependency projects in the canister-discovery guide and CHANGELOG. - Move the store_artifact test module to the end of the file (clippy). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ndencies Cover the layout where two independent sub-projects (service-a, service-b) each depend on the same sibling `openemail` via `../openemail`, and an app depends on both services. - Add examples/icp-project-dependency-shared demonstrating the umbrella layout. - Add an integration test that deploys the app + both services + openemail to a managed network and asserts openemail is deployed once and both services' `PUBLIC_CANISTER_ID:openemail:backend` resolve to the same shared canister id. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
Summary
Adds a top-level
dependencies:block toicp.yamlso a project can depend on anothericpproject vendored into it (typically as a git submodule).icp deploythen deploys the dependency's canisters into the same environment and wires their IDs into your canisters asPUBLIC_CANISTER_ID:<alias>:<canister>environment variables.Running
icp deployinapp/builds/creates/installs the app's canisters and the dependency's, then injects e.g.PUBLIC_CANISTER_ID:openemail:backendinto the app's canisters. The dependency'sicp.yamlstays self-contained and can still be deployed on its own.Behavior
canisters:only filters which dependency IDs are exposed to the parent.service-a,service-b, andopenemail, where both services depend on../openemail— the two edges resolve to the same directory, soopenemailis deployed once and shared by both.vendor/openemail:backend). Aliases drive only env-var names. Cycles are detected.Implementation
Commit-by-commit:
feat(manifest)— thedependencies:manifest type + JSON schema.feat(project)— recursive consolidation: import-all, path-based store keys, diamond de-dup by canonical directory, cycle detection, per-projectPUBLIC_CANISTER_IDbindings,:name/alias validation, and translation of dependency canisters' name-based controller refs.feat(deploy)— inject env vars from each canister's per-project bindings; percent-encode namespaced names in the artifact store so they are valid filenames.fix(url)— namespaced canister names fall back to the principal host when printing URLs (not valid DNS labels).docs(deps)—examples/icp-project-dependency, an integration test, canister-discovery docs, CHANGELOG.test(deps)—examples/icp-project-dependency-shared(umbrella layout) + a shared-dependency integration test.Testing
openemail:backendto the same shared canister).canister_settings,deploy,bundle,build, andprojectsuites;fmt/clippyclean.Not in scope (design leaves room for these)
Pinned/already-deployed dependencies (an additive per-network
pin:), candid/binding generation (each canister sources its own bindings), and per-canisterdepends_on.🤖 Generated with Claude Code