diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..a989c52 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,52 @@ +# Dev container image for VS Code / the devcontainer CLI. +# +# The release builder image (docker/Dockerfile) stays the single source of +# truth for the RISC-V cross toolchain, Go and MaixCDK. This layer only adds +# the frontend toolchain on top: the builder image deliberately ships without +# Node (`make web` runs on the host), but an IDE container should cover web +# development as well. +# +# BASE_IMAGE can be overridden to a locally built builder image, e.g. the +# output of `make builder-image` (nanokvm-builder-local--). +ARG BASE_IMAGE=ghcr.io/sipeed/nanokvm-builder:latest +FROM ${BASE_IMAGE} + +USER root + +# Node major and pnpm major track .github/workflows/package.yml ("Set up +# Node" / "Set up pnpm") and web/package.json "engines" -- keep all of them +# in sync when CI bumps either. +ARG NODE_MAJOR=22 +ARG PNPM_VERSION=11 +# Both can be overridden behind restrictive or mirrored corporate networks +# via devcontainer.json, e.g.: +# "build": {"args": { +# "NODE_DIST_MIRROR": "https://registry.npmmirror.com/-/binary/node", +# "NPM_REGISTRY": "https://registry.npmmirror.com" +# }} +ARG NODE_DIST_MIRROR=https://nodejs.org/dist +ARG NPM_REGISTRY=https://registry.npmjs.org + +# Install the latest Node of the pinned major from official dist tarballs; +# tracking the latest patch release is intentional (rebuild the container to +# pick up updates). The SHA-256 check guards download integrity -- it shares +# its origin with the tarball, so it is not an authenticity proof. +RUN set -eux; \ + apt-get update; \ + apt-get install -y ca-certificates curl xz-utils; \ + rm -rf /var/lib/apt/lists/*; \ + case "$(uname -m)" in \ + x86_64) node_arch=x64 ;; \ + aarch64) node_arch=arm64 ;; \ + *) echo "unsupported architecture: $(uname -m)" >&2; exit 1 ;; \ + esac; \ + curl -fsSL "${NODE_DIST_MIRROR}/latest-v${NODE_MAJOR}.x/SHASUMS256.txt" \ + -o /tmp/SHASUMS256.txt; \ + tarball="$(grep -o "node-v[0-9.]*-linux-${node_arch}\.tar\.xz" /tmp/SHASUMS256.txt | head -n1)"; \ + curl -fsSL "${NODE_DIST_MIRROR}/latest-v${NODE_MAJOR}.x/${tarball}" -o "/tmp/${tarball}"; \ + cd /tmp && grep " ${tarball}\$" SHASUMS256.txt | sha256sum -c -; \ + tar -xJf "/tmp/${tarball}" -C /usr/local --strip-components=1 --no-same-owner; \ + rm -f "/tmp/${tarball}" /tmp/SHASUMS256.txt; \ + node --version && npm --version +RUN npm install --global --registry "${NPM_REGISTRY}" "pnpm@${PNPM_VERSION}" \ + && pnpm --version diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..f64664e --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,49 @@ +// Dev container for NanoKVM: Go cross-compilation, MaixCDK C support layer +// and web frontend in a single container. See "Development" in README.md. +{ + "name": "NanoKVM", + "build": { + "dockerfile": "Dockerfile" + }, + // The Makefile and support/sg2002/build expect the repository at + // ~/NanoKVM of the baked-in "build" user -- mount it exactly there. + "workspaceMount": "source=${localWorkspaceFolder},target=/home/build/NanoKVM,type=bind,consistency=cached", + "workspaceFolder": "/home/build/NanoKVM", + "remoteUser": "build", + // The published builder image bakes in the CI runner's uid (currently + // 1001); on Linux hosts this remaps "build" to the local uid so the bind + // mount stays writable. The remap chowns /home/build once, which makes + // the first container creation noticeably slower. + "updateRemoteUserUID": true, + // Equivalent of `. ~/MaixCDK/bin/activate` for every process spawned in + // the container, interactive or not (tasks, extensions, exec). + "remoteEnv": { + "VIRTUAL_ENV": "/home/build/MaixCDK", + "PATH": "/home/build/MaixCDK/bin:${containerEnv:PATH}" + }, + "forwardPorts": [3001], + "portsAttributes": { + "3001": { + "label": "web dev server (vite)" + } + }, + "postCreateCommand": ".devcontainer/post-create.sh", + // This configuration itself mounts nothing from the host (VS Code may + // still forward your SSH agent and git credentials by default). To use + // git over SSH or reach a NanoKVM device from inside the container, + // uncomment the mount below. + // "mounts": [ + // "source=${localEnv:HOME}/.ssh,target=/home/build/.ssh,type=bind,readonly" + // ], + "customizations": { + "vscode": { + "extensions": [ + "golang.go", + "ms-vscode.cpptools", + "dbaeumer.vscode-eslint", + "esbenp.prettier-vscode", + "bradlc.vscode-tailwindcss" + ] + } + } +} diff --git a/.devcontainer/post-create.sh b/.devcontainer/post-create.sh new file mode 100755 index 0000000..80e87cc --- /dev/null +++ b/.devcontainer/post-create.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash +# Runs once inside the dev container after it is created (see +# .devcontainer/devcontainer.json). Keeps the container aligned with the +# CLI flow in the Makefile and scripts/build-in-container.sh. +set -euo pipefail + +repo_root="$(cd "$(dirname "$0")/.." && pwd)" + +# The MaixCDK components baked into the image can be older than this +# checkout; resync them so C builds never use stale sources (same reason +# scripts/build-in-container.sh runs update_lib first). +"$repo_root/support/sg2002/build" update_lib + +# Frontend dependencies. postCreate has no TTY, and CI=true lets pnpm make +# its non-interactive choices -- e.g. purging a node_modules tree that was +# installed from the host OS through the bind mount. +(cd "$repo_root/web" && CI=true pnpm install --frozen-lockfile) + +# Start every interactive shell with the MaixCDK virtualenv active, +# mirroring `make shell`. +activate_line='. "$HOME/MaixCDK/bin/activate"' +if ! grep -qxF "$activate_line" "$HOME/.bashrc" 2>/dev/null; then + printf '\n# MaixCDK virtualenv (added by .devcontainer/post-create.sh)\n%s\n' \ + "$activate_line" >>"$HOME/.bashrc" +fi + +# The build user's home has no shell skeleton files (useradd ran with +# --no-create-home), so login shells would skip ~/.bashrc; give them the +# standard Debian chain. +if [ ! -f "$HOME/.profile" ]; then + cat >"$HOME/.profile" <<'EOF' +# ~/.profile (added by .devcontainer/post-create.sh) +if [ -n "$BASH_VERSION" ] && [ -f "$HOME/.bashrc" ]; then + . "$HOME/.bashrc" +fi +EOF +fi diff --git a/.gitignore b/.gitignore index db77876..a9d159b 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,10 @@ web/node_modules web/dist web/web +# pnpm keeps its content-addressable store next to the project when the +# home directory is on a different filesystem (e.g. inside the dev container) +.pnpm-store/ + .DS_Store *.local *.suo diff --git a/README.md b/README.md index 4a57a2d..8790d0d 100644 --- a/README.md +++ b/README.md @@ -91,6 +91,18 @@ Start with the guide that matches the part of NanoKVM you want to work on: > Backend compilation and runtime validation require the target toolchain or a NanoKVM device. See the module-specific guides above for the latest development workflow. +### Dev container (optional) + +The repository ships a [Dev Container](https://containers.dev) setup (`.devcontainer/`) that layers the frontend toolchain (Node 22, pnpm 11) on top of the release builder image (RISC-V cross toolchain, Go, MaixCDK), so one container covers Go, C support-layer and web development. It is a convenience for IDE users; the `make` targets in the repository root remain the canonical CLI workflow. + +- Requirements: Docker plus an editor or CLI with dev container support (e.g. the VS Code "Dev Containers" extension, or `devcontainer up`). +- On creation the container prepares itself automatically: `support/sg2002/build update_lib` (resyncs MaixCDK components with the checkout) and `pnpm install` for the frontend. +- Backend: run `server/build.sh` for a release-equivalent binary (BoringCrypto + `dl_lib` RPATH). `make app` builds a plain development binary without those, so prefer `server/build.sh` whenever the result should match a release. +- Support layer: `cd support/sg2002 && ./build kvm_system` (the MaixCDK virtualenv is part of the container environment). +- Frontend: `cd web && pnpm dev` (VS Code forwards port 3001 automatically; other clients may need to map it themselves); see [web/README.md](web/README.md) for pointing it at a device. +- The base image `ghcr.io/sipeed/nanokvm-builder:latest` is x86-64 only; on Apple Silicon it runs under emulation (works, but C builds are slow). You can also build the image locally first (`make builder-image`) and point the `BASE_IMAGE` build arg at it (e.g. via `build.args` in `devcontainer.json`). On Linux hosts the first container creation also takes a while: the container user is remapped to your uid, which re-chowns its home directory once. +- Hardware-dependent verification still requires a real NanoKVM device; the container covers compiling, linting and mock-mode web development only. + ## 🔩 Hardware Platform (NanoKVM Cube/PCIe) NanoKVM is based on Sipeed [LicheeRV Nano](https://wiki.sipeed.com/hardware/zh/lichee/RV_Nano/1_intro.html). You can find specifications, schematics, and dimensional drawings in the [download station](https://dl.sipeed.com/shareURL/LICHEE/LicheeRV_Nano). diff --git a/server/README.md b/server/README.md index 172fa9c..e58acf1 100644 --- a/server/README.md +++ b/server/README.md @@ -64,7 +64,7 @@ turn: ## Compile & Deploy -Note: Use Linux operating system (x86-64) with Go 1.25 or newer. This build process is not compatible with ARM, Windows or macOS. +Note: The manual steps below require a Linux x86-64 host with Go 1.25 or newer; they are not compatible with ARM, Windows or macOS. With Docker you can skip them entirely and use the containerized flow instead — the root [Makefile](../Makefile) (`make shell`) or the dev container (see "Development" in the root [README](../README.md)) — which works on any host OS; run `server/build.sh` inside the container for a release-equivalent build. 1. Install the Toolchain 1. Download the toolchain from the following link: [Download Link](https://sophon-file.sophon.cn/sophon-prod-s3/drive/23/03/07/16/host-tools.tar.gz). diff --git a/web/README.md b/web/README.md index 4c385df..f424716 100644 --- a/web/README.md +++ b/web/README.md @@ -22,6 +22,8 @@ src ## Local Development +Requirements: Node.js >= 22 and pnpm >= 11 (the versions CI uses; enforced via the `engines` field in `package.json`). + > Development requires SSH. You can enable it in the Web Settings: `Settings > SSH`. Due to CORS restrictions, authentication needs to be disabled during local development. diff --git a/web/package.json b/web/package.json index 53c41b2..c393237 100644 --- a/web/package.json +++ b/web/package.json @@ -3,6 +3,10 @@ "private": true, "version": "0.0.0", "type": "module", + "engines": { + "node": ">=22", + "pnpm": ">=11" + }, "scripts": { "dev": "vite", "mocked": "vite --mode mocked",