mirror of
https://github.com/sipeed/NanoKVM.git
synced 2026-09-11 00:22:56 -05:00
feat: add dev container for full-stack development (#861)
Layer the frontend toolchain on top of the existing release builder image so a single container covers Go, C support-layer and web development, with the builder image remaining the single source of truth for the cross toolchain, Go and MaixCDK. - Mount the workspace at /home/build/NanoKVM, where the Makefile and support/sg2002/build expect it, and resync MaixCDK components via update_lib on create so C builds never use stale sources - Install Node from official dist tarballs (integrity-checked, pinned to the major used by CI), with NODE_DIST_MIRROR / NPM_REGISTRY / BASE_IMAGE build args for restrictive or mirrored corporate networks - Export the MaixCDK virtualenv via remoteEnv so non-interactive processes (tasks, extensions, exec) get it too; make updateRemoteUserUID explicit since the published builder image bakes the CI runner's uid - Run pnpm non-interactively during post-create (no TTY) and give the skeleton-less home a standard ~/.profile -> ~/.bashrc chain - Make the web toolchain convention explicit via package.json engines (Node >= 22, pnpm >= 11, matching CI) and document it in web/README - Document the dev container in the README, point server/README at the container flow that works on any host OS, and ignore .pnpm-store/
This commit is contained in:
52
.devcontainer/Dockerfile
Normal file
52
.devcontainer/Dockerfile
Normal file
@@ -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-<uid>-<gid>).
|
||||
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
|
||||
49
.devcontainer/devcontainer.json
Normal file
49
.devcontainer/devcontainer.json
Normal file
@@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
37
.devcontainer/post-create.sh
Executable file
37
.devcontainer/post-create.sh
Executable file
@@ -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
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -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
|
||||
|
||||
12
README.md
12
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).
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"engines": {
|
||||
"node": ">=22",
|
||||
"pnpm": ">=11"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"mocked": "vite --mode mocked",
|
||||
|
||||
Reference in New Issue
Block a user