Skip to content

Commit 4720c6c

Browse files
committed
Decouple gondolin runtime assets and refresh setup docs
1 parent b963602 commit 4720c6c

9 files changed

Lines changed: 267 additions & 45 deletions

File tree

.github/workflows/ci.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,9 @@ jobs:
8282
sudo apt-get update
8383
sudo apt-get install -y qemu-system-x86 e2fsprogs
8484
85+
- name: Prime Gondolin guest assets
86+
run: bunx gondolin exec -- /bin/true
87+
8588
- name: Run integration tests
8689
env:
8790
INTEGRATION_PLATFORM: linux/amd64
@@ -113,6 +116,9 @@ jobs:
113116
sudo apt-get update
114117
sudo apt-get install -y qemu-system-x86 e2fsprogs
115118
119+
- name: Prime Gondolin guest assets
120+
run: bunx gondolin exec -- /bin/true
121+
116122
- name: Run end-to-end smoke test
117123
env:
118124
PLATFORM: linux/amd64

README.md

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
`docker2vm` converts OCI container images (or Dockerfiles via BuildKit) into VM-compatible outputs. Today, the runtime materialization target is [Gondolin](https://github.com/earendil-works/gondolin).
44

5-
It follows an OCI-first flow inspired by "Docker without Docker":
5+
It follows an OCI-first flow inspired by ["Docker without Docker"](https://fly.io/blog/docker-without-docker/):
66

77
- resolve/pull an OCI image
88
- apply layers to a root filesystem
@@ -16,6 +16,7 @@ Docker containers share the host kernel. Gondolin runs workloads inside a VM, so
1616

1717
## Current features
1818

19+
- zero runtime npm dependencies (`dependencies: {}`)
1920
- `oci2gondolin` core converter
2021
- input: `--image`, `--oci-layout`, `--oci-tar` (exactly one)
2122
- platform: `linux/amd64`, `linux/arm64`
@@ -39,6 +40,7 @@ Docker containers share the host kernel. Gondolin runs workloads inside a VM, so
3940
- Bun >= 1.2
4041
- `e2fsprogs` (`mke2fs`, `debugfs`)
4142
- QEMU (for runtime smoke checks via `gondolin exec`)
43+
- Gondolin CLI installed separately (tested with `@earendil-works/gondolin@0.2.1`)
4244
- Docker (only required for `dockerfile2gondolin`)
4345

4446
macOS helpers:
@@ -53,17 +55,25 @@ Ubuntu helpers:
5355
sudo apt-get install -y e2fsprogs qemu-system-x86
5456
```
5557

56-
## Platform setup guides
58+
Install Gondolin CLI (tested version):
5759

58-
- [macOS guide](./docs/macos.md)
59-
- [Linux guide](./docs/linux.md)
60+
```bash
61+
bun add -g @earendil-works/gondolin@0.2.1
62+
```
6063

61-
## Install
64+
Prime guest assets once:
6265

6366
```bash
64-
bun install
67+
gondolin exec -- /bin/true
6568
```
6669

70+
> On macOS, `docker2vm` checks common Homebrew `e2fsprogs` locations automatically; updating `PATH` is usually optional.
71+
72+
## Platform setup guides
73+
74+
- [macOS guide](./docs/macos.md)
75+
- [Linux guide](./docs/linux.md)
76+
6777
## Quickstart
6878

6979
### 1) Validate
@@ -92,11 +102,12 @@ For each distro row, tests run a distro-specific probe command (for example `/et
92102

93103
### Choosing the build platform (`--platform`)
94104

95-
`--platform` selects which OCI image variant to convert, and should match the architecture you plan to run in Gondolin.
105+
`--platform` selects which OCI image variant to convert, and should match the architecture you plan to run in Gondolin. This applies to both `oci2gondolin` and `dockerfile2gondolin`.
96106

97107
- Apple Silicon / arm64 Linux hosts: `linux/arm64`
98108
- Intel / amd64 hosts: `linux/amd64`
99-
- If omitted, `oci2gondolin` defaults from host arch (`x64 -> linux/amd64`, `arm64 -> linux/arm64`).
109+
- If omitted, both commands default from host arch (`x64 -> linux/amd64`, `arm64 -> linux/arm64`).
110+
- You can always override manually with `--platform linux/amd64` or `--platform linux/arm64`.
100111

101112
For helper scripts:
102113

@@ -118,7 +129,7 @@ bun run oci2gondolin -- \
118129
### 3) Run with Gondolin
119130

120131
```bash
121-
GONDOLIN_GUEST_DIR=./out/busybox-assets bunx gondolin exec -- /bin/busybox echo hello
132+
GONDOLIN_GUEST_DIR=./out/busybox-assets gondolin exec -- /bin/busybox echo hello
122133
```
123134

124135
## Dockerfile flow
@@ -143,7 +154,7 @@ bun run dockerfile2gondolin -- \
143154
Then run:
144155

145156
```bash
146-
GONDOLIN_GUEST_DIR=./out/demo-assets bunx gondolin exec -- /usr/games/cowsay "hello"
157+
GONDOLIN_GUEST_DIR=./out/demo-assets gondolin exec -- /usr/games/cowsay "hello"
147158
```
148159

149160
## End-to-end smoke test

bun.lock

Lines changed: 1 addition & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/linux.md

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
This guide is for running `docker2vm` on Linux hosts.
44

5+
`docker2vm` itself has **0 runtime npm dependencies**; system/runtime tools are installed separately.
6+
57
## 1) Install required tools
68

79
### Ubuntu / Debian
@@ -15,30 +17,49 @@ Install Bun:
1517

1618
```bash
1719
curl -fsSL https://bun.sh/install | bash
18-
source ~/.bashrc
20+
export BUN_INSTALL="$HOME/.bun"
21+
export PATH="$BUN_INSTALL/bin:$PATH"
1922
```
2023

2124
If you want Dockerfile conversion (`dockerfile2gondolin`), install Docker and Buildx.
2225

23-
## 2) Verify toolchain
26+
## 2) Install Gondolin CLI separately (tested version)
27+
28+
`docker2vm` is tested with:
29+
30+
- `@earendil-works/gondolin@0.2.1`
31+
32+
Install (global):
33+
34+
```bash
35+
bun add -g @earendil-works/gondolin@0.2.1
36+
```
37+
38+
Prime guest assets once:
39+
40+
```bash
41+
gondolin exec -- /bin/true
42+
```
43+
44+
## 3) Verify toolchain
2445

2546
```bash
2647
bun --version
2748
qemu-system-x86_64 --version
2849
mke2fs -V
2950
debugfs -V
51+
gondolin --help >/dev/null
3052
```
3153

32-
## 3) Install dependencies and validate
54+
## 4) Validate from source checkout
3355

3456
```bash
35-
bun install
3657
bun run test
3758
bun run typecheck
3859
bun run build
3960
```
4061

41-
## 4) Choose the build platform
62+
## 5) Choose the build platform
4263

4364
Use a platform that matches the architecture you will run in Gondolin.
4465

@@ -57,7 +78,7 @@ bun run oci2gondolin -- \
5778
--out ./out/busybox-assets
5879
```
5980

60-
## 5) Run integration + smoke checks
81+
## 6) Run integration + smoke checks
6182

6283
amd64 host:
6384

docs/macos.md

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
This guide is for running `docker2vm` on macOS (Apple Silicon or Intel).
44

5+
`docker2vm` itself has **0 runtime npm dependencies**; system/runtime tools are installed separately.
6+
57
## 1) Install required tools
68

79
```bash
@@ -10,43 +12,55 @@ brew install bun qemu e2fsprogs
1012

1113
If you want Dockerfile conversion (`dockerfile2gondolin`), also install Docker Desktop.
1214

13-
## 2) Ensure `mke2fs` and `debugfs` are on `PATH`
15+
## 2) Optional: add `e2fsprogs` binaries to `PATH`
16+
17+
With Homebrew, `e2fsprogs` is often keg-only. `docker2vm` checks common Homebrew locations automatically, so a PATH change is usually **not required** for normal usage.
18+
19+
If you want to run `mke2fs` / `debugfs` manually in your shell:
20+
21+
```bash
22+
export PATH="$(brew --prefix e2fsprogs)/sbin:$PATH"
23+
```
24+
25+
To persist it, add that `export PATH=...` line to your shell profile (`~/.zshrc`, `~/.bashrc`, `~/.profile`, etc.).
26+
27+
## 3) Install Gondolin CLI separately (tested version)
28+
29+
`docker2vm` is tested with:
1430

15-
`e2fsprogs` is often keg-only on macOS.
31+
- `@earendil-works/gondolin@0.2.1`
1632

17-
### Apple Silicon (`/opt/homebrew`)
33+
Install (global):
1834

1935
```bash
20-
echo 'export PATH="/opt/homebrew/opt/e2fsprogs/sbin:$PATH"' >> ~/.zshrc
21-
source ~/.zshrc
36+
bun add -g @earendil-works/gondolin@0.2.1
2237
```
2338

24-
### Intel (`/usr/local`)
39+
Prime guest assets once:
2540

2641
```bash
27-
echo 'export PATH="/usr/local/opt/e2fsprogs/sbin:$PATH"' >> ~/.zshrc
28-
source ~/.zshrc
42+
gondolin exec -- /bin/true
2943
```
3044

31-
## 3) Verify toolchain
45+
## 4) Verify toolchain
3246

3347
```bash
3448
bun --version
3549
qemu-system-aarch64 --version || qemu-system-x86_64 --version
36-
mke2fs -V
37-
debugfs -V
50+
"$(brew --prefix e2fsprogs)/sbin/mke2fs" -V
51+
"$(brew --prefix e2fsprogs)/sbin/debugfs" -V
52+
gondolin --help >/dev/null
3853
```
3954

40-
## 4) Install dependencies and validate
55+
## 5) Validate from source checkout
4156

4257
```bash
43-
bun install
4458
bun run test
4559
bun run typecheck
4660
bun run build
4761
```
4862

49-
## 5) Choose the build platform
63+
## 6) Choose the build platform
5064

5165
Use a platform that matches the architecture you will run in Gondolin.
5266

@@ -65,7 +79,7 @@ bun run oci2gondolin -- \
6579
--out ./out/busybox-assets
6680
```
6781

68-
## 6) Run integration + smoke checks
82+
## 7) Run integration + smoke checks
6983

7084
Apple Silicon:
7185

@@ -85,7 +99,7 @@ PLATFORM=linux/amd64 bun run e2e:smoke
8599

86100
### `mke2fs` / `debugfs` not found
87101

88-
Confirm PATH includes the `e2fsprogs` `sbin` directory shown above.
102+
`docker2vm` should find common Homebrew locations automatically. If your install uses a custom prefix, either add it to `PATH` or point `GONDOLIN_GUEST_DIR` to prepared assets and verify `e2fsprogs` binaries are installed.
89103

90104
### Case-sensitive filename conflicts during conversion
91105

package.json

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,13 @@
1919
"dockerfile2gondolin": "bun run src/bin/dockerfile2gondolin.ts"
2020
},
2121
"devDependencies": {
22+
"@earendil-works/gondolin": "0.2.1",
2223
"@types/node": "^22.13.10",
2324
"typescript": "^5.7.3"
2425
},
2526
"engines": {
2627
"bun": ">=1.2.0"
2728
},
2829
"packageManager": "bun@1.3.6",
29-
"dependencies": {
30-
"@earendil-works/gondolin": "^0.2.1"
31-
}
30+
"dependencies": {}
3231
}

src/oci2gondolin/materialize/index.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import fs from "node:fs";
22
import path from "node:path";
33

4-
import { ensureGuestAssets } from "@earendil-works/gondolin";
5-
64
import type { AppliedRootfs, MaterializedOutput, Oci2GondolinOptions } from "../types";
75
import { sha256File } from "../utils/digest";
6+
import { resolveGondolinGuestAssets } from "../../shared/gondolin-assets";
87
import { ensureDirectory } from "../utils/fs";
98
import { createExt4FromDirectory } from "./ext4";
109
import { injectGondolinRuntime } from "./runtime-injection";
@@ -51,7 +50,7 @@ export async function materializeOutput(
5150
let assetManifestPath: string | undefined;
5251

5352
if (options.mode === "assets") {
54-
const baseAssets = await ensureGuestAssets();
53+
const baseAssets = resolveGondolinGuestAssets();
5554
const kernelPath = path.join(outDir, KERNEL_FILENAME);
5655
const initramfsPath = path.join(outDir, INITRAMFS_FILENAME);
5756

src/oci2gondolin/materialize/runtime-injection.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,8 @@ import fs from "node:fs";
22
import path from "node:path";
33
import { spawnSync } from "node:child_process";
44

5-
import { ensureGuestAssets } from "@earendil-works/gondolin";
6-
75
import { CliUsageError } from "../../shared/cli-errors";
6+
import { resolveGondolinGuestAssets } from "../../shared/gondolin-assets";
87

98
type RuntimeFileSpec = {
109
sourcePathInRootfs: string;
@@ -89,7 +88,7 @@ export interface RuntimeInjectionResult {
8988
}
9089

9190
export async function extractBaseRootfsTree(destinationDir: string): Promise<string> {
92-
const guestAssets = await ensureGuestAssets();
91+
const guestAssets = resolveGondolinGuestAssets();
9392
const baseRootfsPath = guestAssets.rootfsPath;
9493

9594
if (!fs.existsSync(baseRootfsPath)) {
@@ -119,7 +118,7 @@ export async function extractBaseRootfsTree(destinationDir: string): Promise<str
119118
}
120119

121120
export async function injectGondolinRuntime(rootfsDir: string): Promise<RuntimeInjectionResult> {
122-
const guestAssets = await ensureGuestAssets();
121+
const guestAssets = resolveGondolinGuestAssets();
123122
const baseRootfsPath = guestAssets.rootfsPath;
124123

125124
if (!fs.existsSync(baseRootfsPath)) {

0 commit comments

Comments
 (0)