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
50 changes: 43 additions & 7 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,16 +1,52 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
ARG VARIANT="20-bookworm"
FROM mcr.microsoft.com/devcontainers/typescript-node:1-${VARIANT}

RUN mkdir -p /workspaces && chown node:node /workspaces
# Devcontainer for Devcontainers.app (Tauri 2 + Deno).
#
# The end product is a macOS app, but on Linux we can still:
# * `cargo check` / `clippy` / `test` the Rust crate in `src-tauri/`
# (matches the `rust` job in `.github/workflows/devcontainers-app.yml`),
# * `deno task check` / `build` the engine bundle (matches the `deno` job),
# * `yarn --cwd frontend` build the Vite frontend,
# * `npx tsc -p spec/tsconfig.json --noEmit` for the spec slice.
#
# Rust + Tauri system deps are baked in here. Deno and Node are layered on
# via devcontainer features in `devcontainer.json`.

ARG USERNAME=node
ARG VARIANT="bookworm"
FROM mcr.microsoft.com/devcontainers/rust:1-${VARIANT}

# Tauri 2 Linux system dependencies (webkit2gtk 4.1).
# Mirrors the install step in `.github/workflows/devcontainers-app.yml`.
RUN apt-get update \
&& export DEBIAN_FRONTEND=noninteractive \
&& apt-get install -y --no-install-recommends \
libwebkit2gtk-4.1-dev \
libgtk-3-dev \
libayatana-appindicator3-dev \
librsvg2-dev \
libsoup-3.0-dev \
libjavascriptcoregtk-4.1-dev \
patchelf \
file \
pkg-config \
build-essential \
curl \
wget \
ca-certificates \
xdg-utils \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

ARG USERNAME=vscode

# Pre-install commonly used Rust components and the Tauri CLI.
USER $USERNAME
RUN rustup component add clippy rustfmt \
&& cargo install tauri-cli --version "^2.0" --locked

# Save command line history
# Save command line history across container rebuilds.
RUN echo "export HISTFILE=/home/$USERNAME/commandhistory/.bash_history" >> "/home/$USERNAME/.bashrc" \
&& echo "export PROMPT_COMMAND='history -a'" >> "/home/$USERNAME/.bashrc" \
&& mkdir -p /home/$USERNAME/commandhistory \
&& touch /home/$USERNAME/commandhistory/.bash_history \
&& chown -R $USERNAME /home/$USERNAME/commandhistory
&& touch /home/$USERNAME/commandhistory/.bash_history
9 changes: 0 additions & 9 deletions .devcontainer/devcontainer-lock.json

This file was deleted.

85 changes: 63 additions & 22 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,42 +2,83 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// Devcontainer for working on Devcontainers.app (Tauri 2 + Deno).
//
// See `ROADMAP.md` for what lives where:
// - `src-tauri/` Rust host (Tauri 2)
// - `spec/` Deno-built devcontainer engine slice
// - `frontend/` Vite TS WebView frontend
//
// The legacy Node CLI in `src/` is still present (slated for deletion in
// step 9) but is NOT the focus of this devcontainer.
{
"name": "Dev Containers CLI",
"name": "Devcontainers.app (Tauri 2 + Deno)",
"build": {
"dockerfile": "Dockerfile",
"args": {
"VARIANT": "20-bookworm"
"args": {
"VARIANT": "bookworm"
}
},

"features": {
// Node + yarn for the Vite frontend and the spec-slice tsc check.
"ghcr.io/devcontainers/features/node:1": {
"version": "20",
"nodeGypDependencies": false
},
// Deno for `deno task check` / `build` (engine bundle).
"ghcr.io/devcontainers-extra/features/deno:1": {
"version": "latest"
},
// Useful for invoking the legacy CLI's container backends and for
// future Podman/Docker support (step 11).
"ghcr.io/devcontainers/features/docker-in-docker:2": {},
"ghcr.io/devcontainers/features/github-cli:1": {}
},

"mounts": [
// Keep command history across instances
"source=dev-containers-cli-bashhistory,target=/home/node/commandhistory"
// Keep command history across instances.
"source=devcontainers-app-bashhistory,target=/home/vscode/commandhistory",
// Cache cargo registry/git and target dirs across rebuilds.
"source=devcontainers-app-cargo-registry,target=/usr/local/cargo/registry,type=volume",
"source=devcontainers-app-cargo-git,target=/usr/local/cargo/git,type=volume",
"source=devcontainers-app-cargo-target,target=${containerWorkspaceFolder}/src-tauri/target,type=volume"
],

"postCreateCommand": "yarn install",

"remoteUser": "node",

"features": {
"ghcr.io/devcontainers/features/docker-in-docker:2": {}
"containerEnv": {
"CARGO_TERM_COLOR": "always",
"RUST_BACKTRACE": "1"
},


// Install JS deps for both the legacy spec typecheck (root yarn) and
// the Vite frontend, then warm up Deno's npm cache for the engine build.
"postCreateCommand": "sudo chown -R vscode:vscode /usr/local/cargo/registry /usr/local/cargo/git src-tauri/target && yarn install --frozen-lockfile && yarn --cwd frontend install --frozen-lockfile && deno task check || true",

"remoteUser": "vscode",

"customizations": {
"vscode": {
"extensions": [
"rust-lang.rust-analyzer",
"tamasfe.even-better-toml",
"vadimcn.vscode-lldb",
"denoland.vscode-deno",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"GitHub.vscode-pull-request-github"
]
},
"codespaces": {
"repositories": {
"devcontainers/features": {
"permissions": {
"contents": "write",
"workflows": "write"
}
}
],
"settings": {
"deno.enable": true,
"deno.enablePaths": [
"spec",
"scripts",
"frontend/src/devcontainer-engine"
],
"deno.config": "./deno.json",
"rust-analyzer.linkedProjects": [
"src-tauri/Cargo.toml"
],
"rust-analyzer.check.command": "clippy"
}
}
}
Expand Down
60 changes: 60 additions & 0 deletions .github/workflows/devcontainers-app.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
name: devcontainers-app

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
rust:
name: cargo check / clippy
runs-on: ubuntu-latest
defaults:
run:
working-directory: src-tauri
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy, rustfmt
- name: Install Tauri 2 system dependencies
run: |
sudo apt-get update
sudo apt-get install -y \
libwebkit2gtk-4.1-dev \
libgtk-3-dev \
libayatana-appindicator3-dev \
librsvg2-dev \
patchelf
- uses: Swatinem/rust-cache@v2
with:
workspaces: src-tauri
- run: cargo fmt --check
- run: cargo check --all-targets
- run: cargo clippy --all-targets -- -D warnings
- run: cargo test

deno:
name: deno check / build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: denoland/setup-deno@v2
with:
deno-version: v2.x
- run: deno task check
- run: deno task build
- run: deno task build:check-no-node-builtins

spec-typecheck:
name: spec slice tsc --noEmit
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20.x
cache: yarn
- run: yarn install --frozen-lockfile
- run: npx tsc -p spec/tsconfig.json --noEmit
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,9 @@ src/test/container-features/configs/temp_lifecycle-hooks-alternative-order
test-secrets-temp.json
src/test/container-*/**/src/**/README.md
!src/test/container-features/assets/*.tgz

# Tauri / Deno conversion outputs
/src-tauri/target/
/frontend/dist/
/frontend/node_modules/
.deno/
10 changes: 10 additions & 0 deletions NOTICE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# NOTICE

Devcontainers.app is a fork of `devcontainers/cli` by Microsoft (MIT-licensed,
see `LICENSE.txt` and `ThirdPartyNotices.txt`) being converted into a Tauri 2
macOS desktop application. The surviving TypeScript code in `spec/` retains
its original Microsoft copyright headers and MIT license. New Rust code in
`src-tauri/` and the Tauri/Vite scaffolding under `frontend/` are likewise
MIT-licensed.

The scope of the fork is documented in `ROADMAP.md`.
63 changes: 63 additions & 0 deletions ROADMAP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Roadmap — `devcontainers-cli` → Devcontainers.app

This document tracks the conversion from the Node-based devcontainer CLI to a
Tauri 2 macOS app that manages dev containers. The high-level plan is in the
issue body; the steps below are the unit of PR-by-PR delivery.

## Phase 1 — Foundation (this PR)

- [x] **Step 1 — Carve out the spec slice** (`spec/`) with a `FileHost`-only
`pfs.ts`. No edges into `containerFeaturesConfiguration` (`tar`),
`pfs.ts` real impl (`ncp`), or any `spec-node` module.
- [x] **Step 2 — Deno build pipeline** (`deno.json`, `scripts/build-engine.ts`,
`scripts/check-no-node-builtins.ts`) producing
`dist/devcontainer-engine.js`. CI bans Node built-ins from the bundle.
- [x] **Step 3 — Tauri 2 scaffold** (`src-tauri/`) and Vite TS frontend
(`frontend/`). `beforeBuildCommand` invokes `deno task build && yarn
--cwd ../frontend build`.
- [x] **Step 4 — Host layer skeletons** (`host/{config,permissions,
window_state,menu}.rs`, `commands/`) ported from
`wiki3-ai/wiki3-app`.

## Phase 1 (cont.) — MVP

- [ ] **Step 5 — `apple_containers` impl**: `pull`, `create`, `start`, `exec`,
`logs`, `stop`, `remove`. Behind feature flag `apple-containers-live`
for tests that drive the real `container` CLI.
- [ ] **Step 6 — FileHost bridge wired end-to-end**: WebView-side adapter
(`frontend/src/devcontainer-engine/index.ts`) calls Tauri commands
that read a real `.devcontainer/devcontainer.json`; the spec slice
returns a fully substituted config to Rust.
- [ ] **Step 7 — Lifecycle orchestrator** in Rust: parsed config →
`ContainerSpec` → `ContainerRuntime`. Run lifecycle hooks via
`portable-pty`, stream logs/events to the WebView.
- [ ] **Step 8 — MVP UI**: dashboard, "Open folder", workspace detail with
Up/Stop/Rebuild/Terminal/Logs (xterm.js).

## Phase 1 cleanup

- [ ] **Step 9 — Remove the legacy Node CLI**: delete `devcontainer.js`,
`esbuild.js`, `azure-pipelines.yml`, `node-pty`, `proxy-agent`,
`follow-redirects`, `yargs`, `chalk`, `src/spec-node/`, the old
`src/spec-*` folders (now mirrored in `spec/`). Update `README.md`
and rename the package.

## Phase 2 — Feature parity beyond MVP

- [ ] **Step 10 — Features + templates**: replace the Node `tar`/OCI logic
with Rust (`oci-distribution`, `tar`, `flate2`). Templates browser.
`dockerComposeFile` support (when Apple Containers' compose story is
settled, or via a Rust compose shim). Dotfiles bootstrap.

## Phase 3 — Other backends and platforms

- [ ] **Step 11 — Podman, then Docker** backends. Non-mac platforms.

## Open questions to resolve before each phase

- Apple Containers programmatic API surface (Swift/C) — keep an eye out so
`apple_containers.rs` can switch from CLI shelling to direct API calls.
- Compose semantics in Apple Containers.
- OCI auth parity with the upstream CLI for Features.
- Sync cadence with `devcontainers/cli`; the spec slice's directory layout
is intentionally close to upstream so periodic merges are cheap.
45 changes: 45 additions & 0 deletions deno.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"name": "devcontainers-app-engine",
"version": "0.1.0",
"exports": "./spec/index.ts",
"description": "Deno build of the devcontainer-spec slice consumed by the Tauri 2 app's WebView.",
"tasks": {
"check": "deno check spec/index.ts frontend/src/devcontainer-engine/index.ts",
"build": "deno run --allow-read --allow-write --allow-env --allow-run --allow-net scripts/build-engine.ts",
"build:check-no-node-builtins": "deno run --allow-read scripts/check-no-node-builtins.ts dist/devcontainer-engine.js"
},
"imports": {
"vscode-uri": "npm:vscode-uri@^3.1.0",
"jsonc-parser": "npm:jsonc-parser@^3.3.1",
"esbuild": "npm:esbuild@^0.27.3",
"esbuild-plugin-polyfill-node": "npm:esbuild-plugin-polyfill-node@^0.3.0",
"@tauri-apps/api/core": "npm:@tauri-apps/api@^2.9.0/core",
"@tauri-apps/api/event": "npm:@tauri-apps/api@^2.9.0/event"
},
"compilerOptions": {
"lib": ["ES2022", "DOM", "deno.window"],
"strict": true
},
"unstable": ["sloppy-imports"],
"nodeModulesDir": "auto",
"fmt": {
"useTabs": true,
"lineWidth": 200,
"singleQuote": true,
"semiColons": true
},
"lint": {
"rules": {
"tags": ["recommended"]
}
},
"exclude": [
"src/",
"built/",
"dist/",
"node_modules/",
"src-tauri/target/",
"frontend/node_modules/",
"frontend/dist/"
]
}
Loading
Loading