@getmonoceros/workbench 1.2.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
|
@@ -203,25 +203,25 @@ function detectHelpRequest(argv, main2) {
|
|
|
203
203
|
const separatorIdx = argv.indexOf("--");
|
|
204
204
|
if (helpIdx === -1) return null;
|
|
205
205
|
if (separatorIdx !== -1 && separatorIdx < helpIdx) return null;
|
|
206
|
-
const
|
|
206
|
+
const path13 = [];
|
|
207
207
|
const tokens = argv.slice(
|
|
208
208
|
0,
|
|
209
209
|
separatorIdx === -1 ? argv.length : separatorIdx
|
|
210
210
|
);
|
|
211
211
|
let cursor = main2;
|
|
212
212
|
const mainName = (main2.meta ?? {}).name ?? "monoceros";
|
|
213
|
-
|
|
213
|
+
path13.push(mainName);
|
|
214
214
|
for (const tok of tokens) {
|
|
215
215
|
if (tok.startsWith("-")) continue;
|
|
216
216
|
const subs = cursor.subCommands ?? {};
|
|
217
217
|
if (tok in subs) {
|
|
218
218
|
cursor = subs[tok];
|
|
219
|
-
|
|
219
|
+
path13.push(tok);
|
|
220
220
|
continue;
|
|
221
221
|
}
|
|
222
222
|
break;
|
|
223
223
|
}
|
|
224
|
-
return { path:
|
|
224
|
+
return { path: path13, cmd: cursor };
|
|
225
225
|
}
|
|
226
226
|
async function maybeRenderHelp(argv, main2) {
|
|
227
227
|
const hit = detectHelpRequest(argv, main2);
|
|
@@ -451,6 +451,9 @@ function workbenchCheckoutRoot() {
|
|
|
451
451
|
function componentsDir(root = workbenchRoot()) {
|
|
452
452
|
return path.join(root, "templates", "components");
|
|
453
453
|
}
|
|
454
|
+
function bundledFeaturesDir(root = workbenchRoot()) {
|
|
455
|
+
return path.join(root, "features");
|
|
456
|
+
}
|
|
454
457
|
function containerConfigsDir(home = monocerosHome()) {
|
|
455
458
|
return path.join(home, "container-configs");
|
|
456
459
|
}
|
|
@@ -466,6 +469,16 @@ function containerDir(name, home = monocerosHome()) {
|
|
|
466
469
|
function monocerosConfigPath(home = monocerosHome()) {
|
|
467
470
|
return path.join(home, "monoceros-config.yml");
|
|
468
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
|
+
}
|
|
469
482
|
|
|
470
483
|
// src/create/catalog.ts
|
|
471
484
|
var DEFAULT_BASE_IMAGE = "ghcr.io/getmonoceros/monoceros-runtime:1";
|
|
@@ -2312,7 +2325,7 @@ async function runApply(opts) {
|
|
|
2312
2325
|
});
|
|
2313
2326
|
}
|
|
2314
2327
|
logger.success(
|
|
2315
|
-
`Materialized config '${opts.name}' into ${targetDir}. Starting container\u2026`
|
|
2328
|
+
`Materialized config '${opts.name}' into ${prettyPath(targetDir)}. Starting container\u2026`
|
|
2316
2329
|
);
|
|
2317
2330
|
const exitCode = await runContainerCycle(targetDir, {
|
|
2318
2331
|
hasCompose: needsCompose(createOpts),
|
|
@@ -2363,7 +2376,7 @@ function warnOnDeprecatedFeatureRefs(containerFeatures, globalConfig, logger) {
|
|
|
2363
2376
|
}
|
|
2364
2377
|
|
|
2365
2378
|
// src/version.ts
|
|
2366
|
-
var CLI_VERSION = "1.
|
|
2379
|
+
var CLI_VERSION = "1.3.0";
|
|
2367
2380
|
|
|
2368
2381
|
// src/commands/_dispatch.ts
|
|
2369
2382
|
import { consola as consola11 } from "consola";
|
|
@@ -2615,7 +2628,6 @@ import { consola as consola13 } from "consola";
|
|
|
2615
2628
|
|
|
2616
2629
|
// src/init/index.ts
|
|
2617
2630
|
import { existsSync as existsSync7, promises as fs10 } from "fs";
|
|
2618
|
-
import path10 from "path";
|
|
2619
2631
|
import { consola as consola12 } from "consola";
|
|
2620
2632
|
|
|
2621
2633
|
// src/init/components.ts
|
|
@@ -2814,11 +2826,10 @@ function generateComposedYml(name, components, lookupManifest) {
|
|
|
2814
2826
|
if (merged.features.length > 0) {
|
|
2815
2827
|
lines.push("features:");
|
|
2816
2828
|
for (const f of merged.features) {
|
|
2817
|
-
const hints = lookupManifest(f.ref)?.optionHints ?? [];
|
|
2818
2829
|
renderFeatureBlock(
|
|
2819
2830
|
lines,
|
|
2820
2831
|
f,
|
|
2821
|
-
|
|
2832
|
+
lookupManifest(f.ref),
|
|
2822
2833
|
/* commented */
|
|
2823
2834
|
false
|
|
2824
2835
|
);
|
|
@@ -2903,11 +2914,10 @@ function generateDocumentedYml(name, catalog, lookupManifest) {
|
|
|
2903
2914
|
for (const f of c.file.contributes.features ?? []) {
|
|
2904
2915
|
if (renderedRefs.has(f.ref)) continue;
|
|
2905
2916
|
renderedRefs.add(f.ref);
|
|
2906
|
-
const hints = lookupManifest(f.ref)?.optionHints ?? [];
|
|
2907
2917
|
renderFeatureBlock(
|
|
2908
2918
|
lines,
|
|
2909
2919
|
f,
|
|
2910
|
-
|
|
2920
|
+
lookupManifest(f.ref),
|
|
2911
2921
|
/* commented */
|
|
2912
2922
|
true
|
|
2913
2923
|
);
|
|
@@ -2918,11 +2928,10 @@ function generateDocumentedYml(name, catalog, lookupManifest) {
|
|
|
2918
2928
|
for (const f of c.file.contributes.features ?? []) {
|
|
2919
2929
|
if (renderedRefs.has(f.ref)) continue;
|
|
2920
2930
|
renderedRefs.add(f.ref);
|
|
2921
|
-
const hints = lookupManifest(f.ref)?.optionHints ?? [];
|
|
2922
2931
|
renderFeatureBlock(
|
|
2923
2932
|
lines,
|
|
2924
2933
|
f,
|
|
2925
|
-
|
|
2934
|
+
lookupManifest(f.ref),
|
|
2926
2935
|
/* commented */
|
|
2927
2936
|
true
|
|
2928
2937
|
);
|
|
@@ -2932,8 +2941,21 @@ function generateDocumentedYml(name, catalog, lookupManifest) {
|
|
|
2932
2941
|
}
|
|
2933
2942
|
return ensureTrailingNewline(lines.join("\n"));
|
|
2934
2943
|
}
|
|
2935
|
-
|
|
2944
|
+
var COMMENT_WIDTH = 72;
|
|
2945
|
+
function renderFeatureBlock(out, feature, summary, commented) {
|
|
2936
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
|
+
}
|
|
2937
2959
|
out.push(`${c}- ref: ${feature.ref}`);
|
|
2938
2960
|
const options = feature.options ?? {};
|
|
2939
2961
|
const activeOptions = Object.entries(options);
|
|
@@ -2948,7 +2970,7 @@ function renderFeatureBlock(out, feature, optionHints, commented) {
|
|
|
2948
2970
|
`${c} # Optional \u2014 override monoceros-config.yml defaults.features:`
|
|
2949
2971
|
);
|
|
2950
2972
|
for (const hint of remainingHints) {
|
|
2951
|
-
out
|
|
2973
|
+
emitHint(out, hint, optionDescriptions[hint], `${c} `);
|
|
2952
2974
|
}
|
|
2953
2975
|
}
|
|
2954
2976
|
} else if (remainingHints.length > 0) {
|
|
@@ -2957,10 +2979,42 @@ function renderFeatureBlock(out, feature, optionHints, commented) {
|
|
|
2957
2979
|
);
|
|
2958
2980
|
out.push(`${c} # options:`);
|
|
2959
2981
|
for (const hint of remainingHints) {
|
|
2960
|
-
out
|
|
2982
|
+
emitHint(out, hint, optionDescriptions[hint], `${c} # `);
|
|
2961
2983
|
}
|
|
2962
2984
|
}
|
|
2963
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
|
+
}
|
|
2964
3018
|
function renderScalarValue(value) {
|
|
2965
3019
|
if (typeof value === "string") {
|
|
2966
3020
|
return /^[A-Za-z_][A-Za-z0-9._-]*$/.test(value) ? value : JSON.stringify(value);
|
|
@@ -2988,28 +3042,50 @@ function ensureTrailingNewline(s) {
|
|
|
2988
3042
|
// src/init/manifest.ts
|
|
2989
3043
|
import { existsSync as existsSync6, readFileSync as readFileSync3 } from "fs";
|
|
2990
3044
|
import path9 from "path";
|
|
2991
|
-
function
|
|
2992
|
-
if (
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
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(),
|
|
3000
3058
|
name,
|
|
3001
3059
|
"devcontainer-feature.json"
|
|
3002
3060
|
);
|
|
3003
|
-
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;
|
|
3004
3069
|
try {
|
|
3005
3070
|
const text = readFileSync3(manifestPath, "utf8");
|
|
3006
3071
|
const parsed = JSON.parse(text);
|
|
3007
|
-
const
|
|
3008
|
-
|
|
3009
|
-
const hints = raw.filter(
|
|
3072
|
+
const rawHints = parsed["x-monoceros"]?.optionHints;
|
|
3073
|
+
const optionHints = Array.isArray(rawHints) ? rawHints.filter(
|
|
3010
3074
|
(x) => typeof x === "string" && x.length > 0
|
|
3011
|
-
);
|
|
3012
|
-
|
|
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 };
|
|
3013
3089
|
} catch {
|
|
3014
3090
|
return void 0;
|
|
3015
3091
|
}
|
|
@@ -3053,14 +3129,14 @@ async function runInit(opts) {
|
|
|
3053
3129
|
await fs10.mkdir(containerConfigsDir(home), { recursive: true });
|
|
3054
3130
|
await fs10.writeFile(dest, text, "utf8");
|
|
3055
3131
|
const documented = requested.length === 0;
|
|
3056
|
-
const
|
|
3132
|
+
const displayPath = prettyPath(dest);
|
|
3057
3133
|
if (documented) {
|
|
3058
3134
|
logger.success(
|
|
3059
|
-
`Wrote documented default to ${
|
|
3135
|
+
`Wrote documented default to ${displayPath}. Un-comment what you need, then \`monoceros apply ${opts.name}\`.`
|
|
3060
3136
|
);
|
|
3061
3137
|
} else {
|
|
3062
3138
|
logger.success(
|
|
3063
|
-
`Composed ${requested.length} component(s) into ${
|
|
3139
|
+
`Composed ${requested.length} component(s) into ${displayPath}: ${requested.join(", ")}`
|
|
3064
3140
|
);
|
|
3065
3141
|
logger.info(
|
|
3066
3142
|
`Edit the file if you need to tweak, then \`monoceros apply ${opts.name}\`.`
|
|
@@ -3297,7 +3373,7 @@ import { createInterface } from "readline/promises";
|
|
|
3297
3373
|
|
|
3298
3374
|
// src/remove/index.ts
|
|
3299
3375
|
import { existsSync as existsSync8, promises as fs11 } from "fs";
|
|
3300
|
-
import
|
|
3376
|
+
import path10 from "path";
|
|
3301
3377
|
import { consola as consola17 } from "consola";
|
|
3302
3378
|
async function runRemove(opts) {
|
|
3303
3379
|
const home = opts.monocerosHome ?? monocerosHome();
|
|
@@ -3340,19 +3416,17 @@ async function runRemove(opts) {
|
|
|
3340
3416
|
let backupPath = null;
|
|
3341
3417
|
if (!opts.noBackup && (hasYml || hasContainer)) {
|
|
3342
3418
|
const ts = (opts.now ?? /* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
3343
|
-
backupPath =
|
|
3419
|
+
backupPath = path10.join(home, "container-backups", `${opts.name}-${ts}`);
|
|
3344
3420
|
await fs11.mkdir(backupPath, { recursive: true });
|
|
3345
3421
|
if (hasYml) {
|
|
3346
|
-
await fs11.copyFile(ymlPath,
|
|
3422
|
+
await fs11.copyFile(ymlPath, path10.join(backupPath, `${opts.name}.yml`));
|
|
3347
3423
|
}
|
|
3348
3424
|
if (hasContainer) {
|
|
3349
|
-
await fs11.cp(containerPath,
|
|
3425
|
+
await fs11.cp(containerPath, path10.join(backupPath, "container"), {
|
|
3350
3426
|
recursive: true
|
|
3351
3427
|
});
|
|
3352
3428
|
}
|
|
3353
|
-
logger.info(
|
|
3354
|
-
`Backup written to ${path11.relative(home, backupPath) || backupPath}.`
|
|
3355
|
-
);
|
|
3429
|
+
logger.info(`Backup written to ${prettyPath(backupPath)}.`);
|
|
3356
3430
|
}
|
|
3357
3431
|
if (hasYml) {
|
|
3358
3432
|
await fs11.rm(ymlPath, { force: true });
|
|
@@ -3443,7 +3517,7 @@ import { consola as consola20 } from "consola";
|
|
|
3443
3517
|
|
|
3444
3518
|
// src/restore/index.ts
|
|
3445
3519
|
import { existsSync as existsSync9, promises as fs12 } from "fs";
|
|
3446
|
-
import
|
|
3520
|
+
import path11 from "path";
|
|
3447
3521
|
import { consola as consola19 } from "consola";
|
|
3448
3522
|
async function runRestore(opts) {
|
|
3449
3523
|
const home = opts.monocerosHome ?? monocerosHome();
|
|
@@ -3451,7 +3525,7 @@ async function runRestore(opts) {
|
|
|
3451
3525
|
info: (msg) => consola19.info(msg),
|
|
3452
3526
|
success: (msg) => consola19.success(msg)
|
|
3453
3527
|
};
|
|
3454
|
-
const backup =
|
|
3528
|
+
const backup = path11.resolve(opts.backupPath);
|
|
3455
3529
|
if (!existsSync9(backup)) {
|
|
3456
3530
|
throw new Error(`Backup not found: ${backup}.`);
|
|
3457
3531
|
}
|
|
@@ -3473,7 +3547,7 @@ async function runRestore(opts) {
|
|
|
3473
3547
|
}
|
|
3474
3548
|
const ymlFile = ymlFiles[0];
|
|
3475
3549
|
const name = ymlFile.replace(/\.yml$/, "");
|
|
3476
|
-
const containerInBackup =
|
|
3550
|
+
const containerInBackup = path11.join(backup, "container");
|
|
3477
3551
|
const hasContainer = existsSync9(containerInBackup);
|
|
3478
3552
|
const destYml = containerConfigPath(name, home);
|
|
3479
3553
|
const destContainer = containerDir(name, home);
|
|
@@ -3488,13 +3562,11 @@ async function runRestore(opts) {
|
|
|
3488
3562
|
);
|
|
3489
3563
|
}
|
|
3490
3564
|
await fs12.mkdir(containerConfigsDir(home), { recursive: true });
|
|
3491
|
-
await fs12.copyFile(
|
|
3565
|
+
await fs12.copyFile(path11.join(backup, ymlFile), destYml);
|
|
3492
3566
|
if (hasContainer) {
|
|
3493
3567
|
await fs12.cp(containerInBackup, destContainer, { recursive: true });
|
|
3494
3568
|
}
|
|
3495
|
-
logger.success(
|
|
3496
|
-
`Restored '${name}' from ${path12.relative(home, backup) || backup}.`
|
|
3497
|
-
);
|
|
3569
|
+
logger.success(`Restored '${name}' from ${prettyPath(backup)}.`);
|
|
3498
3570
|
logger.info(
|
|
3499
3571
|
`Run \`monoceros apply ${name}\` to bring the container back up.`
|
|
3500
3572
|
);
|
|
@@ -3703,7 +3775,7 @@ import { consola as consola25 } from "consola";
|
|
|
3703
3775
|
|
|
3704
3776
|
// src/devcontainer/shell.ts
|
|
3705
3777
|
import { existsSync as existsSync10 } from "fs";
|
|
3706
|
-
import
|
|
3778
|
+
import path12 from "path";
|
|
3707
3779
|
async function runShell(opts) {
|
|
3708
3780
|
assertContainerExists(opts.root);
|
|
3709
3781
|
const spawnFn = opts.spawn ?? spawnDevcontainer;
|
|
@@ -3718,7 +3790,7 @@ async function runShell(opts) {
|
|
|
3718
3790
|
});
|
|
3719
3791
|
}
|
|
3720
3792
|
function assertContainerExists(root) {
|
|
3721
|
-
if (!existsSync10(
|
|
3793
|
+
if (!existsSync10(path12.join(root, ".devcontainer"))) {
|
|
3722
3794
|
throw new Error(
|
|
3723
3795
|
`No .devcontainer/ at ${root}. Run \`monoceros apply <name>\` first.`
|
|
3724
3796
|
);
|