@getmonoceros/workbench 1.1.0 → 1.3.0
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
CHANGED
|
@@ -17,16 +17,18 @@ das vorab und geben plattform-spezifische Anleitung aus.
|
|
|
17
17
|
## Installation
|
|
18
18
|
|
|
19
19
|
```sh
|
|
20
|
-
|
|
20
|
+
# macOS / Linux
|
|
21
|
+
curl -fsSL https://raw.githubusercontent.com/getmonoceros/workbench/main/install.sh | sh
|
|
21
22
|
```
|
|
22
23
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
curl -fsSL https://raw.githubusercontent.com/getmonoceros/workbench/main/install.sh | sh
|
|
24
|
+
```powershell
|
|
25
|
+
# Windows (PowerShell)
|
|
26
|
+
iwr -useb https://raw.githubusercontent.com/getmonoceros/workbench/main/install.ps1 | iex
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
Das Skript prüft Docker + Node, installiert das Paket global via
|
|
30
|
+
`npm install -g`, und richtet die Shell-Completion für deine Shell
|
|
31
|
+
ein.
|
|
30
32
|
|
|
31
33
|
## Erste Schritte
|
|
32
34
|
|
package/dist/bin.js
CHANGED
|
@@ -122,12 +122,13 @@ function renderCommandsBlock(entries) {
|
|
|
122
122
|
const renderSection = (label, items) => {
|
|
123
123
|
if (items.length === 0) return;
|
|
124
124
|
lines.push("");
|
|
125
|
-
lines.push(
|
|
125
|
+
lines.push(underline(grey(label)));
|
|
126
|
+
lines.push("");
|
|
126
127
|
const rows = items.map((e) => [
|
|
127
128
|
cyan(e.name),
|
|
128
129
|
e.description
|
|
129
130
|
]);
|
|
130
|
-
lines.push(alignTable(rows, "
|
|
131
|
+
lines.push(alignTable(rows, ""));
|
|
131
132
|
};
|
|
132
133
|
for (const { key, label } of GROUPS) {
|
|
133
134
|
renderSection(label, byGroup.get(key) ?? []);
|
|
@@ -202,25 +203,25 @@ function detectHelpRequest(argv, main2) {
|
|
|
202
203
|
const separatorIdx = argv.indexOf("--");
|
|
203
204
|
if (helpIdx === -1) return null;
|
|
204
205
|
if (separatorIdx !== -1 && separatorIdx < helpIdx) return null;
|
|
205
|
-
const
|
|
206
|
+
const path13 = [];
|
|
206
207
|
const tokens = argv.slice(
|
|
207
208
|
0,
|
|
208
209
|
separatorIdx === -1 ? argv.length : separatorIdx
|
|
209
210
|
);
|
|
210
211
|
let cursor = main2;
|
|
211
212
|
const mainName = (main2.meta ?? {}).name ?? "monoceros";
|
|
212
|
-
|
|
213
|
+
path13.push(mainName);
|
|
213
214
|
for (const tok of tokens) {
|
|
214
215
|
if (tok.startsWith("-")) continue;
|
|
215
216
|
const subs = cursor.subCommands ?? {};
|
|
216
217
|
if (tok in subs) {
|
|
217
218
|
cursor = subs[tok];
|
|
218
|
-
|
|
219
|
+
path13.push(tok);
|
|
219
220
|
continue;
|
|
220
221
|
}
|
|
221
222
|
break;
|
|
222
223
|
}
|
|
223
|
-
return { path:
|
|
224
|
+
return { path: path13, cmd: cursor };
|
|
224
225
|
}
|
|
225
226
|
async function maybeRenderHelp(argv, main2) {
|
|
226
227
|
const hit = detectHelpRequest(argv, main2);
|
|
@@ -450,6 +451,9 @@ function workbenchCheckoutRoot() {
|
|
|
450
451
|
function componentsDir(root = workbenchRoot()) {
|
|
451
452
|
return path.join(root, "templates", "components");
|
|
452
453
|
}
|
|
454
|
+
function bundledFeaturesDir(root = workbenchRoot()) {
|
|
455
|
+
return path.join(root, "features");
|
|
456
|
+
}
|
|
453
457
|
function containerConfigsDir(home = monocerosHome()) {
|
|
454
458
|
return path.join(home, "container-configs");
|
|
455
459
|
}
|
|
@@ -465,6 +469,16 @@ function containerDir(name, home = monocerosHome()) {
|
|
|
465
469
|
function monocerosConfigPath(home = monocerosHome()) {
|
|
466
470
|
return path.join(home, "monoceros-config.yml");
|
|
467
471
|
}
|
|
472
|
+
function prettyPath(p) {
|
|
473
|
+
const home = os.homedir();
|
|
474
|
+
if (!home) return p;
|
|
475
|
+
if (p === home) return "~";
|
|
476
|
+
const prefix = home.endsWith(path.sep) ? home : home + path.sep;
|
|
477
|
+
if (p.startsWith(prefix)) {
|
|
478
|
+
return "~" + path.sep + p.slice(prefix.length);
|
|
479
|
+
}
|
|
480
|
+
return p;
|
|
481
|
+
}
|
|
468
482
|
|
|
469
483
|
// src/create/catalog.ts
|
|
470
484
|
var DEFAULT_BASE_IMAGE = "ghcr.io/getmonoceros/monoceros-runtime:1";
|
|
@@ -2311,7 +2325,7 @@ async function runApply(opts) {
|
|
|
2311
2325
|
});
|
|
2312
2326
|
}
|
|
2313
2327
|
logger.success(
|
|
2314
|
-
`Materialized config '${opts.name}' into ${targetDir}. Starting container\u2026`
|
|
2328
|
+
`Materialized config '${opts.name}' into ${prettyPath(targetDir)}. Starting container\u2026`
|
|
2315
2329
|
);
|
|
2316
2330
|
const exitCode = await runContainerCycle(targetDir, {
|
|
2317
2331
|
hasCompose: needsCompose(createOpts),
|
|
@@ -2362,7 +2376,7 @@ function warnOnDeprecatedFeatureRefs(containerFeatures, globalConfig, logger) {
|
|
|
2362
2376
|
}
|
|
2363
2377
|
|
|
2364
2378
|
// src/version.ts
|
|
2365
|
-
var CLI_VERSION = "1.
|
|
2379
|
+
var CLI_VERSION = "1.3.0";
|
|
2366
2380
|
|
|
2367
2381
|
// src/commands/_dispatch.ts
|
|
2368
2382
|
import { consola as consola11 } from "consola";
|
|
@@ -2451,7 +2465,7 @@ var COMMANDS_WITH_CONTAINER_ARG = [
|
|
|
2451
2465
|
"remove-from-url",
|
|
2452
2466
|
"remove-repo"
|
|
2453
2467
|
];
|
|
2454
|
-
var SHELLS = ["bash", "zsh"];
|
|
2468
|
+
var SHELLS = ["bash", "zsh", "pwsh"];
|
|
2455
2469
|
function renderCompletionScript(shell) {
|
|
2456
2470
|
const commands = ALL_COMMANDS.join(" ");
|
|
2457
2471
|
const containerCommandsRegex = COMMANDS_WITH_CONTAINER_ARG.join("|");
|
|
@@ -2492,6 +2506,54 @@ function renderCompletionScript(shell) {
|
|
|
2492
2506
|
""
|
|
2493
2507
|
].join("\n");
|
|
2494
2508
|
}
|
|
2509
|
+
if (shell === "pwsh") {
|
|
2510
|
+
return [
|
|
2511
|
+
"# PowerShell completion for monoceros",
|
|
2512
|
+
"# install: dot-source this file from your $PROFILE, e.g.",
|
|
2513
|
+
"# monoceros completion pwsh > $HOME/.config/monoceros/completion.ps1",
|
|
2514
|
+
"# Add-Content $PROFILE '. $HOME/.config/monoceros/completion.ps1'",
|
|
2515
|
+
"",
|
|
2516
|
+
"Register-ArgumentCompleter -Native -CommandName monoceros -ScriptBlock {",
|
|
2517
|
+
" param($wordToComplete, $commandAst, $cursorPosition)",
|
|
2518
|
+
"",
|
|
2519
|
+
" $commands = @(",
|
|
2520
|
+
...ALL_COMMANDS.map((c) => ` '${c}'`),
|
|
2521
|
+
" )",
|
|
2522
|
+
` $shells = @('${SHELLS.join("', '")}')`,
|
|
2523
|
+
" $containerCommands = @(",
|
|
2524
|
+
...COMMANDS_WITH_CONTAINER_ARG.map((c) => ` '${c}'`),
|
|
2525
|
+
" )",
|
|
2526
|
+
"",
|
|
2527
|
+
" $tokens = $commandAst.CommandElements",
|
|
2528
|
+
" $position = $tokens.Count",
|
|
2529
|
+
" if ($wordToComplete) { $position-- }",
|
|
2530
|
+
"",
|
|
2531
|
+
" if ($position -eq 1) {",
|
|
2532
|
+
' $commands | Where-Object { $_ -like "$wordToComplete*" } |',
|
|
2533
|
+
' ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, "ParameterValue", $_) }',
|
|
2534
|
+
" return",
|
|
2535
|
+
" }",
|
|
2536
|
+
"",
|
|
2537
|
+
" if ($position -eq 2) {",
|
|
2538
|
+
" $cmd = $tokens[1].Value",
|
|
2539
|
+
" if ($containerCommands -contains $cmd) {",
|
|
2540
|
+
' $home = if ($env:MONOCEROS_HOME) { $env:MONOCEROS_HOME } else { Join-Path $env:USERPROFILE ".monoceros" }',
|
|
2541
|
+
' $configsDir = Join-Path $home "container-configs"',
|
|
2542
|
+
" if (Test-Path $configsDir) {",
|
|
2543
|
+
' Get-ChildItem -Path $configsDir -Filter "*.yml" |',
|
|
2544
|
+
" ForEach-Object { $_.BaseName } |",
|
|
2545
|
+
' Where-Object { $_ -like "$wordToComplete*" } |',
|
|
2546
|
+
' ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, "ParameterValue", $_) }',
|
|
2547
|
+
" }",
|
|
2548
|
+
' } elseif ($cmd -eq "completion") {',
|
|
2549
|
+
' $shells | Where-Object { $_ -like "$wordToComplete*" } |',
|
|
2550
|
+
' ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, "ParameterValue", $_) }',
|
|
2551
|
+
" }",
|
|
2552
|
+
" }",
|
|
2553
|
+
"}",
|
|
2554
|
+
""
|
|
2555
|
+
].join("\n");
|
|
2556
|
+
}
|
|
2495
2557
|
return [
|
|
2496
2558
|
"#compdef monoceros",
|
|
2497
2559
|
"# zsh completion for monoceros",
|
|
@@ -2538,18 +2600,18 @@ var completionCommand = defineCommand8({
|
|
|
2538
2600
|
meta: {
|
|
2539
2601
|
name: "completion",
|
|
2540
2602
|
group: "tooling",
|
|
2541
|
-
description: "Print a shell completion script for bash or
|
|
2603
|
+
description: "Print a shell completion script for bash, zsh or PowerShell to stdout. Pipe the output into a file your shell loads at startup. The install scripts (install.sh / install.ps1) call this automatically."
|
|
2542
2604
|
},
|
|
2543
2605
|
args: {
|
|
2544
2606
|
shell: {
|
|
2545
2607
|
type: "positional",
|
|
2546
|
-
description: "Target shell. One of: 'bash', 'zsh'.",
|
|
2608
|
+
description: "Target shell. One of: 'bash', 'zsh', 'pwsh'.",
|
|
2547
2609
|
required: true
|
|
2548
2610
|
}
|
|
2549
2611
|
},
|
|
2550
2612
|
run({ args }) {
|
|
2551
2613
|
const shell = args.shell;
|
|
2552
|
-
if (shell !== "bash" && shell !== "zsh") {
|
|
2614
|
+
if (shell !== "bash" && shell !== "zsh" && shell !== "pwsh") {
|
|
2553
2615
|
process.stderr.write(
|
|
2554
2616
|
`Unknown shell: ${JSON.stringify(shell)}. Supported: ${SHELLS.join(", ")}.
|
|
2555
2617
|
`
|
|
@@ -2566,7 +2628,6 @@ import { consola as consola13 } from "consola";
|
|
|
2566
2628
|
|
|
2567
2629
|
// src/init/index.ts
|
|
2568
2630
|
import { existsSync as existsSync7, promises as fs10 } from "fs";
|
|
2569
|
-
import path10 from "path";
|
|
2570
2631
|
import { consola as consola12 } from "consola";
|
|
2571
2632
|
|
|
2572
2633
|
// src/init/components.ts
|
|
@@ -2765,11 +2826,10 @@ function generateComposedYml(name, components, lookupManifest) {
|
|
|
2765
2826
|
if (merged.features.length > 0) {
|
|
2766
2827
|
lines.push("features:");
|
|
2767
2828
|
for (const f of merged.features) {
|
|
2768
|
-
const hints = lookupManifest(f.ref)?.optionHints ?? [];
|
|
2769
2829
|
renderFeatureBlock(
|
|
2770
2830
|
lines,
|
|
2771
2831
|
f,
|
|
2772
|
-
|
|
2832
|
+
lookupManifest(f.ref),
|
|
2773
2833
|
/* commented */
|
|
2774
2834
|
false
|
|
2775
2835
|
);
|
|
@@ -2854,11 +2914,10 @@ function generateDocumentedYml(name, catalog, lookupManifest) {
|
|
|
2854
2914
|
for (const f of c.file.contributes.features ?? []) {
|
|
2855
2915
|
if (renderedRefs.has(f.ref)) continue;
|
|
2856
2916
|
renderedRefs.add(f.ref);
|
|
2857
|
-
const hints = lookupManifest(f.ref)?.optionHints ?? [];
|
|
2858
2917
|
renderFeatureBlock(
|
|
2859
2918
|
lines,
|
|
2860
2919
|
f,
|
|
2861
|
-
|
|
2920
|
+
lookupManifest(f.ref),
|
|
2862
2921
|
/* commented */
|
|
2863
2922
|
true
|
|
2864
2923
|
);
|
|
@@ -2869,11 +2928,10 @@ function generateDocumentedYml(name, catalog, lookupManifest) {
|
|
|
2869
2928
|
for (const f of c.file.contributes.features ?? []) {
|
|
2870
2929
|
if (renderedRefs.has(f.ref)) continue;
|
|
2871
2930
|
renderedRefs.add(f.ref);
|
|
2872
|
-
const hints = lookupManifest(f.ref)?.optionHints ?? [];
|
|
2873
2931
|
renderFeatureBlock(
|
|
2874
2932
|
lines,
|
|
2875
2933
|
f,
|
|
2876
|
-
|
|
2934
|
+
lookupManifest(f.ref),
|
|
2877
2935
|
/* commented */
|
|
2878
2936
|
true
|
|
2879
2937
|
);
|
|
@@ -2883,8 +2941,21 @@ function generateDocumentedYml(name, catalog, lookupManifest) {
|
|
|
2883
2941
|
}
|
|
2884
2942
|
return ensureTrailingNewline(lines.join("\n"));
|
|
2885
2943
|
}
|
|
2886
|
-
|
|
2944
|
+
var COMMENT_WIDTH = 72;
|
|
2945
|
+
function renderFeatureBlock(out, feature, summary, commented) {
|
|
2887
2946
|
const c = commented ? "# " : " ";
|
|
2947
|
+
const optionHints = summary?.optionHints ?? [];
|
|
2948
|
+
const optionDescriptions = summary?.optionDescriptions ?? {};
|
|
2949
|
+
const usageNotes = summary?.usageNotes ?? [];
|
|
2950
|
+
for (let i = 0; i < usageNotes.length; i++) {
|
|
2951
|
+
if (i > 0) out.push(`${c}#`);
|
|
2952
|
+
for (const line of wrapToComment(
|
|
2953
|
+
usageNotes[i],
|
|
2954
|
+
COMMENT_WIDTH - c.length
|
|
2955
|
+
)) {
|
|
2956
|
+
out.push(`${c}# ${line}`);
|
|
2957
|
+
}
|
|
2958
|
+
}
|
|
2888
2959
|
out.push(`${c}- ref: ${feature.ref}`);
|
|
2889
2960
|
const options = feature.options ?? {};
|
|
2890
2961
|
const activeOptions = Object.entries(options);
|
|
@@ -2899,7 +2970,7 @@ function renderFeatureBlock(out, feature, optionHints, commented) {
|
|
|
2899
2970
|
`${c} # Optional \u2014 override monoceros-config.yml defaults.features:`
|
|
2900
2971
|
);
|
|
2901
2972
|
for (const hint of remainingHints) {
|
|
2902
|
-
out
|
|
2973
|
+
emitHint(out, hint, optionDescriptions[hint], `${c} `);
|
|
2903
2974
|
}
|
|
2904
2975
|
}
|
|
2905
2976
|
} else if (remainingHints.length > 0) {
|
|
@@ -2908,10 +2979,42 @@ function renderFeatureBlock(out, feature, optionHints, commented) {
|
|
|
2908
2979
|
);
|
|
2909
2980
|
out.push(`${c} # options:`);
|
|
2910
2981
|
for (const hint of remainingHints) {
|
|
2911
|
-
out
|
|
2982
|
+
emitHint(out, hint, optionDescriptions[hint], `${c} # `);
|
|
2912
2983
|
}
|
|
2913
2984
|
}
|
|
2914
2985
|
}
|
|
2986
|
+
function emitHint(out, hint, description, linePrefix) {
|
|
2987
|
+
if (description) {
|
|
2988
|
+
for (const line of wrapToComment(
|
|
2989
|
+
description,
|
|
2990
|
+
COMMENT_WIDTH - linePrefix.length
|
|
2991
|
+
)) {
|
|
2992
|
+
out.push(`${linePrefix}# ${line}`);
|
|
2993
|
+
}
|
|
2994
|
+
}
|
|
2995
|
+
out.push(`${linePrefix}${hint}:`);
|
|
2996
|
+
}
|
|
2997
|
+
function wrapToComment(text, width) {
|
|
2998
|
+
const words = text.split(/\s+/).filter((w) => w.length > 0);
|
|
2999
|
+
if (words.length === 0) return [""];
|
|
3000
|
+
const usable = Math.max(width, 20);
|
|
3001
|
+
const lines = [];
|
|
3002
|
+
let current = "";
|
|
3003
|
+
for (const w of words) {
|
|
3004
|
+
if (current.length === 0) {
|
|
3005
|
+
current = w;
|
|
3006
|
+
continue;
|
|
3007
|
+
}
|
|
3008
|
+
if (current.length + 1 + w.length <= usable) {
|
|
3009
|
+
current += " " + w;
|
|
3010
|
+
} else {
|
|
3011
|
+
lines.push(current);
|
|
3012
|
+
current = w;
|
|
3013
|
+
}
|
|
3014
|
+
}
|
|
3015
|
+
if (current.length > 0) lines.push(current);
|
|
3016
|
+
return lines;
|
|
3017
|
+
}
|
|
2915
3018
|
function renderScalarValue(value) {
|
|
2916
3019
|
if (typeof value === "string") {
|
|
2917
3020
|
return /^[A-Za-z_][A-Za-z0-9._-]*$/.test(value) ? value : JSON.stringify(value);
|
|
@@ -2939,28 +3042,50 @@ function ensureTrailingNewline(s) {
|
|
|
2939
3042
|
// src/init/manifest.ts
|
|
2940
3043
|
import { existsSync as existsSync6, readFileSync as readFileSync3 } from "fs";
|
|
2941
3044
|
import path9 from "path";
|
|
2942
|
-
function
|
|
2943
|
-
if (
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
3045
|
+
function resolveManifestPath(name, checkoutRoot) {
|
|
3046
|
+
if (checkoutRoot) {
|
|
3047
|
+
const checkoutPath = path9.join(
|
|
3048
|
+
checkoutRoot,
|
|
3049
|
+
"images",
|
|
3050
|
+
"features",
|
|
3051
|
+
name,
|
|
3052
|
+
"devcontainer-feature.json"
|
|
3053
|
+
);
|
|
3054
|
+
if (existsSync6(checkoutPath)) return checkoutPath;
|
|
3055
|
+
}
|
|
3056
|
+
const bundlePath = path9.join(
|
|
3057
|
+
bundledFeaturesDir(),
|
|
2951
3058
|
name,
|
|
2952
3059
|
"devcontainer-feature.json"
|
|
2953
3060
|
);
|
|
2954
|
-
if (
|
|
3061
|
+
if (existsSync6(bundlePath)) return bundlePath;
|
|
3062
|
+
return null;
|
|
3063
|
+
}
|
|
3064
|
+
function loadFeatureManifestSummary(ref, checkoutRoot = workbenchCheckoutRoot()) {
|
|
3065
|
+
const match = matchMonocerosFeature(ref);
|
|
3066
|
+
if (!match) return void 0;
|
|
3067
|
+
const manifestPath = resolveManifestPath(match.name, checkoutRoot);
|
|
3068
|
+
if (!manifestPath) return void 0;
|
|
2955
3069
|
try {
|
|
2956
3070
|
const text = readFileSync3(manifestPath, "utf8");
|
|
2957
3071
|
const parsed = JSON.parse(text);
|
|
2958
|
-
const
|
|
2959
|
-
|
|
2960
|
-
const hints = raw.filter(
|
|
3072
|
+
const rawHints = parsed["x-monoceros"]?.optionHints;
|
|
3073
|
+
const optionHints = Array.isArray(rawHints) ? rawHints.filter(
|
|
2961
3074
|
(x) => typeof x === "string" && x.length > 0
|
|
2962
|
-
);
|
|
2963
|
-
|
|
3075
|
+
) : [];
|
|
3076
|
+
const rawNotes = parsed["x-monoceros"]?.usageNotes;
|
|
3077
|
+
const usageNotes = Array.isArray(rawNotes) ? rawNotes.filter(
|
|
3078
|
+
(x) => typeof x === "string" && x.length > 0
|
|
3079
|
+
) : [];
|
|
3080
|
+
const optionDescriptions = {};
|
|
3081
|
+
if (parsed.options) {
|
|
3082
|
+
for (const [key, opt] of Object.entries(parsed.options)) {
|
|
3083
|
+
if (opt && typeof opt === "object" && typeof opt.description === "string" && opt.description.length > 0) {
|
|
3084
|
+
optionDescriptions[key] = opt.description;
|
|
3085
|
+
}
|
|
3086
|
+
}
|
|
3087
|
+
}
|
|
3088
|
+
return { optionHints, optionDescriptions, usageNotes };
|
|
2964
3089
|
} catch {
|
|
2965
3090
|
return void 0;
|
|
2966
3091
|
}
|
|
@@ -3004,14 +3129,14 @@ async function runInit(opts) {
|
|
|
3004
3129
|
await fs10.mkdir(containerConfigsDir(home), { recursive: true });
|
|
3005
3130
|
await fs10.writeFile(dest, text, "utf8");
|
|
3006
3131
|
const documented = requested.length === 0;
|
|
3007
|
-
const
|
|
3132
|
+
const displayPath = prettyPath(dest);
|
|
3008
3133
|
if (documented) {
|
|
3009
3134
|
logger.success(
|
|
3010
|
-
`Wrote documented default to ${
|
|
3135
|
+
`Wrote documented default to ${displayPath}. Un-comment what you need, then \`monoceros apply ${opts.name}\`.`
|
|
3011
3136
|
);
|
|
3012
3137
|
} else {
|
|
3013
3138
|
logger.success(
|
|
3014
|
-
`Composed ${requested.length} component(s) into ${
|
|
3139
|
+
`Composed ${requested.length} component(s) into ${displayPath}: ${requested.join(", ")}`
|
|
3015
3140
|
);
|
|
3016
3141
|
logger.info(
|
|
3017
3142
|
`Edit the file if you need to tweak, then \`monoceros apply ${opts.name}\`.`
|
|
@@ -3248,7 +3373,7 @@ import { createInterface } from "readline/promises";
|
|
|
3248
3373
|
|
|
3249
3374
|
// src/remove/index.ts
|
|
3250
3375
|
import { existsSync as existsSync8, promises as fs11 } from "fs";
|
|
3251
|
-
import
|
|
3376
|
+
import path10 from "path";
|
|
3252
3377
|
import { consola as consola17 } from "consola";
|
|
3253
3378
|
async function runRemove(opts) {
|
|
3254
3379
|
const home = opts.monocerosHome ?? monocerosHome();
|
|
@@ -3291,19 +3416,17 @@ async function runRemove(opts) {
|
|
|
3291
3416
|
let backupPath = null;
|
|
3292
3417
|
if (!opts.noBackup && (hasYml || hasContainer)) {
|
|
3293
3418
|
const ts = (opts.now ?? /* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
3294
|
-
backupPath =
|
|
3419
|
+
backupPath = path10.join(home, "container-backups", `${opts.name}-${ts}`);
|
|
3295
3420
|
await fs11.mkdir(backupPath, { recursive: true });
|
|
3296
3421
|
if (hasYml) {
|
|
3297
|
-
await fs11.copyFile(ymlPath,
|
|
3422
|
+
await fs11.copyFile(ymlPath, path10.join(backupPath, `${opts.name}.yml`));
|
|
3298
3423
|
}
|
|
3299
3424
|
if (hasContainer) {
|
|
3300
|
-
await fs11.cp(containerPath,
|
|
3425
|
+
await fs11.cp(containerPath, path10.join(backupPath, "container"), {
|
|
3301
3426
|
recursive: true
|
|
3302
3427
|
});
|
|
3303
3428
|
}
|
|
3304
|
-
logger.info(
|
|
3305
|
-
`Backup written to ${path11.relative(home, backupPath) || backupPath}.`
|
|
3306
|
-
);
|
|
3429
|
+
logger.info(`Backup written to ${prettyPath(backupPath)}.`);
|
|
3307
3430
|
}
|
|
3308
3431
|
if (hasYml) {
|
|
3309
3432
|
await fs11.rm(ymlPath, { force: true });
|
|
@@ -3394,7 +3517,7 @@ import { consola as consola20 } from "consola";
|
|
|
3394
3517
|
|
|
3395
3518
|
// src/restore/index.ts
|
|
3396
3519
|
import { existsSync as existsSync9, promises as fs12 } from "fs";
|
|
3397
|
-
import
|
|
3520
|
+
import path11 from "path";
|
|
3398
3521
|
import { consola as consola19 } from "consola";
|
|
3399
3522
|
async function runRestore(opts) {
|
|
3400
3523
|
const home = opts.monocerosHome ?? monocerosHome();
|
|
@@ -3402,7 +3525,7 @@ async function runRestore(opts) {
|
|
|
3402
3525
|
info: (msg) => consola19.info(msg),
|
|
3403
3526
|
success: (msg) => consola19.success(msg)
|
|
3404
3527
|
};
|
|
3405
|
-
const backup =
|
|
3528
|
+
const backup = path11.resolve(opts.backupPath);
|
|
3406
3529
|
if (!existsSync9(backup)) {
|
|
3407
3530
|
throw new Error(`Backup not found: ${backup}.`);
|
|
3408
3531
|
}
|
|
@@ -3424,7 +3547,7 @@ async function runRestore(opts) {
|
|
|
3424
3547
|
}
|
|
3425
3548
|
const ymlFile = ymlFiles[0];
|
|
3426
3549
|
const name = ymlFile.replace(/\.yml$/, "");
|
|
3427
|
-
const containerInBackup =
|
|
3550
|
+
const containerInBackup = path11.join(backup, "container");
|
|
3428
3551
|
const hasContainer = existsSync9(containerInBackup);
|
|
3429
3552
|
const destYml = containerConfigPath(name, home);
|
|
3430
3553
|
const destContainer = containerDir(name, home);
|
|
@@ -3439,13 +3562,11 @@ async function runRestore(opts) {
|
|
|
3439
3562
|
);
|
|
3440
3563
|
}
|
|
3441
3564
|
await fs12.mkdir(containerConfigsDir(home), { recursive: true });
|
|
3442
|
-
await fs12.copyFile(
|
|
3565
|
+
await fs12.copyFile(path11.join(backup, ymlFile), destYml);
|
|
3443
3566
|
if (hasContainer) {
|
|
3444
3567
|
await fs12.cp(containerInBackup, destContainer, { recursive: true });
|
|
3445
3568
|
}
|
|
3446
|
-
logger.success(
|
|
3447
|
-
`Restored '${name}' from ${path12.relative(home, backup) || backup}.`
|
|
3448
|
-
);
|
|
3569
|
+
logger.success(`Restored '${name}' from ${prettyPath(backup)}.`);
|
|
3449
3570
|
logger.info(
|
|
3450
3571
|
`Run \`monoceros apply ${name}\` to bring the container back up.`
|
|
3451
3572
|
);
|
|
@@ -3654,7 +3775,7 @@ import { consola as consola25 } from "consola";
|
|
|
3654
3775
|
|
|
3655
3776
|
// src/devcontainer/shell.ts
|
|
3656
3777
|
import { existsSync as existsSync10 } from "fs";
|
|
3657
|
-
import
|
|
3778
|
+
import path12 from "path";
|
|
3658
3779
|
async function runShell(opts) {
|
|
3659
3780
|
assertContainerExists(opts.root);
|
|
3660
3781
|
const spawnFn = opts.spawn ?? spawnDevcontainer;
|
|
@@ -3669,7 +3790,7 @@ async function runShell(opts) {
|
|
|
3669
3790
|
});
|
|
3670
3791
|
}
|
|
3671
3792
|
function assertContainerExists(root) {
|
|
3672
|
-
if (!existsSync10(
|
|
3793
|
+
if (!existsSync10(path12.join(root, ".devcontainer"))) {
|
|
3673
3794
|
throw new Error(
|
|
3674
3795
|
`No .devcontainer/ at ${root}. Run \`monoceros apply <name>\` first.`
|
|
3675
3796
|
);
|