Skip to content

chore: migrate rust/image-classification to icp-cli#1419

Open
marc0olo wants to merge 17 commits into
masterfrom
chore/migrate-rust-image-classification-to-icp-cli
Open

chore: migrate rust/image-classification to icp-cli#1419
marc0olo wants to merge 17 commits into
masterfrom
chore/migrate-rust-image-classification-to-icp-cli

Conversation

@marc0olo

@marc0olo marc0olo commented Jun 16, 2026

Copy link
Copy Markdown
Member

Summary

  • Replace dfx.json with icp.yaml; custom build steps: cargo build --target=wasm32-wasip1wasi2icwasm-opt (auto-installed if missing) → ic-wasm metadata embed
  • Restructure source: src/backend/backend/, src/frontend/frontend/; add workspace Cargo.toml
  • Upgrade ic-cdk 0.140.20, ic-stable-structures 0.60.7, ic-wasi-polyfill 0.4.10.13; update edition to "2024"
  • Rename wasm32-wasiwasm32-wasip1 throughout (target removed in Rust stable post-1.78); update .cargo/config.toml, rust-toolchain.toml, icp.yaml, CI, README
  • Remove channel = "1.79" pin from rust-toolchain.toml (blocked builds with modern stable)
  • Fix download_model.sh: correct path to backend/assets/ (was src/backend/assets/); force IPv4 to avoid hanging IPv6 connections to media.githubusercontent.com
  • Fix classify(): was #[ic_cdk::query] but backend.did and README describe it as an update; changed to #[ic_cdk::update] (replicated execution)
  • Migrate frontend from webpack + dfx declarations to Vite + @icp-sdk/bindgen; update actor.js and vite.config.js to canonical icp-cli pattern (no dfx fallbacks); bump @icp-sdk/core 5.0→5.4, @icp-sdk/bindgen 0.2.2→0.4.0, vite 5.4.11→8.1.3
  • Frontend UX: add visible upload hint; show full image at natural aspect ratio (remove forced 300×300 crop); re-show "click to classify another" hint after results
  • Replace Makefile with test.sh; replace run.sh with test.sh
  • Add CI workflow image-classification.yml (icp-dev-env-rust:1.0.1, installs build-essential for wasi2ic, downloads model); remove legacy workflow
  • Delete dfx.json, build.sh, webpack.config.js, Makefile, old src/ tree, old declarations, legacy CI

Test plan

  • ./download_model.sh && icp network start -d && icp deploy && bash test.sh passes — run() classifies man_on_ferrari_1975.png and returns tractor
  • Frontend accessible at the URL printed by icp deploy; image upload and classification work
  • CI workflow image-classification passes on push

🤖 Generated with Claude Code

@marc0olo marc0olo force-pushed the chore/migrate-rust-image-classification-to-icp-cli branch from 919846a to 6209214 Compare June 17, 2026 17:02
marc0olo and others added 2 commits June 19, 2026 08:32
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…e to 1.0.1

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@marc0olo marc0olo force-pushed the chore/migrate-rust-image-classification-to-icp-cli branch from 6209214 to 3a5e1ad Compare June 19, 2026 06:34
marc0olo and others added 13 commits July 3, 2026 13:19
… clean up icp.yaml

- Fix download_model.sh: target path was still src/backend/assets/ after source
  restructure to backend/; the build fails because include_bytes! can't find the model
- Remove redundant network definition from icp.yaml (local managed is the default)
- Update ic-cdk 0.14 → 0.20, ic-stable-structures 0.6 → 0.7,
  ic-wasi-polyfill 0.4.1 → 0.13 (0.13 is built for ic-cdk 0.20)
- Update edition to 2024; add cdylib comment to Cargo.toml
- README: add Node.js v18+, Rust v1.85+ prerequisites

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…-toolchain.toml

Cargo 1.79 can't parse edition = "2024" in dependency manifests (stabilized in 1.85).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
wasm32-wasi was deprecated in Rust 1.78 and removed in later stable releases.
Updated all references in .cargo/config.toml, rust-toolchain.toml, icp.yaml,
CI workflow, and README.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… convention (PR 1426)

- vite.config.js: use icp network status --json for proxyTarget and rootKey,
  proper error message when backend not deployed, remove dfx loadEnv/define fallback
- actor.js: remove dfx CANISTER_ID_BACKEND fallback, resolve canister ID from
  ic_env cookie only
- package.json: bump @icp-sdk/core 5.0→5.4, @icp-sdk/bindgen 0.2.2→0.4.0,
  vite 5.4.11→8.1.3

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
media.githubusercontent.com IPv6 connections can hang; --inet4-only / -4
ensures the download completes reliably.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… icp.yaml build

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…DME prerequisites

- Fix classify(): was #[ic_cdk::query] but backend.did and README both describe
  it as an update (replicated execution); change to #[ic_cdk::update] to match
- README: remove wasm-opt (auto-installed by icp.yaml), add wasi2ic install
  command, move candid-extractor to the section where it's actually needed,
  clarify classify vs classify_query distinction

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add dashed border + subtle background to placeholder image to signal interactivity
- Add "Click to upload" hint text below the image, hidden once a file is selected

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ion with updated text

After results appear, show "↑ Click image to classify another" so the next action is obvious.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… ratio

- Remove forced 300x300 square and object-fit:cover from .image (was cropping uploads)
- Keep square dimensions only for .image.placeholder (the logo before any upload)
- Remove hardcoded img.width=600; CSS max-width:100% handles sizing

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…c in CI

wasi2ic has a C++ dependency (link-cplusplus) that requires a C++ compiler;
the icp-dev-env-rust container ships without build-essential.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tial install

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… handle it

The binaryen GitHub release download is fragile (504 errors). icp.yaml already
has "which wasm-opt || cargo install wasm-opt" and build-essential is present
from the wasi2ic step, so wasm-opt installs automatically on first icp deploy.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR migrates the rust/image-classification example from dfx + webpack to the icp-cli project layout and workflow, updating the Rust canister toolchain/build pipeline (WASI → ICP WASM) and modernizing the frontend to Vite + @icp-sdk/bindgen.

Changes:

  • Replace dfx.json/Makefile/webpack setup with icp.yaml + test.sh and a new GitHub Actions workflow using icp-dev-env-rust.
  • Restructure the project into backend/ and frontend/, introduce a workspace Cargo.toml, and update Rust deps/toolchain/targets (incl. wasm32-wasip1).
  • Migrate frontend to Vite + bindgen actor wiring and update the UI assets/UX for image upload + classification results.

Reviewed changes

Copilot reviewed 27 out of 39 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
rust/image-classification/icp.yaml Adds icp-cli canister definitions and a custom backend build pipeline (WASI → wasi2ic → wasm-opt → ic-wasm metadata).
rust/image-classification/test.sh New icp canister call-based smoke test for run().
rust/image-classification/README.md Updates docs to icp-cli, new prerequisites, deploy/test steps, and candid update instructions.
rust/image-classification/package.json Converts root JS setup to a workspace wrapper for the Vite frontend.
rust/image-classification/Cargo.toml Updates workspace member path to backend/.
rust/image-classification/rust-toolchain.toml Updates targets to include wasm32-wasip1 and removes the old pin.
rust/image-classification/.cargo/config.toml Switches default target config from wasm32-wasi to wasm32-wasip1 and keeps SIMD rustflags.
rust/image-classification/.gitignore Removes dfx-era ignores and ignores new generated bindings + model location.
rust/image-classification/download_model.sh Fixes model download target path and forces IPv4 for wget/curl.
rust/image-classification/backend/Cargo.toml Upgrades edition/deps (incl. ic-cdk) and adjusts crate metadata.
rust/image-classification/backend/src/lib.rs Aligns classify() with the .did by changing it to an update call; keeps query variants and setup.
rust/image-classification/backend/src/onnx.rs Introduces model setup/inference code and ImageNet label table.
rust/image-classification/backend/backend.did Adds the committed Candid interface in the new location.
rust/image-classification/frontend/package.json Adds the Vite frontend package with bindgen + Vite deps/scripts.
rust/image-classification/frontend/vite.config.js New Vite config wiring bindgen + dev server cookie/proxy config for icp-cli.
rust/image-classification/frontend/src/actor.js New canonical actor wiring via ic_env cookie + safeGetCanisterEnv.
rust/image-classification/frontend/src/main.js Switches to new actor import and tweaks UX behavior (hint visibility, image sizing).
rust/image-classification/frontend/index.html Moves assets under assets/, adds module script entry, and adds upload hint UI.
rust/image-classification/frontend/assets/main.css Updates image sizing rules and adds placeholder/hint styling.
rust/image-classification/frontend/assets/.ic-assets.json5 Restores asset-canister headers/CSP configuration in the new frontend layout.
rust/image-classification/frontend/assets/logo_transparent.png Adds/moves the logo asset into the new frontend asset location.
rust/image-classification/frontend/assets/loader.svg Adds/moves loader asset into the new frontend asset location.
rust/image-classification/frontend/assets/favicon.ico Adds/moves favicon asset into the new frontend asset location.
.github/workflows/image-classification.yml Adds new CI workflow using icp-cli in the Rust dev-env container.
.github/workflows/rust-image-classification-example.yaml Removes legacy dfx-based CI workflow.
rust/image-classification/dfx.json Removes dfx project configuration.
rust/image-classification/build.sh Removes legacy build script (replaced by icp.yaml build steps).
rust/image-classification/Makefile Removes legacy make-based build/test flow (replaced by test.sh).
rust/image-classification/webpack.config.js Removes webpack configuration (frontend migrated to Vite).
rust/image-classification/src/frontend/src/.ic-assets.json5 Removes old frontend asset headers config under the legacy src/ tree.
rust/image-classification/src/declarations/backend/index.js Removes legacy dfx-generated declarations.
rust/image-classification/src/declarations/backend/index.d.ts Removes legacy dfx-generated declarations typings.
rust/image-classification/src/declarations/backend/backend.did.js Removes legacy dfx-generated Candid JS binding.
rust/image-classification/src/declarations/backend/backend.did.d.ts Removes legacy dfx-generated Candid TS binding.
rust/image-classification/src/declarations/backend/backend.did Removes legacy .did file under the old declarations tree.
Comments suppressed due to low confidence (2)

rust/image-classification/frontend/index.html:10

  • <base href="/" /> forces all relative URLs to resolve as absolute paths (e.g. /assets/...). This commonly breaks local canister gateway URLs (like .../?canisterId=...) because the query param is dropped on subsequent asset requests. Use a relative base instead.
    rust/image-classification/backend/src/lib.rs:41
  • ic_cdk::export_candid!(); is missing. This repo’s Rust canisters consistently include it so candid-extractor can generate backend.did (and your README’s “Updating the Candid interface” command will fail without the exported candid pointer).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread rust/image-classification/frontend/vite.config.js
Comment thread rust/image-classification/frontend/vite.config.js
marc0olo and others added 2 commits July 3, 2026 19:11
…-extractor

Without it the README's "Updating the Candid interface" command fails.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Note that export_candid!() is only needed for re-generation, not for deployment.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 27 out of 39 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (1)

rust/image-classification/backend/src/lib.rs:93

  • run() is a query method but it calls classify(), which is intended to be the update/replicated entrypoint. This is confusing (and can change semantics if classify later gains update-only behavior). Prefer calling the query variant (or a shared internal helper) from run().

@marc0olo

marc0olo commented Jul 3, 2026

Copy link
Copy Markdown
Member Author

Regarding the suppressed comment on run() calling classify(): this is intentional. #[ic_cdk::update] only affects how classify is exposed as a canister endpoint — calling it as a regular Rust function from within run() is valid and doesn't trigger update semantics. run() is a convenience query that exercises the same logic as classify() on a built-in test image, deliberately exposed as a query so the smoke test in test.sh is fast.

@marc0olo marc0olo marked this pull request as ready for review July 3, 2026 17:28
@marc0olo marc0olo requested review from a team as code owners July 3, 2026 17:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants