Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
7ced866
feat: add Codespaces support to who_am_i examples (pilot)
marc0olo May 21, 2026
3f0a8fb
fix: add postAttachCommand to open README with Runme buttons on attach
marc0olo May 21, 2026
10d68bd
docs: improve Codespace resume guidance in both who_am_i READMEs
marc0olo May 21, 2026
ac9b17f
fix: use code -r to scope VS Code window to example subdirectory
marc0olo May 21, 2026
f27b777
fix: move Codespaces devcontainers to repo root .devcontainer/
marc0olo May 21, 2026
0bb4e03
fix: replace code -r with code README.md, suppress noisy notifications
marc0olo May 21, 2026
ecef03c
chore: remove per-example .devcontainer directories
marc0olo May 21, 2026
6256a86
fix: Codespaces DX improvements for who_am_i examples
marc0olo May 21, 2026
871cfe5
fix: route agent to port 8000 in Codespaces, use ?canisterId= URLs
marc0olo May 21, 2026
56c9881
feat: auto-deploy and open frontend URL on Codespace attach
marc0olo May 21, 2026
e3b5f6b
chore: improve postAttach transparency with status messages
marc0olo May 21, 2026
f237502
docs: re-add redeploy cell to Codespace actions (preserves state)
marc0olo May 21, 2026
b25182f
fix: suppress port 7863 notification (PocketIC internal port)
marc0olo May 21, 2026
f750b04
fix: suppress port 7864 notification (Runme extension process)
marc0olo May 21, 2026
3ca4221
fix: remove code --open-url (browser blocks programmatic tabs)
marc0olo May 21, 2026
abccf6b
fix: root key fetch and broken-pipe in Codespaces
marc0olo May 21, 2026
986af95
fix: use echo to pipe JSON to jq (printf omits trailing newline)
marc0olo May 21, 2026
f2306c7
revert: remove fetchRootKey logic from actor.js
marc0olo May 21, 2026
ebd5ee1
fix: add *.app.github.dev to gateway domains for Codespaces support
marc0olo May 21, 2026
1f73c78
revert: remove Codespaces host workarounds from actor.js and vite.con…
marc0olo May 21, 2026
128441d
fix: inject Codespaces domain into icp.yaml via postStart.sh
marc0olo May 21, 2026
e7a47f7
chore: centralize devcontainer scripts and add generic CODESPACE.md
marc0olo May 21, 2026
50b20cd
refactor: extract URL display to show-urls.sh
marc0olo May 21, 2026
37e2ef6
docs: clean up Codespace section in who_am_i READMEs
marc0olo May 22, 2026
6b2f799
chore: remove Runme, add startup editor suppression, document URL lim…
marc0olo May 22, 2026
779dbfa
chore: improve Codespace UX for who_am_i examples
marc0olo May 22, 2026
d4ab1f9
chore: update port labels to ICP and Vite
marc0olo May 22, 2026
e8cc740
feat: add root devcontainer for local multi-example development
marc0olo May 22, 2026
57e1fb2
fix: use fully qualified rust-lang.rust-analyzer extension ID
marc0olo May 22, 2026
3da3378
docs: update README with icp-cli, Codespaces, and Dev Containers
marc0olo May 22, 2026
f7e3542
docs: remove ICP Ninja section and fix security best practices URL
marc0olo May 22, 2026
638669e
chore: remove NINJA_CONTRIBUTING.md
marc0olo May 22, 2026
89eff98
docs: refurbish ADDING_AN_EXAMPLE.md
marc0olo May 22, 2026
cd8c55e
docs: remove outdated portal submodule documentation section
marc0olo May 22, 2026
d9680c1
docs: remove ICP Ninja reference and update example submission guidance
marc0olo May 22, 2026
cc1b1ca
feat: add Codespaces support to hello_world examples
marc0olo May 22, 2026
04a43dc
feat: add Makefiles with tests and CI workflows for hello_world and w…
marc0olo May 22, 2026
1c50536
docs: add workflow template, clarify npm run dev, update contributing…
marc0olo May 22, 2026
f21fbb4
fix: handle icp.yaml without a network section in postStart.sh
marc0olo May 22, 2026
a53a79b
fix: use correct networks.local structure when appending to icp.yaml
marc0olo May 22, 2026
adde114
fix: networks is a sequence not a map in icp.yaml
marc0olo May 22, 2026
5f21a7e
fix: add Vite devserver domain to gateway domains in postStart.sh
marc0olo May 22, 2026
af48b75
fix: ensure network is running in postAttach.sh
marc0olo May 22, 2026
58533d0
fix: wait for ICP network to be ready before deploying
marc0olo May 22, 2026
608944c
fix: remove redundant network start and polling from postAttach.sh
marc0olo May 22, 2026
6db9783
fix: wait for network readiness in postStart.sh before returning
marc0olo May 22, 2026
f71ef43
fix: add set -e to lifecycle scripts and remove unnecessary polling
marc0olo May 22, 2026
9a8f53d
refactor: rename canisters to backend/frontend in who_am_i examples
marc0olo May 22, 2026
b907833
chore: bump moc/core, use --default-persistent-actors, pin recipe fix…
marc0olo May 22, 2026
985dacd
docs: add AGENTS.md and CLAUDE.md with example structure guidelines
marc0olo May 22, 2026
5d98d08
docs: instruct agents to fetch ICP skills before working
marc0olo May 22, 2026
9b30706
fix: add paths filter to ninja_pr_checks workflow
marc0olo May 22, 2026
cc1cdd4
fix: pin Motoko recipe to immutable commit SHA
marc0olo May 26, 2026
d19e1c6
refactor: adopt new Motoko recipe format (v5 schema)
marc0olo May 26, 2026
f3f3bb4
fix: remove deleted examples from ninja_pr_checks paths filter
marc0olo May 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions .devcontainer/CODESPACE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# ICP Codespace

> **Setup in progress:** The ICP local network is starting and canisters are being deployed automatically. Check the terminal panel for status. Access URLs will be printed once deployment is complete.

The network starts and canisters are deployed automatically when this Codespace opens. Access URLs are printed in the terminal once deployment completes.

## Access URLs

`icp deploy` prints URLs using `localhost:8000`, which do not work inside a Codespace. Run the following command to get the correct forwarded URLs:

```bash
bash /workspaces/examples/.devcontainer/scripts/show-urls.sh
```

## Deploy / Redeploy

Deploys or redeploys all canisters, preserving their state. This also runs automatically every time the Codespace starts.

```bash
icp deploy
```

## Reset & Redeploy

Reinstalls all canisters from scratch, wiping their state. The network keeps running.

```bash
icp deploy --mode reinstall -y
```

## Note for non-SPA frontends

Frontend URLs use a `?canisterId=` query parameter for routing. This works correctly for single-page apps (all navigation stays client-side). If your frontend uses real path-based navigation where clicking a link triggers a new browser request (e.g. navigating to `/page2`), the query parameter will be dropped and the gateway will not know which canister to serve. Subdomain-based routing is not available in Codespaces because GitHub's TLS certificate only covers one subdomain level, making `<canisterId>.<codespace>-8000.app.github.dev` invalid.
31 changes: 31 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"name": "ICP Examples (Motoko + Rust)",
"image": "ghcr.io/marc0olo/icp-dev-env-all:dev",
"workspaceFolder": "/workspaces/examples",
"forwardPorts": [8000, 5173],
"portsAttributes": {
"8000": {
"label": "ICP",
"onAutoForward": "ignore"
},
"5173": {
"label": "Vite",
"onAutoForward": "openBrowser"
}
},
"customizations": {
"vscode": {
"extensions": [
"dfinity-foundation.vscode-motoko",
"rust-lang.rust-analyzer"
],
"settings": {
"git.openRepositoryInParentFolders": "always",
"workbench.startupEditor": "none",
"workbench.editorAssociations": {
"*.md": "vscode.markdown.preview.editor"
}
}
}
}
}
32 changes: 32 additions & 0 deletions .devcontainer/motoko-hello-world/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"name": "Hello World (Motoko)",
"image": "ghcr.io/marc0olo/icp-dev-env-motoko:dev",
"workspaceFolder": "/workspaces/examples/motoko/hello_world",
"forwardPorts": [8000, 5173],
"portsAttributes": {
"8000": {
"label": "ICP",
"onAutoForward": "ignore"
},
"5173": {
"label": "Vite",
"onAutoForward": "openBrowser"
}
},
"postStartCommand": "bash /workspaces/examples/.devcontainer/scripts/postStart.sh",
"postAttachCommand": "bash /workspaces/examples/.devcontainer/scripts/postAttach.sh",
"customizations": {
"vscode": {
"extensions": [
"dfinity-foundation.vscode-motoko"
],
"settings": {
"git.openRepositoryInParentFolders": "always",
"workbench.startupEditor": "none",
"workbench.editorAssociations": {
"*.md": "vscode.markdown.preview.editor"
}
}
}
}
}
32 changes: 32 additions & 0 deletions .devcontainer/motoko-who-am-i/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"name": "Who Am I? (Motoko)",
"image": "ghcr.io/marc0olo/icp-dev-env-motoko:dev",
"workspaceFolder": "/workspaces/examples/motoko/who_am_i",
"forwardPorts": [8000, 5173],
"portsAttributes": {
"8000": {
"label": "ICP",
"onAutoForward": "ignore"
},
"5173": {
"label": "Vite",
"onAutoForward": "openBrowser"
}
},
"postStartCommand": "bash /workspaces/examples/.devcontainer/scripts/postStart.sh",
"postAttachCommand": "bash /workspaces/examples/.devcontainer/scripts/postAttach.sh",
"customizations": {
"vscode": {
"extensions": [
"dfinity-foundation.vscode-motoko"
],
"settings": {
"git.openRepositoryInParentFolders": "always",
"workbench.startupEditor": "none",
"workbench.editorAssociations": {
"*.md": "vscode.markdown.preview.editor"
}
}
}
}
}
32 changes: 32 additions & 0 deletions .devcontainer/rust-hello-world/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"name": "Hello World (Rust)",
"image": "ghcr.io/marc0olo/icp-dev-env-rust:dev",
"workspaceFolder": "/workspaces/examples/rust/hello_world",
"forwardPorts": [8000, 5173],
"portsAttributes": {
"8000": {
"label": "ICP",
"onAutoForward": "ignore"
},
"5173": {
"label": "Vite",
"onAutoForward": "openBrowser"
}
},
"postStartCommand": "bash /workspaces/examples/.devcontainer/scripts/postStart.sh",
"postAttachCommand": "bash /workspaces/examples/.devcontainer/scripts/postAttach.sh",
"customizations": {
"vscode": {
"extensions": [
"rust-lang.rust-analyzer"
],
"settings": {
"git.openRepositoryInParentFolders": "always",
"workbench.startupEditor": "none",
"workbench.editorAssociations": {
"*.md": "vscode.markdown.preview.editor"
}
}
}
}
}
32 changes: 32 additions & 0 deletions .devcontainer/rust-who-am-i/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"name": "Who Am I? (Rust)",
"image": "ghcr.io/marc0olo/icp-dev-env-rust:dev",
"workspaceFolder": "/workspaces/examples/rust/who_am_i",
"forwardPorts": [8000, 5173],
"portsAttributes": {
"8000": {
"label": "ICP",
"onAutoForward": "ignore"
},
"5173": {
"label": "Vite",
"onAutoForward": "openBrowser"
}
},
"postStartCommand": "bash /workspaces/examples/.devcontainer/scripts/postStart.sh",
"postAttachCommand": "bash /workspaces/examples/.devcontainer/scripts/postAttach.sh",
"customizations": {
"vscode": {
"extensions": [
"rust-lang.rust-analyzer"
],
"settings": {
"git.openRepositoryInParentFolders": "always",
"workbench.startupEditor": "none",
"workbench.editorAssociations": {
"*.md": "vscode.markdown.preview.editor"
}
}
}
}
}
11 changes: 11 additions & 0 deletions .devcontainer/scripts/postAttach.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/bin/bash
set -e

code CODESPACE.md

echo "Deploying canisters..."
icp deploy
echo ""
echo "Access URLs:"
echo ""
bash /workspaces/examples/.devcontainer/scripts/show-urls.sh
27 changes: 27 additions & 0 deletions .devcontainer/scripts/postStart.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#!/bin/bash
set -e

# In GitHub Codespaces, inject the forwarded domain into icp.yaml so the
# HTTP gateway accepts requests with that Host header.
if [ -n "$CODESPACE_NAME" ]; then
DOMAIN="${CODESPACE_NAME}-8000.app.github.dev"
DEV_DOMAIN="${CODESPACE_NAME}-5173.app.github.dev"
node -e "
const fs = require('fs');
const content = fs.readFileSync('icp.yaml', 'utf8');
if (!content.includes('gateway:')) {
const gateway = ' gateway:\n domains:\n - localhost\n - ${DOMAIN}\n - ${DEV_DOMAIN}\n';
let updated;
if (content.includes(' ii: true\n')) {
updated = content.replace(' ii: true\n', ' ii: true\n' + gateway);
} else if (content.includes(' mode: managed\n')) {
updated = content.replace(' mode: managed\n', ' mode: managed\n' + gateway);
} else {
updated = content.trimEnd() + '\nnetworks:\n - name: local\n mode: managed\n' + gateway;
}
fs.writeFileSync('icp.yaml', updated);
}
"
fi

icp network start -d
47 changes: 47 additions & 0 deletions .devcontainer/scripts/show-urls.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#!/bin/bash

CANISTER_JSON=$(icp canister status --json 2>/dev/null)
NETWORK_JSON=$(icp network status --json 2>/dev/null)
CANDID_UI_ID=$(echo "$NETWORK_JSON" | jq -r '.candid_ui_principal // ""')

if [ -n "$CODESPACE_NAME" ]; then
BASE="https://${CODESPACE_NAME}-8000.app.github.dev"
else
BASE="http://localhost:8000"
fi

FRONTEND_NAMES=$(icp project show 2>/dev/null | node -e '
let data = "";
process.stdin.on("data", c => data += c);
process.stdin.on("end", () => {
const lines = data.split("\n");
const frontends = [];
let inCanisters = false, currentCanister = null;
for (const line of lines) {
if (/^\S/.test(line)) {
inCanisters = (line === "canisters:");
currentCanister = null;
continue;
}
if (!inCanisters) continue;
const m = line.match(/^ ([a-z][a-z0-9_-]*):\s*$/);
if (m) { currentCanister = m[1]; continue; }
if (currentCanister && !frontends.includes(currentCanister)) {
if (line.includes("asset-canister") || /^\s+type:\s+assets\s*$/.test(line)) {
frontends.push(currentCanister);
}
}
}
process.stdout.write(frontends.join("\n") + (frontends.length ? "\n" : ""));
});
')

echo "$CANISTER_JSON" | while IFS= read -r entry; do
ID=$(echo "$entry" | jq -r '.id')
NAME=$(echo "$entry" | jq -r '.name')
if echo "$FRONTEND_NAMES" | grep -qx "$NAME"; then
echo " $NAME: ${BASE}/?canisterId=${ID}"
elif [ -n "$CANDID_UI_ID" ]; then
echo " $NAME (Candid UI): ${BASE}/?canisterId=${CANDID_UI_ID}&id=${ID}"
fi
done
4 changes: 1 addition & 3 deletions .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# Contributing

> For ICP Ninja: check [NINJA_CONTRIBUTING.md](./../NINJA_CONTRIBUTING.md) for how to contribute a project to ICP Ninja.

Thank you for your interest in contributing to example apps for the Internet Computer.
By participating in this project, you agree to abide by our [Code of Conduct](./CODE_OF_CONDUCT.md).

Expand Down Expand Up @@ -116,4 +114,4 @@ To open a new issue:

### Submitting your own example

We're not accepting community examples at this time -- we have something better planned.
See [ADDING_AN_EXAMPLE.md](./../ADDING_AN_EXAMPLE.md) for guidelines on how to structure and submit a new example.
35 changes: 35 additions & 0 deletions .github/workflow-template.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Workflow template for ICP examples.
# Copy this file to .github/workflows/<example_name>.yml and replace the placeholders.
#
# PLACEHOLDERS:
# <example_name> e.g. hello_world
# <language> motoko | rust
# <image> ghcr.io/dfinity/icp-dev-env-motoko | ghcr.io/dfinity/icp-dev-env-rust

name: <example_name>

on:
push:
branches:
- master
pull_request:
paths:
- <language>/<example_name>/**
- .github/workflows/<example_name>.yml

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
<language>-<example_name>:
runs-on: ubuntu-24.04
container: <image>
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
- name: Deploy and test
working-directory: <language>/<example_name>
run: |
icp network start -d
icp deploy
make test
40 changes: 40 additions & 0 deletions .github/workflows/hello_world.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: hello_world

on:
push:
branches:
- master
pull_request:
paths:
- motoko/hello_world/**
- rust/hello_world/**
- .github/workflows/hello_world.yml

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
motoko-hello_world:
runs-on: ubuntu-24.04
container: ghcr.io/marc0olo/icp-dev-env-motoko:dev
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
- name: Deploy and test
working-directory: motoko/hello_world
run: |
icp network start -d
icp deploy
make test

rust-hello_world:
runs-on: ubuntu-24.04
container: ghcr.io/marc0olo/icp-dev-env-rust:dev
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
- name: Deploy and test
working-directory: rust/hello_world
run: |
icp network start -d
icp deploy
make test
Loading