tangled
alpha
login
or
join now
tsiry-sandratraina.com
/
freebsd-up
1
fork
atom
A simple, zero-configuration script to quickly boot FreeBSD ISO images using QEMU
1
fork
atom
overview
issues
pulls
pipelines
use deno cliffy for parsing command line args
tsiry-sandratraina.com
4 months ago
92a2dcf0
4bf606e6
+131
-47
3 changed files
expand all
collapse all
unified
split
deno.json
deno.lock
main.ts
+2
deno.json
···
3
3
"dev": "deno run --watch main.ts"
4
4
},
5
5
"imports": {
6
6
+
"@cliffy/command": "jsr:@cliffy/command@^1.0.0-rc.8",
7
7
+
"@cliffy/flags": "jsr:@cliffy/flags@^1.0.0-rc.8",
6
8
"@std/assert": "jsr:@std/assert@1",
7
9
"chalk": "npm:chalk@^5.6.2"
8
10
}
+40
deno.lock
···
1
1
{
2
2
"version": "5",
3
3
"specifiers": {
4
4
+
"jsr:@cliffy/command@^1.0.0-rc.8": "1.0.0-rc.8",
5
5
+
"jsr:@cliffy/flags@1.0.0-rc.8": "1.0.0-rc.8",
6
6
+
"jsr:@cliffy/flags@^1.0.0-rc.8": "1.0.0-rc.8",
7
7
+
"jsr:@cliffy/internal@1.0.0-rc.8": "1.0.0-rc.8",
8
8
+
"jsr:@cliffy/table@1.0.0-rc.8": "1.0.0-rc.8",
4
9
"jsr:@std/assert@1": "1.0.15",
10
10
+
"jsr:@std/fmt@~1.0.2": "1.0.8",
5
11
"jsr:@std/internal@^1.0.12": "1.0.12",
12
12
+
"jsr:@std/text@~1.0.7": "1.0.15",
6
13
"npm:chalk@^5.6.2": "5.6.2"
7
14
},
8
15
"jsr": {
16
16
+
"@cliffy/command@1.0.0-rc.8": {
17
17
+
"integrity": "758147790797c74a707e5294cc7285df665422a13d2a483437092ffce40b5557",
18
18
+
"dependencies": [
19
19
+
"jsr:@cliffy/flags@1.0.0-rc.8",
20
20
+
"jsr:@cliffy/internal",
21
21
+
"jsr:@cliffy/table",
22
22
+
"jsr:@std/fmt",
23
23
+
"jsr:@std/text"
24
24
+
]
25
25
+
},
26
26
+
"@cliffy/flags@1.0.0-rc.8": {
27
27
+
"integrity": "0f1043ce6ef037ba1cb5fe6b1bcecb25dc2f29371a1c17f278ab0f45e4b6f46c",
28
28
+
"dependencies": [
29
29
+
"jsr:@std/text"
30
30
+
]
31
31
+
},
32
32
+
"@cliffy/internal@1.0.0-rc.8": {
33
33
+
"integrity": "34cdf2fad9b084b5aed493b138d573f52d4e988767215f7460daf0b918ff43d8"
34
34
+
},
35
35
+
"@cliffy/table@1.0.0-rc.8": {
36
36
+
"integrity": "8bbcdc2ba5e0061b4b13810a24e6f5c6ab19c09f0cce9eb691ccd76c7c6c9db5",
37
37
+
"dependencies": [
38
38
+
"jsr:@std/fmt"
39
39
+
]
40
40
+
},
9
41
"@std/assert@1.0.15": {
10
42
"integrity": "d64018e951dbdfab9777335ecdb000c0b4e3df036984083be219ce5941e4703b",
11
43
"dependencies": [
12
44
"jsr:@std/internal"
13
45
]
46
46
+
},
47
47
+
"@std/fmt@1.0.8": {
48
48
+
"integrity": "71e1fc498787e4434d213647a6e43e794af4fd393ef8f52062246e06f7e372b7"
14
49
},
15
50
"@std/internal@1.0.12": {
16
51
"integrity": "972a634fd5bc34b242024402972cd5143eac68d8dffaca5eaa4dba30ce17b027"
52
52
+
},
53
53
+
"@std/text@1.0.15": {
54
54
+
"integrity": "91f5cc1e12779a3d95f1be34e763f9c28a75a078b7360e6fcaef0d8d9b1e3e7f"
17
55
}
18
56
},
19
57
"npm": {
···
23
61
},
24
62
"workspace": {
25
63
"dependencies": [
64
64
+
"jsr:@cliffy/command@^1.0.0-rc.8",
65
65
+
"jsr:@cliffy/flags@^1.0.0-rc.8",
26
66
"jsr:@std/assert@1",
27
67
"npm:chalk@^5.6.2"
28
68
]
+89
-47
main.ts
···
1
1
#!/usr/bin/env -S deno run --allow-run --allow-read --allow-env
2
2
3
3
+
import { Command } from "@cliffy/command";
3
4
import chalk from "chalk";
4
5
5
6
const DEFAULT_VERSION = "14.3-RELEASE";
7
7
+
8
8
+
interface Options {
9
9
+
output?: string;
10
10
+
cpu: string;
11
11
+
memory: string;
12
12
+
}
6
13
7
14
async function downloadIso(url: string, outputPath?: string): Promise<string> {
8
15
const filename = url.split("/").pop()!;
···
34
41
return outputPath;
35
42
}
36
43
37
37
-
if (import.meta.main) {
38
38
-
if (Deno.args.includes("--help") || Deno.args.includes("-h")) {
39
39
-
console.error(
40
40
-
chalk.greenBright(
41
41
-
"Usage: freebsd-up [path-to-iso | version | url]",
42
42
-
),
43
43
-
);
44
44
-
Deno.exit(1);
45
45
-
}
46
46
-
47
47
-
if (Deno.args.length === 0) {
48
48
-
console.log(
49
49
-
chalk.blueBright(
50
50
-
`No ISO path provided, defaulting to ${chalk.cyan("FreeBSD")} ${
51
51
-
chalk.cyan(DEFAULT_VERSION)
52
52
-
}...`,
53
53
-
),
54
54
-
);
55
55
-
const url = `https://download.freebsd.org/ftp/releases/ISO-IMAGES/${
56
56
-
DEFAULT_VERSION.split("-")[0]
57
57
-
}/FreeBSD-${DEFAULT_VERSION}-amd64-disc1.iso`;
58
58
-
Deno.args.push(url);
59
59
-
} else {
60
60
-
const versionRegex = /^\d{1,2}\.\d{1,2}-(RELEASE|BETA\d*|RC\d*)$/;
61
61
-
const arg = Deno.args[0];
62
62
-
if (versionRegex.test(arg)) {
63
63
-
console.log(
64
64
-
chalk.blueBright(
65
65
-
`Detected version ${chalk.cyan(arg)}, constructing download URL...`,
66
66
-
),
67
67
-
);
68
68
-
const url = `https://download.freebsd.org/ftp/releases/ISO-IMAGES/${
69
69
-
arg.split("-")[0]
70
70
-
}/FreeBSD-${arg}-amd64-disc1.iso`;
71
71
-
Deno.args[0] = url;
72
72
-
}
73
73
-
}
74
74
-
75
75
-
let isoPath = Deno.args[0];
76
76
-
77
77
-
if (
78
78
-
Deno.args[0].startsWith("https://") || Deno.args[0].startsWith("http://")
79
79
-
) {
80
80
-
isoPath = await downloadIso(Deno.args[0]);
81
81
-
}
44
44
+
function constructDownloadUrl(version: string): string {
45
45
+
return `https://download.freebsd.org/ftp/releases/ISO-IMAGES/${
46
46
+
version.split("-")[0]
47
47
+
}/FreeBSD-${version}-amd64-disc1.iso`;
48
48
+
}
82
49
50
50
+
async function runQemu(isoPath: string, options: Options): Promise<void> {
83
51
const cmd = new Deno.Command("qemu-system-x86_64", {
84
52
args: [
85
53
"-enable-kvm",
86
54
"-cpu",
87
87
-
"host",
55
55
+
options.cpu,
88
56
"-m",
89
89
-
"2G",
57
57
+
options.memory,
90
58
"-smp",
91
59
"2",
92
60
"-cdrom",
···
116
84
Deno.exit(status.code);
117
85
}
118
86
}
87
87
+
88
88
+
function handleInput(input?: string): string {
89
89
+
if (!input) {
90
90
+
console.log(
91
91
+
chalk.blueBright(
92
92
+
`No ISO path provided, defaulting to ${chalk.cyan("FreeBSD")} ${
93
93
+
chalk.cyan(DEFAULT_VERSION)
94
94
+
}...`,
95
95
+
),
96
96
+
);
97
97
+
return constructDownloadUrl(DEFAULT_VERSION);
98
98
+
}
99
99
+
100
100
+
const versionRegex = /^\d{1,2}\.\d{1,2}-(RELEASE|BETA\d*|RC\d*)$/;
101
101
+
102
102
+
if (versionRegex.test(input)) {
103
103
+
console.log(
104
104
+
chalk.blueBright(
105
105
+
`Detected version ${chalk.cyan(input)}, constructing download URL...`,
106
106
+
),
107
107
+
);
108
108
+
return constructDownloadUrl(input);
109
109
+
}
110
110
+
111
111
+
return input;
112
112
+
}
113
113
+
114
114
+
if (import.meta.main) {
115
115
+
await new Command()
116
116
+
.name("freebsd-up")
117
117
+
.version("0.1.0")
118
118
+
.description("Start a FreeBSD virtual machine using QEMU")
119
119
+
.arguments("[path-to-iso-or-version:string]")
120
120
+
.option("-o, --output <path:string>", "Output path for downloaded ISO")
121
121
+
.option("-c, --cpu <type:string>", "Type of CPU to emulate", {
122
122
+
default: "host",
123
123
+
})
124
124
+
.option("-m, --memory <size:string>", "Amount of memory for the VM", {
125
125
+
default: "2G",
126
126
+
})
127
127
+
.example(
128
128
+
"Default usage",
129
129
+
"freebsd-up",
130
130
+
)
131
131
+
.example(
132
132
+
"Specific version",
133
133
+
"freebsd-up 14.3-RELEASE",
134
134
+
)
135
135
+
.example(
136
136
+
"Local ISO file",
137
137
+
"freebsd-up /path/to/freebsd.iso",
138
138
+
)
139
139
+
.example(
140
140
+
"Download URL",
141
141
+
"freebsd-up https://download.freebsd.org/ftp/releases/ISO-IMAGES/14.3/FreeBSD-14.3-RELEASE-amd64-disc1.iso",
142
142
+
)
143
143
+
.action(async (options: Options, input?: string) => {
144
144
+
const resolvedInput = handleInput(input);
145
145
+
let isoPath = resolvedInput;
146
146
+
147
147
+
if (
148
148
+
resolvedInput.startsWith("https://") ||
149
149
+
resolvedInput.startsWith("http://")
150
150
+
) {
151
151
+
isoPath = await downloadIso(resolvedInput, options.output);
152
152
+
}
153
153
+
154
154
+
await runQemu(isoPath, {
155
155
+
cpu: options.cpu,
156
156
+
memory: options.memory,
157
157
+
});
158
158
+
})
159
159
+
.parse(Deno.args);
160
160
+
}