Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
98 changes: 0 additions & 98 deletions .github/workflows/build-wheels.yml

This file was deleted.

74 changes: 74 additions & 0 deletions .github/workflows/release-vmsh.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
name: Release experimental vmsh
on:
workflow_dispatch:
inputs:
version:
description: Release tag
required: true
publish:
description: Publish a GitHub release
type: boolean
default: false
permissions:
contents: write
jobs:
build:
strategy:
fail-fast: false
matrix:
include:
- {os: linux, arch: amd64, runner: ubuntu-24.04}
- {os: linux, arch: arm64, runner: ubuntu-24.04}
- {os: windows, arch: amd64, runner: ubuntu-24.04, ext: .exe}
- {os: windows, arch: arm64, runner: ubuntu-24.04, ext: .exe}
- {os: darwin, arch: arm64, runner: macos-15}
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version-file: go.mod
cache-dependency-path: |
go.sum
frontends/vmsh/go.sum
- run: go run ./internal/cmd/build-guestinit -arch "${{ matrix.arch }}"
- name: Build vmsh
working-directory: frontends/vmsh
env:
GOOS: ${{ matrix.os }}
GOARCH: ${{ matrix.arch }}
VERSION: ${{ inputs.version }}
EXE: ${{ matrix.ext }}
run: |
mkdir -p ../../dist
CGO_ENABLED=0 go build -trimpath -buildvcs=false \
-ldflags "-X j5.nz/cc/frontends/vmsh/internal/version.Release=${VERSION} -X j5.nz/cc/frontends/vmsh/internal/version.Commit=${GITHUB_SHA} -X j5.nz/cc/frontends/vmsh/internal/version.Dirty=false" \
-o "../../dist/vmsh_${VERSION}_${GOOS}_${GOARCH}${EXE}" ./cmd/vmsh
- name: Sign development macOS binary
if: matrix.os == 'darwin'
run: codesign --force --sign - --entitlements tools/entitlements.xml dist/vmsh_*
- uses: actions/upload-artifact@v4
with:
name: vmsh-${{ matrix.os }}-${{ matrix.arch }}
path: dist/vmsh_*
publish:
runs-on: ubuntu-24.04
needs: build
steps:
- uses: actions/download-artifact@v4
with:
path: dist
merge-multiple: true
- name: Checksums
working-directory: dist
run: sha256sum vmsh_* > checksums.txt
- uses: actions/upload-artifact@v4
with:
name: vmsh-checksums
path: dist/checksums.txt
- uses: softprops/action-gh-release@v2
if: inputs.publish
with:
tag_name: ${{ inputs.version }}
generate_release_notes: true
files: dist/*
22 changes: 22 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ jobs:
run: go test -race -short ./internal/ccvmd ./internal/netstack ./internal/virtio ./internal/vm/... -count=1 -timeout 15m

kvm-test:
# Keep full VM/rootfs runs available without extending every commit check.
if: github.event_name == 'workflow_dispatch'
name: KVM and rootfs tests (${{ matrix.name }})
runs-on: ubuntu-24.04
timeout-minutes: 45
Expand Down Expand Up @@ -190,3 +192,23 @@ jobs:
fi
go test -p 1 "${run_args[@]}" ${TEST_PACKAGES} -count=1 -timeout 40m
echo "KVM/rootfs shard '${{ matrix.name }}' took $((SECONDS - start))s"

vmsh:
name: Interactive vmsh
runs-on: ubuntu-24.04
timeout-minutes: 10
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version-file: go.mod
cache-dependency-path: |
go.sum
frontends/vmsh/go.sum
- run: go run ./internal/cmd/build-guestinit
- name: Test interactive shell
working-directory: frontends/vmsh
run: go test -short ./... -count=1 -timeout 3m
- name: Build interactive shell
working-directory: frontends/vmsh
run: go build -o ../../build/vmsh ./cmd/vmsh
13 changes: 1 addition & 12 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,8 @@
/tools/__pycache__
/tools/*.pyc
/tools/*.pyo
/pyneurodesk/.git
/pyneurodesk/.venv
/pyneurodesk/.pytest_cache
/pyneurodesk/.ipynb_checkpoints
/pyneurodesk/notebooks/.ipynb_checkpoints
/pyneurodesk/**/__pycache__
/pyneurodesk/**/*.pyc
/pyneurodesk/**/*.pyo
/pyneurodesk/.DS_Store
/pyneurodesk/dist
/pyneurodesk/src/pyneurodesk/bin/ccvm
/pyneurodesk/src/pyneurodesk/bin/ccvm.exe

*.simg
!fixtures/alpine.simg
!fixtures/alpine-arm64.simg
/frontends/vmsh/build/
48 changes: 26 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,31 @@ devices, or running a privileged helper daemon.
This repository is published at
[github.com/tinyrange/cc](https://github.com/tinyrange/cc).

The production desktop apps, SquadVM and NeurodeskAppX, live in the independent
[CrumbleCracker](https://github.com/tinyrange/vmsh) codebase. This
repository retains the experimental runtime, complete daemon and worker system,
and command-line frontends. There is no automatic synchronization between the
codebases. The old Python frontend has been removed; a future production Python
API will be designed separately.

## Interactive vmsh

The interactive shell is maintained in [`frontends/vmsh`](frontends/vmsh).
Build it against this checkout:

```sh
go run ./internal/cmd/build-guestinit
cd frontends/vmsh
go build -o ../../build/vmsh ./cmd/vmsh
../../build/vmsh
```

Inside the shell, select `@alpine`, run ordinary commands, and use `@host` to
return to the host. The vmsh binary includes its daemon entry point; a separate
ccvm executable is optional. The manual `Release experimental vmsh` workflow
builds shell artifacts and can publish them to this repository; `@upgrade` uses
those releases. Its macOS artifacts are ad-hoc signed development binaries.

## Status

Supported host backends:
Expand Down Expand Up @@ -201,27 +226,6 @@ VM and exec requests and through `GET /vm` for listing. Reported
`max_instances` is a daemon concurrency limit, not a guarantee that the host has
enough free memory or CPU for that many guests.

## Python Client

The Python package lives in `pyneurodesk/` and is published as `neurodesk`. It
can start or connect to the daemon, import Neurodesk containers from CVMFS, and
expose container commands through Python or shell wrappers.

```sh
pip install neurodesk
```

Example:

```python
import neurodesk as nd

nm = nd.container("niimath")
print(nm.run("niimath", "-help"))
```

See [pyneurodesk/README.md](pyneurodesk/README.md) for Python-specific usage.

## Worker control transport

Sidecar worker control uses an owner-only Unix socket where the platform
Expand Down Expand Up @@ -291,5 +295,5 @@ plaintext deprecation window before v1.
- `internal/oci`: OCI, SIMG/SIF, and CVMFS image import
- `internal/cvmfs`: minimal remote CVMFS catalog and file client
- `docs/design`: accepted plans for cross-cutting runtime features
- `pyneurodesk`: Python client and shell integration
- `frontends/vmsh`: interactive host, VM, and SSH shell
- `PLAN.md`: linux/amd64 support plan and milestone notes
59 changes: 59 additions & 0 deletions frontends/vmsh/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Repository Guidance

## vmsh Workflow Model

`vmsh` is an interactive, session-oriented shell. Do not reason about it as a
one-shot CLI whose primary workflow is `vmsh @image command`. A normal shell's
context is mostly "the current working directory"; `vmsh` extends that context
to "the selected system plus that system's working directory." Shell semantics
should feel seamless across host, VM, and SSH systems.

The ordinary workflow is:

- Start `vmsh` as an interactive shell.
- Use `@<image>` or `@<name> --from <image>` to select or create a VM-backed
system context.
- Run ordinary command lines in the selected context.
- Use `@host` to return to the host context.

For example, `@alpine` by itself is a context transition that starts the VM and
waits for it to become ready before returning the prompt.
`@alpine uname -a` is a supported one-shot command line inside a vmsh session,
but it is not the mental model for the product. Prefer examples and tests that
exercise context selection followed by ordinary commands when the behavior under
test depends on shell state, session state, daemon reuse, host/guest cwd
mirroring, aliases, exports, or repeated commands.

When discussing VM-host reliability, model the system as a long-lived
conversation between the frontend, vmshd/ccvm, the VM backend, guest init, and
the selected shell context. Avoid reducing failures to a single command
invocation. The important contracts are the allowed state transitions and
communication guarantees between those actors.

Tests should be useful, accurate, and focused on objective behavior. Avoid tests
that make incidental user-facing wording a strict contract; those tests block
reasonable copy changes without proving that behavior is broken. Use exact
assertions for structured state, parsed fields, exit codes, command routing,
files, protocol data, and other real contracts. Do not use `strings.Contains`
in tests unless the surrounding text is intentionally flexible and substring
matching is the only practical assertion. If a test primarily verifies UI prose,
remove it or replace it with a behavior-level check. If a test matches backend
errors, assert the structured error information instead of broad text. If a
test depends on strings from an unusual environment, treat it as flaky and
prefer rewriting or removing it.

Tests should primarily protect users from real bugs, not check that the code
still has a particular shape. Favor tests for end-to-end or behavior-level
outcomes that would matter to a user: a VM boots, a command runs in the right
place, copying works, terminal bytes are preserved, a documented protocol is
parsed, or a dangerous operation is blocked. Avoid adding tests whose main value
is preserving a convenience choice, recently chosen default, helper output,
exact argv construction, fallback order, or other implementation-adjacent
behavior unless failing that test would correspond to a real user-facing bug. A
compatibility guarantee invented during the current change is not automatically
worth testing; only keep it if it protects an important user workflow or
documented interface.

When deciding whether to add a test, ask: "What user bug would this catch?" If
the answer is mostly "it tells us the code changed," do not add the test. Prefer
no test over a low-value test that makes future useful changes harder.
Loading
Loading