vercel-vm-factory 0.9.8 → 0.10.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/deploy-vm.mjs +93 -79
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -78,10 +78,12 @@ Choosing bash or zsh adds the matching package to the generated Dockerfile when
|
|
|
78
78
|
|
|
79
79
|
Generated shell setup examples:
|
|
80
80
|
|
|
81
|
-
- Alpine + `/bin/zsh`: installs `zsh curl git`,
|
|
81
|
+
- Alpine + `/bin/zsh`: installs `zsh curl git`, installs oh-my-zsh unattended, and writes `/root/.zshrc`.
|
|
82
82
|
- Ubuntu/Debian + `/bin/bash`: installs `bash` with `apt-get`.
|
|
83
83
|
- `/bin/sh`: no extra shell package is installed.
|
|
84
84
|
|
|
85
|
+
The generated Dockerfile sets `HOME=/root` and `SHELL` to the selected shell path.
|
|
86
|
+
|
|
85
87
|
Preinstall tools:
|
|
86
88
|
|
|
87
89
|
- `nodejs`
|
package/deploy-vm.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { mkdir, readFile, rm, stat, writeFile } from "node:fs/promises";
|
|
3
3
|
import { spawn } from "node:child_process";
|
|
4
|
-
import { createInterface } from "node:readline/promises";
|
|
5
4
|
import { stdin as input, stdout as output } from "node:process";
|
|
6
5
|
import { homedir } from "node:os";
|
|
7
6
|
import path from "node:path";
|
|
7
|
+
import * as p from "@clack/prompts";
|
|
8
8
|
|
|
9
9
|
const defaultWsShellImage = "ghcr.io/v1xingyue/ws-shell:v1.8.alpine";
|
|
10
10
|
|
|
@@ -294,15 +294,14 @@ async function installVercelCli() {
|
|
|
294
294
|
);
|
|
295
295
|
|
|
296
296
|
const install = await choosePackageInstall();
|
|
297
|
-
const answer = (
|
|
298
|
-
|
|
299
|
-
`Vercel CLI is not installed. Install with ${install.command} ${install.args.join(" ")}?`,
|
|
300
|
-
|
|
301
|
-
)
|
|
302
|
-
)
|
|
297
|
+
const answer = await promptResult(
|
|
298
|
+
p.confirm({
|
|
299
|
+
message: `Vercel CLI is not installed. Install with ${install.command} ${install.args.join(" ")}?`,
|
|
300
|
+
initialValue: false,
|
|
301
|
+
}),
|
|
302
|
+
);
|
|
303
303
|
|
|
304
|
-
if (answer
|
|
305
|
-
throw new Error("Vercel CLI is required. Exiting.");
|
|
304
|
+
if (!answer) throw new Error("Vercel CLI is required. Exiting.");
|
|
306
305
|
|
|
307
306
|
step(`Installing Vercel CLI with ${install.command}`);
|
|
308
307
|
await runNoUrl(install.command, install.args);
|
|
@@ -367,7 +366,13 @@ function makeDockerfile({ shell, tools, vmImage, vmImageName, wsShellImage }) {
|
|
|
367
366
|
const shellInstall = makeShellInstall({ shell, vmImageName });
|
|
368
367
|
const ohMyZshInstall =
|
|
369
368
|
path.basename(shell) === "zsh"
|
|
370
|
-
? `RUN RUNZSH=no CHSH=no KEEP_ZSHRC=
|
|
369
|
+
? `RUN RUNZSH=no CHSH=no KEEP_ZSHRC=no sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended \\
|
|
370
|
+
&& printf '%s\\n' \\
|
|
371
|
+
'export ZSH="$HOME/.oh-my-zsh"' \\
|
|
372
|
+
'ZSH_THEME="robbyrussell"' \\
|
|
373
|
+
'plugins=(git)' \\
|
|
374
|
+
'source "$ZSH/oh-my-zsh.sh"' \\
|
|
375
|
+
> /root/.zshrc`
|
|
371
376
|
: "";
|
|
372
377
|
const toolInstall = makeToolInstall({ tools, vmImageName });
|
|
373
378
|
const shellSetup = [shellInstall, ohMyZshInstall, toolInstall]
|
|
@@ -384,6 +389,8 @@ COPY --from=ws-shell /app/bin/wsterm /app/bin/wsterm
|
|
|
384
389
|
|
|
385
390
|
WORKDIR /app
|
|
386
391
|
ENV ENABLE_SSL=false
|
|
392
|
+
ENV HOME=/root
|
|
393
|
+
ENV SHELL=${shell}
|
|
387
394
|
EXPOSE 80
|
|
388
395
|
CMD ["/app/bin/wsterm","-bind",":80","-fork","${shell}"]
|
|
389
396
|
`;
|
|
@@ -468,7 +475,7 @@ async function secret(name, question, fallback) {
|
|
|
468
475
|
if (!input.isTTY) return fallback || "";
|
|
469
476
|
|
|
470
477
|
const placeholder = fallback ? mask(fallback) : "skip";
|
|
471
|
-
const answer = await
|
|
478
|
+
const answer = await askSecret(question, placeholder);
|
|
472
479
|
return answer || fallback || "";
|
|
473
480
|
}
|
|
474
481
|
|
|
@@ -513,22 +520,18 @@ async function chooseAuthMode(fallback) {
|
|
|
513
520
|
}
|
|
514
521
|
if (!input.isTTY) return modes.has(fallback) ? fallback : "basic";
|
|
515
522
|
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
if (answer === "2") return "github";
|
|
529
|
-
if (answer === "3") return "both";
|
|
530
|
-
if (answer === "4") return "none";
|
|
531
|
-
throw new Error("Authentication must be basic, github, both, or none");
|
|
523
|
+
return promptResult(
|
|
524
|
+
p.select({
|
|
525
|
+
message: "Authentication",
|
|
526
|
+
initialValue: modes.has(fallback) ? fallback : "basic",
|
|
527
|
+
options: [
|
|
528
|
+
{ value: "basic", label: "Basic", hint: "username/password" },
|
|
529
|
+
{ value: "github", label: "GitHub OAuth" },
|
|
530
|
+
{ value: "both", label: "Basic + GitHub OAuth" },
|
|
531
|
+
{ value: "none", label: "None", hint: "no app auth" },
|
|
532
|
+
],
|
|
533
|
+
}),
|
|
534
|
+
);
|
|
532
535
|
}
|
|
533
536
|
|
|
534
537
|
function usesBasicAuth(mode) {
|
|
@@ -545,25 +548,23 @@ async function chooseVmImage(fallback) {
|
|
|
545
548
|
if (!input.isTTY) return fallback;
|
|
546
549
|
|
|
547
550
|
const names = Object.keys(vmImages);
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
551
|
+
const answer = await promptResult(
|
|
552
|
+
p.select({
|
|
553
|
+
message: "VM image",
|
|
554
|
+
initialValue: vmImages[fallback] ? fallback : "custom",
|
|
555
|
+
options: names
|
|
556
|
+
.map((name) => ({ value: name, label: name, hint: vmImages[name] }))
|
|
557
|
+
.concat([
|
|
558
|
+
{ value: "custom", label: "Custom", hint: "enter a full image name" },
|
|
559
|
+
]),
|
|
560
|
+
}),
|
|
555
561
|
);
|
|
556
562
|
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
if (!answer) return fallback;
|
|
560
|
-
if (vmImages[answer]) return answer;
|
|
561
|
-
if (/^[1-3]$/.test(answer)) return names[Number(answer) - 1];
|
|
562
|
-
if (answer === "4")
|
|
563
|
+
if (answer === "custom")
|
|
563
564
|
return value(
|
|
564
565
|
"custom-vm-image",
|
|
565
566
|
"Custom VM image",
|
|
566
|
-
defaults["custom-vm-image"],
|
|
567
|
+
vmImages[fallback] ? defaults["custom-vm-image"] : fallback,
|
|
567
568
|
);
|
|
568
569
|
return answer;
|
|
569
570
|
}
|
|
@@ -572,21 +573,17 @@ async function chooseShell(fallback) {
|
|
|
572
573
|
if (args.shell) return args.shell;
|
|
573
574
|
if (!input.isTTY) return fallback;
|
|
574
575
|
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
shell,
|
|
580
|
-
|
|
581
|
-
|
|
576
|
+
return promptResult(
|
|
577
|
+
p.select({
|
|
578
|
+
message: "Shell",
|
|
579
|
+
initialValue: shells.includes(fallback) ? fallback : "/bin/sh",
|
|
580
|
+
options: shells.map((shell, index) => ({
|
|
581
|
+
value: shell,
|
|
582
|
+
label: shell,
|
|
583
|
+
hint: index === shells.length - 1 ? "default" : undefined,
|
|
584
|
+
})),
|
|
585
|
+
}),
|
|
582
586
|
);
|
|
583
|
-
|
|
584
|
-
const answer = await askText("Shell", fallback);
|
|
585
|
-
|
|
586
|
-
if (!answer) return fallback;
|
|
587
|
-
if (shells.includes(answer)) return answer;
|
|
588
|
-
if (/^[1-3]$/.test(answer)) return shells[Number(answer) - 1];
|
|
589
|
-
return answer;
|
|
590
587
|
}
|
|
591
588
|
|
|
592
589
|
async function chooseTools(fallback) {
|
|
@@ -594,17 +591,20 @@ async function chooseTools(fallback) {
|
|
|
594
591
|
if (!input.isTTY) return parseTools(fallback).join(",");
|
|
595
592
|
|
|
596
593
|
const names = Object.keys(toolChoices);
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
594
|
+
const selected = await promptResult(
|
|
595
|
+
p.multiselect({
|
|
596
|
+
message: "Preinstall tools",
|
|
597
|
+
required: false,
|
|
598
|
+
initialValues: parseTools(fallback),
|
|
599
|
+
options: names.map((name) => ({
|
|
600
|
+
value: name,
|
|
601
|
+
label: name,
|
|
602
|
+
hint: toolChoices[name],
|
|
603
|
+
})),
|
|
604
|
+
}),
|
|
604
605
|
);
|
|
605
606
|
|
|
606
|
-
|
|
607
|
-
return parseTools(answer || fallback).join(",");
|
|
607
|
+
return selected.join(",");
|
|
608
608
|
}
|
|
609
609
|
|
|
610
610
|
function parseTools(value) {
|
|
@@ -779,6 +779,10 @@ function findLastUrl(text) {
|
|
|
779
779
|
}
|
|
780
780
|
|
|
781
781
|
function printHeader() {
|
|
782
|
+
if (input.isTTY) {
|
|
783
|
+
p.intro("Vercel VM Factory");
|
|
784
|
+
return;
|
|
785
|
+
}
|
|
782
786
|
console.log("");
|
|
783
787
|
console.log(color.cyan(" __ ____ __ _____ _ "));
|
|
784
788
|
console.log(color.cyan(" \\ \\ / / \\/ | | ___|_ _ ___| |_ ___ _ __ _ _ "));
|
|
@@ -830,25 +834,35 @@ function printKeyValue(key, value) {
|
|
|
830
834
|
}
|
|
831
835
|
|
|
832
836
|
async function askText(question, fallback = "") {
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
837
|
+
return String(
|
|
838
|
+
await promptResult(
|
|
839
|
+
p.text({
|
|
840
|
+
message: question,
|
|
841
|
+
placeholder: fallback || undefined,
|
|
842
|
+
}),
|
|
843
|
+
),
|
|
839
844
|
).trim();
|
|
840
|
-
rl.close();
|
|
841
|
-
return answer;
|
|
842
845
|
}
|
|
843
846
|
|
|
844
|
-
function
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
847
|
+
async function askSecret(question, fallback = "") {
|
|
848
|
+
return String(
|
|
849
|
+
await promptResult(
|
|
850
|
+
p.password({
|
|
851
|
+
message: question,
|
|
852
|
+
mask: "*",
|
|
853
|
+
placeholder: fallback || undefined,
|
|
854
|
+
}),
|
|
855
|
+
),
|
|
856
|
+
).trim();
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
async function promptResult(resultPromise) {
|
|
860
|
+
const result = await resultPromise;
|
|
861
|
+
if (p.isCancel(result)) {
|
|
862
|
+
p.cancel("Operation cancelled.");
|
|
863
|
+
process.exit(0);
|
|
850
864
|
}
|
|
851
|
-
|
|
865
|
+
return result;
|
|
852
866
|
}
|
|
853
867
|
|
|
854
868
|
function step(text) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vercel-vm-factory",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.8",
|
|
4
4
|
"description": "Create Vercel Container deployments for ws-shell from selectable VM images.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -12,6 +12,9 @@
|
|
|
12
12
|
"deploy-vm.mjs",
|
|
13
13
|
"README.md"
|
|
14
14
|
],
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"@clack/prompts": "^0.11.0"
|
|
17
|
+
},
|
|
15
18
|
"scripts": {
|
|
16
19
|
"deploy": "node deploy-vm.mjs create",
|
|
17
20
|
"doctor": "node deploy-vm.mjs doctor",
|