@envmanager-cli/cli 0.1.9 → 0.2.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/dist/bin/envmanager.js +226 -33
- package/dist/bin/envmanager.js.map +1 -1
- package/dist/index.d.ts +12 -0
- package/dist/index.js +19 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/bin/envmanager.js
CHANGED
|
@@ -464,6 +464,147 @@ import { resolve } from "path";
|
|
|
464
464
|
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
465
465
|
import { join as join2, dirname } from "path";
|
|
466
466
|
import { z } from "zod";
|
|
467
|
+
|
|
468
|
+
// src/lib/formatters.ts
|
|
469
|
+
import * as yaml from "js-yaml";
|
|
470
|
+
var EXPORT_FORMATS = [
|
|
471
|
+
"dotenv",
|
|
472
|
+
"docker-compose",
|
|
473
|
+
"k8s-secret",
|
|
474
|
+
"k8s-configmap",
|
|
475
|
+
"vercel",
|
|
476
|
+
"railway",
|
|
477
|
+
"render"
|
|
478
|
+
];
|
|
479
|
+
function sanitizeK8sName(name) {
|
|
480
|
+
return name.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/^-+|-+$/g, "").substring(0, 63);
|
|
481
|
+
}
|
|
482
|
+
function toDotEnv(variables) {
|
|
483
|
+
return variables.map((v) => {
|
|
484
|
+
const needsQuotes = /[\s='"#\n\r]/.test(v.value);
|
|
485
|
+
const value = needsQuotes ? `"${v.value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n")}"` : v.value;
|
|
486
|
+
return `${v.key}=${value}`;
|
|
487
|
+
}).join("\n");
|
|
488
|
+
}
|
|
489
|
+
function toDockerCompose(variables) {
|
|
490
|
+
const envData = {};
|
|
491
|
+
for (const v of variables) {
|
|
492
|
+
envData[v.key] = v.value;
|
|
493
|
+
}
|
|
494
|
+
const snippet = {
|
|
495
|
+
services: {
|
|
496
|
+
app: {
|
|
497
|
+
environment: envData
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
};
|
|
501
|
+
const yamlOutput = yaml.dump(snippet, { lineWidth: -1, quotingType: '"' });
|
|
502
|
+
return `# Docker Compose environment section
|
|
503
|
+
# Copy this into your docker-compose.yml and replace 'app' with your service name
|
|
504
|
+
${yamlOutput}`;
|
|
505
|
+
}
|
|
506
|
+
function toKubernetesSecret(variables, config) {
|
|
507
|
+
const data = {};
|
|
508
|
+
for (const v of variables) {
|
|
509
|
+
data[v.key] = Buffer.from(v.value).toString("base64");
|
|
510
|
+
}
|
|
511
|
+
const manifest = {
|
|
512
|
+
apiVersion: "v1",
|
|
513
|
+
kind: "Secret",
|
|
514
|
+
metadata: {
|
|
515
|
+
name: config.name,
|
|
516
|
+
namespace: config.namespace
|
|
517
|
+
},
|
|
518
|
+
type: "Opaque",
|
|
519
|
+
data
|
|
520
|
+
};
|
|
521
|
+
return yaml.dump(manifest, { lineWidth: -1, quotingType: '"' });
|
|
522
|
+
}
|
|
523
|
+
function toKubernetesConfigMap(variables, config) {
|
|
524
|
+
const data = {};
|
|
525
|
+
for (const v of variables) {
|
|
526
|
+
data[v.key] = v.value;
|
|
527
|
+
}
|
|
528
|
+
const manifest = {
|
|
529
|
+
apiVersion: "v1",
|
|
530
|
+
kind: "ConfigMap",
|
|
531
|
+
metadata: {
|
|
532
|
+
name: config.name,
|
|
533
|
+
namespace: config.namespace
|
|
534
|
+
},
|
|
535
|
+
data
|
|
536
|
+
};
|
|
537
|
+
return yaml.dump(manifest, { lineWidth: -1, quotingType: '"' });
|
|
538
|
+
}
|
|
539
|
+
function toVercelCLI(variables) {
|
|
540
|
+
if (variables.length === 0) return "# No variables to export";
|
|
541
|
+
const header = `# Vercel CLI commands to add environment variables
|
|
542
|
+
# Run these commands in your project directory
|
|
543
|
+
# Docs: https://vercel.com/docs/cli/env
|
|
544
|
+
# Note: You may need to run 'vercel login' first
|
|
545
|
+
|
|
546
|
+
`;
|
|
547
|
+
const commands = variables.map((v) => {
|
|
548
|
+
if (v.value.includes("\n")) {
|
|
549
|
+
const escapedValue = v.value.replace(/'/g, "'\\''");
|
|
550
|
+
return `vercel env add ${v.key} production << 'EOF'
|
|
551
|
+
${escapedValue}
|
|
552
|
+
EOF`;
|
|
553
|
+
} else {
|
|
554
|
+
const escapedValue = v.value.replace(/'/g, "'\\''");
|
|
555
|
+
return `echo '${escapedValue}' | vercel env add ${v.key} production`;
|
|
556
|
+
}
|
|
557
|
+
}).join("\n\n");
|
|
558
|
+
return header + commands;
|
|
559
|
+
}
|
|
560
|
+
function toRailwayCLI(variables) {
|
|
561
|
+
if (variables.length === 0) return "# No variables to export";
|
|
562
|
+
const header = `# Railway CLI commands to set environment variables
|
|
563
|
+
# Run these commands in your project directory
|
|
564
|
+
# Docs: https://docs.railway.app/reference/cli-api#variables-set
|
|
565
|
+
# Note: You may need to run 'railway login' first
|
|
566
|
+
|
|
567
|
+
`;
|
|
568
|
+
const commands = variables.map((v) => {
|
|
569
|
+
const escapedValue = v.value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
570
|
+
return `railway variables set ${v.key}="${escapedValue}"`;
|
|
571
|
+
}).join("\n");
|
|
572
|
+
return header + commands;
|
|
573
|
+
}
|
|
574
|
+
function toRenderCLI(variables) {
|
|
575
|
+
if (variables.length === 0) return "# No variables to export";
|
|
576
|
+
const header = `# Render CLI commands to set environment variables
|
|
577
|
+
# Run these commands in your project directory
|
|
578
|
+
# Docs: https://render.com/docs/cli
|
|
579
|
+
# Note: You may need to authenticate first
|
|
580
|
+
|
|
581
|
+
`;
|
|
582
|
+
const commands = variables.map((v) => {
|
|
583
|
+
const escapedValue = v.value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
584
|
+
return `render env:set ${v.key}="${escapedValue}"`;
|
|
585
|
+
}).join("\n");
|
|
586
|
+
return header + commands;
|
|
587
|
+
}
|
|
588
|
+
function formatVariables(variables, format, k8sConfig) {
|
|
589
|
+
switch (format) {
|
|
590
|
+
case "dotenv":
|
|
591
|
+
return toDotEnv(variables);
|
|
592
|
+
case "docker-compose":
|
|
593
|
+
return toDockerCompose(variables);
|
|
594
|
+
case "k8s-secret":
|
|
595
|
+
return toKubernetesSecret(variables, k8sConfig ?? { name: "app-secrets", namespace: "default" });
|
|
596
|
+
case "k8s-configmap":
|
|
597
|
+
return toKubernetesConfigMap(variables, k8sConfig ?? { name: "app-config", namespace: "default" });
|
|
598
|
+
case "vercel":
|
|
599
|
+
return toVercelCLI(variables);
|
|
600
|
+
case "railway":
|
|
601
|
+
return toRailwayCLI(variables);
|
|
602
|
+
case "render":
|
|
603
|
+
return toRenderCLI(variables);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// src/lib/config.ts
|
|
467
608
|
var ConfigSchema = z.object({
|
|
468
609
|
project_id: z.string().uuid().optional(),
|
|
469
610
|
project_name: z.string().optional(),
|
|
@@ -471,7 +612,11 @@ var ConfigSchema = z.object({
|
|
|
471
612
|
environment_id: z.string().uuid().optional(),
|
|
472
613
|
organization_id: z.string().uuid().optional(),
|
|
473
614
|
output: z.string().default(".env"),
|
|
474
|
-
api_url: z.string().url().optional()
|
|
615
|
+
api_url: z.string().url().optional(),
|
|
616
|
+
format: z.enum(EXPORT_FORMATS).optional(),
|
|
617
|
+
k8s_namespace: z.string().optional(),
|
|
618
|
+
k8s_name: z.string().optional(),
|
|
619
|
+
tags: z.array(z.string()).optional()
|
|
475
620
|
});
|
|
476
621
|
var CONFIG_FILENAMES = ["envmanager.json", ".envmanagerrc"];
|
|
477
622
|
function findConfigFile(startDir = process.cwd()) {
|
|
@@ -741,17 +886,23 @@ function resolveAll(variables) {
|
|
|
741
886
|
}
|
|
742
887
|
|
|
743
888
|
// src/commands/pull.ts
|
|
744
|
-
var pullCommand = new Command4("pull").description("Pull environment variables from EnvManager to local .env file").option("--org <name>", "Organization name (required if you belong to multiple)").option("-e, --environment <name>", 'Environment name (default: from config or "development")').option("-p, --project <id>", "Project ID (default: from config)").option("-o, --output <file>", "Output file path (default: .env)").option("--no-secrets", "Exclude secret values (will be empty)").option("-f, --force", "Overwrite existing file without prompting").option("-r, --resolve-references", "Resolve ${VAR} references to their values").option("-F, --include-fallbacks", "Include fallback values for empty variables").option("-s, --show-sources", "Show value source as inline comments").action(async (options) => {
|
|
889
|
+
var pullCommand = new Command4("pull").description("Pull environment variables from EnvManager to local .env file").option("--org <name>", "Organization name (required if you belong to multiple)").option("-e, --environment <name>", 'Environment name (default: from config or "development")').option("-p, --project <id>", "Project ID (default: from config)").option("-o, --output <file>", "Output file path (default: .env)").option("--no-secrets", "Exclude secret values (will be empty)").option("-f, --force", "Overwrite existing file without prompting").option("-r, --resolve-references", "Resolve ${VAR} references to their values").option("-F, --include-fallbacks", "Include fallback values for empty variables").option("-s, --show-sources", "Show value source as inline comments").option("--format <type>", `Export format (${EXPORT_FORMATS.join(", ")})`).option("--k8s-namespace <ns>", 'Kubernetes namespace (default: "default")').option("--k8s-name <name>", "Kubernetes resource name").option("--tag <tags...>", "Filter by tags (untagged variables always included)").action(async (options) => {
|
|
745
890
|
const spinner = ora3("Connecting to EnvManager...").start();
|
|
746
891
|
try {
|
|
747
892
|
const config = loadConfig();
|
|
748
893
|
const projectInput = options.project || config?.project_id;
|
|
749
894
|
const envName = options.environment || config?.environment || "development";
|
|
750
|
-
const outputFile = resolve(options.output || ".env");
|
|
895
|
+
const outputFile = resolve(options.output || config?.output || ".env");
|
|
751
896
|
const includeSecrets = options.secrets !== false;
|
|
752
897
|
const shouldResolve = options.resolveReferences === true;
|
|
753
898
|
const shouldFallback = options.includeFallbacks === true;
|
|
754
899
|
const shouldShowSources = options.showSources === true;
|
|
900
|
+
const formatInput = options.format || config?.format || "dotenv";
|
|
901
|
+
if (!EXPORT_FORMATS.includes(formatInput)) {
|
|
902
|
+
spinner.fail(`Invalid format "${formatInput}". Valid formats: ${EXPORT_FORMATS.join(", ")}`);
|
|
903
|
+
process.exit(1);
|
|
904
|
+
}
|
|
905
|
+
const format = formatInput;
|
|
755
906
|
if (!projectInput) {
|
|
756
907
|
spinner.fail("No project specified");
|
|
757
908
|
console.log(chalk4.yellow("\nSpecify a project with --project <id-or-name> or create envmanager.json"));
|
|
@@ -807,7 +958,11 @@ File ${outputFile} already exists.`));
|
|
|
807
958
|
console.log(chalk4.gray("Use --force to overwrite."));
|
|
808
959
|
process.exit(1);
|
|
809
960
|
}
|
|
810
|
-
const
|
|
961
|
+
const filterTags = options.tag || config?.tags || [];
|
|
962
|
+
const vars = filterTags.length > 0 ? variables.filter((v) => {
|
|
963
|
+
const t = v.tags || [];
|
|
964
|
+
return t.length === 0 || t.some((tag) => filterTags.includes(tag));
|
|
965
|
+
}).sort((a, b) => a.key.localeCompare(b.key)) : variables.sort((a, b) => a.key.localeCompare(b.key));
|
|
811
966
|
let resolvedMap = null;
|
|
812
967
|
if (shouldResolve) {
|
|
813
968
|
spinner.text = "Resolving variable references...";
|
|
@@ -828,39 +983,64 @@ File ${outputFile} already exists.`));
|
|
|
828
983
|
const resolved = resolveAll(inputs);
|
|
829
984
|
resolvedMap = new Map(resolved.map((r) => [r.key, r]));
|
|
830
985
|
}
|
|
831
|
-
spinner.text =
|
|
832
|
-
const
|
|
986
|
+
spinner.text = `Writing ${format} output...`;
|
|
987
|
+
const exportVars = vars.map((v) => {
|
|
833
988
|
let value;
|
|
834
|
-
let source = null;
|
|
835
989
|
if (resolvedMap) {
|
|
836
|
-
|
|
837
|
-
value = resolved.resolvedValue;
|
|
838
|
-
source = resolved.source;
|
|
990
|
+
value = resolvedMap.get(v.key).resolvedValue;
|
|
839
991
|
} else if (shouldFallback && (!v.value || v.value === "") && v.fallback_value) {
|
|
840
992
|
value = v.fallback_value;
|
|
841
|
-
source = "fallback";
|
|
842
993
|
} else {
|
|
843
994
|
value = v.value || "";
|
|
844
995
|
}
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
996
|
+
return { key: v.key, value, isSecret: v.is_secret };
|
|
997
|
+
});
|
|
998
|
+
let content;
|
|
999
|
+
if (format === "dotenv" && (shouldShowSources || shouldResolve)) {
|
|
1000
|
+
content = vars.map((v) => {
|
|
1001
|
+
let value;
|
|
1002
|
+
let source = null;
|
|
1003
|
+
if (resolvedMap) {
|
|
1004
|
+
const resolved = resolvedMap.get(v.key);
|
|
1005
|
+
value = resolved.resolvedValue;
|
|
1006
|
+
source = resolved.source;
|
|
1007
|
+
} else if (shouldFallback && (!v.value || v.value === "") && v.fallback_value) {
|
|
1008
|
+
value = v.fallback_value;
|
|
1009
|
+
source = "fallback";
|
|
1010
|
+
} else {
|
|
1011
|
+
value = v.value || "";
|
|
1012
|
+
}
|
|
1013
|
+
const needsQuotes = value.includes(" ") || value.includes("\n") || value.includes('"');
|
|
1014
|
+
const formattedValue = needsQuotes ? `"${value.replace(/"/g, '\\"')}"` : value;
|
|
1015
|
+
let line = `${v.key}=${formattedValue}`;
|
|
1016
|
+
if (shouldShowSources && resolvedMap) {
|
|
1017
|
+
const resolved = resolvedMap.get(v.key);
|
|
1018
|
+
if (resolved.references.length > 0 && resolved.source !== "empty") {
|
|
1019
|
+
line += ` # resolved from ${resolved.rawValue ?? v.value ?? ""}`;
|
|
1020
|
+
} else if (resolved.source === "fallback") {
|
|
1021
|
+
line += ` # fallback value`;
|
|
1022
|
+
}
|
|
1023
|
+
} else if (shouldShowSources && source === "fallback") {
|
|
853
1024
|
line += ` # fallback value`;
|
|
854
1025
|
}
|
|
855
|
-
|
|
856
|
-
|
|
1026
|
+
return line;
|
|
1027
|
+
}).join("\n");
|
|
1028
|
+
} else {
|
|
1029
|
+
let k8sConfig;
|
|
1030
|
+
if (format === "k8s-secret" || format === "k8s-configmap") {
|
|
1031
|
+
const defaultName = format === "k8s-secret" ? "app-secrets" : "app-config";
|
|
1032
|
+
k8sConfig = {
|
|
1033
|
+
name: sanitizeK8sName(options.k8sName || config?.k8s_name || defaultName),
|
|
1034
|
+
namespace: options.k8sNamespace || config?.k8s_namespace || "default"
|
|
1035
|
+
};
|
|
857
1036
|
}
|
|
858
|
-
|
|
859
|
-
}
|
|
860
|
-
writeFileSync2(outputFile,
|
|
1037
|
+
content = formatVariables(exportVars, format, k8sConfig);
|
|
1038
|
+
}
|
|
1039
|
+
writeFileSync2(outputFile, content + "\n");
|
|
861
1040
|
const secretCount = vars.filter((v) => v.is_secret).length;
|
|
862
1041
|
const plainCount = vars.length - secretCount;
|
|
863
|
-
|
|
1042
|
+
const tagInfo = filterTags.length > 0 ? ` (tags: ${filterTags.join(", ")})` : "";
|
|
1043
|
+
spinner.succeed(`Pulled ${vars.length} variables to ${outputFile} (${format})${tagInfo}`);
|
|
864
1044
|
console.log(chalk4.gray(` ${plainCount} plain, ${secretCount} secrets`));
|
|
865
1045
|
client.rpc("log_variable_access", {
|
|
866
1046
|
p_environment_id: environmentId,
|
|
@@ -1166,7 +1346,7 @@ import chalk7 from "chalk";
|
|
|
1166
1346
|
import ora5 from "ora";
|
|
1167
1347
|
import { readFileSync as readFileSync4, existsSync as existsSync5 } from "fs";
|
|
1168
1348
|
import { resolve as resolve3 } from "path";
|
|
1169
|
-
var diffCommand = new Command6("diff").description("Show differences between local .env and EnvManager").option("--org <name>", "Organization name (required if you belong to multiple)").option("-e, --environment <name>", 'Environment name (default: from config or "development")').option("-p, --project <id>", "Project ID (default: from config)").option("-i, --input <file>", "Input file path (default: .env)").option("--keys-only", "Only show key names, not values").action(async (options) => {
|
|
1349
|
+
var diffCommand = new Command6("diff").description("Show differences between local .env and EnvManager").option("--org <name>", "Organization name (required if you belong to multiple)").option("-e, --environment <name>", 'Environment name (default: from config or "development")').option("-p, --project <id>", "Project ID (default: from config)").option("-i, --input <file>", "Input file path (default: .env)").option("--keys-only", "Only show key names, not values").option("--tag <tags...>", "Filter by tags (untagged variables always included)").action(async (options) => {
|
|
1170
1350
|
const spinner = ora5("Comparing...").start();
|
|
1171
1351
|
try {
|
|
1172
1352
|
const config = loadConfig();
|
|
@@ -1218,8 +1398,13 @@ var diffCommand = new Command6("diff").description("Show differences between loc
|
|
|
1218
1398
|
console.error(chalk7.red(varError.message));
|
|
1219
1399
|
process.exit(1);
|
|
1220
1400
|
}
|
|
1401
|
+
const filterTags = options.tag || config?.tags || [];
|
|
1402
|
+
const filteredRemoteData = filterTags.length > 0 ? (remoteVarsData || []).filter((v) => {
|
|
1403
|
+
const t = v.tags || [];
|
|
1404
|
+
return t.length === 0 || t.some((tag) => filterTags.includes(tag));
|
|
1405
|
+
}) : remoteVarsData || [];
|
|
1221
1406
|
const remoteVars = /* @__PURE__ */ new Map();
|
|
1222
|
-
for (const v of
|
|
1407
|
+
for (const v of filteredRemoteData) {
|
|
1223
1408
|
remoteVars.set(v.key, v);
|
|
1224
1409
|
}
|
|
1225
1410
|
spinner.stop();
|
|
@@ -1703,7 +1888,7 @@ function mergeWithRemote(local, remoteVariables, strategy) {
|
|
|
1703
1888
|
}
|
|
1704
1889
|
|
|
1705
1890
|
// src/commands/dev.ts
|
|
1706
|
-
var devCommand = new Command9("dev").description("Start real-time sync daemon - watches for remote variable changes").option("--org <name>", "Organization name (required if you belong to multiple)").option("-e, --environment <name>", 'Environment name (default: from config or "development")').option("-p, --project <id>", "Project ID (default: from config)").option("--output <file>", "Output file path (default: .env)").option("--no-watch", "Disable local file watching").option("--strategy <type>", "Merge strategy: remote_wins, local_wins, merge_new (default: remote_wins)", "remote_wins").action(async (options) => {
|
|
1891
|
+
var devCommand = new Command9("dev").description("Start real-time sync daemon - watches for remote variable changes").option("--org <name>", "Organization name (required if you belong to multiple)").option("-e, --environment <name>", 'Environment name (default: from config or "development")').option("-p, --project <id>", "Project ID (default: from config)").option("--output <file>", "Output file path (default: .env)").option("--no-watch", "Disable local file watching").option("--strategy <type>", "Merge strategy: remote_wins, local_wins, merge_new (default: remote_wins)", "remote_wins").option("--tag <tags...>", "Filter by tags (untagged variables always included)").action(async (options) => {
|
|
1707
1892
|
const spinner = ora7("Starting dev mode...").start();
|
|
1708
1893
|
try {
|
|
1709
1894
|
const config = loadConfig();
|
|
@@ -1740,8 +1925,16 @@ var devCommand = new Command9("dev").description("Start real-time sync daemon -
|
|
|
1740
1925
|
process.exit(1);
|
|
1741
1926
|
}
|
|
1742
1927
|
const environmentId = environment.id;
|
|
1928
|
+
const filterTags = options.tag || config?.tags || [];
|
|
1929
|
+
const applyTagFilter = (vars) => {
|
|
1930
|
+
if (filterTags.length === 0) return vars;
|
|
1931
|
+
return vars.filter((v) => {
|
|
1932
|
+
const t = v.tags || [];
|
|
1933
|
+
return t.length === 0 || t.some((tag) => filterTags.includes(tag));
|
|
1934
|
+
});
|
|
1935
|
+
};
|
|
1743
1936
|
spinner.text = "Performing initial sync...";
|
|
1744
|
-
const remoteVariables = await fetchAllVariables(environmentId, true);
|
|
1937
|
+
const remoteVariables = applyTagFilter(await fetchAllVariables(environmentId, true));
|
|
1745
1938
|
let localVariables = /* @__PURE__ */ new Map();
|
|
1746
1939
|
if (existsSync7(outputFile)) {
|
|
1747
1940
|
const content = readFileSync6(outputFile, "utf-8");
|
|
@@ -1758,7 +1951,7 @@ var devCommand = new Command9("dev").description("Start real-time sync daemon -
|
|
|
1758
1951
|
let isPaused = false;
|
|
1759
1952
|
let lastRemoteKeys = null;
|
|
1760
1953
|
async function syncRemoteToLocal(silent = false) {
|
|
1761
|
-
const updatedVariables = await fetchAllVariables(environmentId, true);
|
|
1954
|
+
const updatedVariables = applyTagFilter(await fetchAllVariables(environmentId, true));
|
|
1762
1955
|
const currentLocal = /* @__PURE__ */ new Map();
|
|
1763
1956
|
if (existsSync7(outputFile)) {
|
|
1764
1957
|
const content = readFileSync6(outputFile, "utf-8");
|
|
@@ -2003,9 +2196,9 @@ function validateAgainstTemplate(template, env) {
|
|
|
2003
2196
|
}
|
|
2004
2197
|
|
|
2005
2198
|
// src/lib/template-yaml.ts
|
|
2006
|
-
import
|
|
2199
|
+
import yaml2 from "js-yaml";
|
|
2007
2200
|
function parseYamlTemplate(content) {
|
|
2008
|
-
const parsed =
|
|
2201
|
+
const parsed = yaml2.load(content);
|
|
2009
2202
|
if (!parsed || typeof parsed !== "object") {
|
|
2010
2203
|
throw new Error("Invalid YAML template: must be an object");
|
|
2011
2204
|
}
|
|
@@ -2150,7 +2343,7 @@ function generateYamlTemplate(variables, options = {}) {
|
|
|
2150
2343
|
vars[v.key] = varConfig;
|
|
2151
2344
|
}
|
|
2152
2345
|
template.variables = vars;
|
|
2153
|
-
return
|
|
2346
|
+
return yaml2.dump(template, { lineWidth: -1, quotingType: '"' });
|
|
2154
2347
|
}
|
|
2155
2348
|
|
|
2156
2349
|
// src/commands/init.ts
|