@envpilot/cli 1.3.4 → 1.4.1
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/index.js +418 -20
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -9,7 +9,7 @@ function initSentry() {
|
|
|
9
9
|
Sentry.init({
|
|
10
10
|
dsn,
|
|
11
11
|
environment: "cli",
|
|
12
|
-
release: true ? "1.
|
|
12
|
+
release: true ? "1.4.1" : "0.0.0",
|
|
13
13
|
// Free tier: disable performance monitoring
|
|
14
14
|
tracesSampleRate: 0,
|
|
15
15
|
beforeSend(event) {
|
|
@@ -748,6 +748,11 @@ var projectSchema = z.object({
|
|
|
748
748
|
userRole: z.string().nullable().optional(),
|
|
749
749
|
projectRole: z.string().nullable().optional()
|
|
750
750
|
});
|
|
751
|
+
var variableTagSchema = z.object({
|
|
752
|
+
_id: z.string(),
|
|
753
|
+
name: z.string(),
|
|
754
|
+
color: z.string()
|
|
755
|
+
});
|
|
751
756
|
var variableSchema = z.object({
|
|
752
757
|
_id: z.string(),
|
|
753
758
|
key: z.string(),
|
|
@@ -756,7 +761,8 @@ var variableSchema = z.object({
|
|
|
756
761
|
projectId: z.string(),
|
|
757
762
|
description: z.string().optional(),
|
|
758
763
|
isSensitive: z.boolean().optional(),
|
|
759
|
-
version: z.number().optional()
|
|
764
|
+
version: z.number().optional(),
|
|
765
|
+
tags: z.array(variableTagSchema).optional()
|
|
760
766
|
});
|
|
761
767
|
var environmentSchema = z.enum([
|
|
762
768
|
"development",
|
|
@@ -1384,10 +1390,356 @@ function printPostInit(selectedOrg, selectedProject) {
|
|
|
1384
1390
|
import { Command as Command3 } from "commander";
|
|
1385
1391
|
import chalk5 from "chalk";
|
|
1386
1392
|
import inquirer2 from "inquirer";
|
|
1393
|
+
|
|
1394
|
+
// src/lib/format-converter.ts
|
|
1395
|
+
var ALL_FORMATS = [
|
|
1396
|
+
"env",
|
|
1397
|
+
"json",
|
|
1398
|
+
"yaml",
|
|
1399
|
+
"docker-compose",
|
|
1400
|
+
"aws",
|
|
1401
|
+
"vercel",
|
|
1402
|
+
"netlify"
|
|
1403
|
+
];
|
|
1404
|
+
function serialize(vars, format, meta) {
|
|
1405
|
+
switch (format) {
|
|
1406
|
+
case "env":
|
|
1407
|
+
return serializeEnv(vars, meta);
|
|
1408
|
+
case "json":
|
|
1409
|
+
return serializeJson(vars);
|
|
1410
|
+
case "yaml":
|
|
1411
|
+
return serializeYaml(vars, meta);
|
|
1412
|
+
case "docker-compose":
|
|
1413
|
+
return serializeDockerCompose(vars, meta);
|
|
1414
|
+
case "aws":
|
|
1415
|
+
return serializeAws(vars, meta);
|
|
1416
|
+
case "vercel":
|
|
1417
|
+
return serializeVercel(vars);
|
|
1418
|
+
case "netlify":
|
|
1419
|
+
return serializeNetlify(vars, meta);
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
function parse(content, format, options) {
|
|
1423
|
+
switch (format) {
|
|
1424
|
+
case "env":
|
|
1425
|
+
return parseEnvFile(content);
|
|
1426
|
+
case "json":
|
|
1427
|
+
return parseJson(content);
|
|
1428
|
+
case "yaml":
|
|
1429
|
+
return parseYaml(content);
|
|
1430
|
+
case "docker-compose":
|
|
1431
|
+
return parseDockerCompose(content);
|
|
1432
|
+
case "aws":
|
|
1433
|
+
return parseAws(content, options?.prefix);
|
|
1434
|
+
case "vercel":
|
|
1435
|
+
return parseVercel(content);
|
|
1436
|
+
case "netlify":
|
|
1437
|
+
return parseNetlify(content);
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
function getDefaultFilename(format, environment) {
|
|
1441
|
+
const env = environment || "development";
|
|
1442
|
+
switch (format) {
|
|
1443
|
+
case "env":
|
|
1444
|
+
return env === "development" ? ".env.local" : `.env.${env}`;
|
|
1445
|
+
case "json":
|
|
1446
|
+
return `${env}.json`;
|
|
1447
|
+
case "yaml":
|
|
1448
|
+
return `${env}.yaml`;
|
|
1449
|
+
case "docker-compose":
|
|
1450
|
+
return "docker-compose.yml";
|
|
1451
|
+
case "aws":
|
|
1452
|
+
return `${env}.aws.json`;
|
|
1453
|
+
case "vercel":
|
|
1454
|
+
return `${env}.vercel.json`;
|
|
1455
|
+
case "netlify":
|
|
1456
|
+
return "netlify.toml";
|
|
1457
|
+
}
|
|
1458
|
+
}
|
|
1459
|
+
function serializeEnv(vars, meta) {
|
|
1460
|
+
const lines = [];
|
|
1461
|
+
if (meta?.environment) {
|
|
1462
|
+
lines.push(`# Environment: ${meta.environment}`);
|
|
1463
|
+
}
|
|
1464
|
+
if (meta?.projectName) {
|
|
1465
|
+
lines.push(`# Project: ${meta.projectName}`);
|
|
1466
|
+
}
|
|
1467
|
+
if (lines.length > 0) {
|
|
1468
|
+
lines.push(`# Exported: ${(/* @__PURE__ */ new Date()).toISOString()}`);
|
|
1469
|
+
lines.push("");
|
|
1470
|
+
}
|
|
1471
|
+
return lines.join("\n") + stringifyEnv(vars, { sort: true });
|
|
1472
|
+
}
|
|
1473
|
+
function serializeJson(vars) {
|
|
1474
|
+
const sorted = Object.keys(vars).sort().reduce(
|
|
1475
|
+
(obj, key) => {
|
|
1476
|
+
obj[key] = vars[key];
|
|
1477
|
+
return obj;
|
|
1478
|
+
},
|
|
1479
|
+
{}
|
|
1480
|
+
);
|
|
1481
|
+
return JSON.stringify(sorted, null, 2) + "\n";
|
|
1482
|
+
}
|
|
1483
|
+
function parseJson(content) {
|
|
1484
|
+
const data = JSON.parse(content);
|
|
1485
|
+
if (typeof data !== "object" || data === null || Array.isArray(data)) {
|
|
1486
|
+
throw new Error("Expected a JSON object with string key-value pairs");
|
|
1487
|
+
}
|
|
1488
|
+
const result = {};
|
|
1489
|
+
for (const [key, value] of Object.entries(data)) {
|
|
1490
|
+
if (typeof value === "string") {
|
|
1491
|
+
result[key] = value;
|
|
1492
|
+
} else if (value !== null && value !== void 0) {
|
|
1493
|
+
result[key] = String(value);
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
return result;
|
|
1497
|
+
}
|
|
1498
|
+
function yamlQuote(value) {
|
|
1499
|
+
if (value === "" || value === "true" || value === "false" || value === "null" || value === "~" || /^[0-9]/.test(value) || /[:#\[\]{}&*!|>'"%@`]/.test(value) || value.includes("\n") || value.startsWith(" ") || value.endsWith(" ")) {
|
|
1500
|
+
const escaped = value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t");
|
|
1501
|
+
return `"${escaped}"`;
|
|
1502
|
+
}
|
|
1503
|
+
return value;
|
|
1504
|
+
}
|
|
1505
|
+
function serializeYaml(vars, meta) {
|
|
1506
|
+
const lines = [];
|
|
1507
|
+
if (meta?.environment) {
|
|
1508
|
+
lines.push(`# Environment: ${meta.environment}`);
|
|
1509
|
+
}
|
|
1510
|
+
if (meta?.projectName) {
|
|
1511
|
+
lines.push(`# Project: ${meta.projectName}`);
|
|
1512
|
+
}
|
|
1513
|
+
if (lines.length > 0) {
|
|
1514
|
+
lines.push("");
|
|
1515
|
+
}
|
|
1516
|
+
for (const key of Object.keys(vars).sort()) {
|
|
1517
|
+
lines.push(`${key}: ${yamlQuote(vars[key])}`);
|
|
1518
|
+
}
|
|
1519
|
+
return lines.join("\n") + "\n";
|
|
1520
|
+
}
|
|
1521
|
+
function parseYaml(content) {
|
|
1522
|
+
const result = {};
|
|
1523
|
+
for (const line of content.split("\n")) {
|
|
1524
|
+
const trimmed = line.trim();
|
|
1525
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
1526
|
+
const colonIndex = trimmed.indexOf(": ");
|
|
1527
|
+
if (colonIndex === -1) {
|
|
1528
|
+
if (trimmed.endsWith(":")) {
|
|
1529
|
+
const key2 = trimmed.slice(0, -1).trim();
|
|
1530
|
+
if (key2 && /^[A-Za-z_][A-Za-z0-9_]*$/.test(key2)) {
|
|
1531
|
+
result[key2] = "";
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
continue;
|
|
1535
|
+
}
|
|
1536
|
+
const key = trimmed.substring(0, colonIndex).trim();
|
|
1537
|
+
let value = trimmed.substring(colonIndex + 2).trim();
|
|
1538
|
+
if (line.startsWith(" ") || line.startsWith(" ")) continue;
|
|
1539
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
1540
|
+
value = value.slice(1, -1);
|
|
1541
|
+
if (value.includes("\\")) {
|
|
1542
|
+
value = value.replace(/\\n/g, "\n").replace(/\\r/g, "\r").replace(/\\t/g, " ").replace(/\\"/g, '"').replace(/\\\\/g, "\\");
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1545
|
+
if (/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {
|
|
1546
|
+
result[key] = value;
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
return result;
|
|
1550
|
+
}
|
|
1551
|
+
function serializeDockerCompose(vars, meta) {
|
|
1552
|
+
const lines = [];
|
|
1553
|
+
if (meta?.environment || meta?.projectName) {
|
|
1554
|
+
lines.push("# Docker Compose environment variables");
|
|
1555
|
+
if (meta.projectName) lines.push(`# Project: ${meta.projectName}`);
|
|
1556
|
+
if (meta.environment) lines.push(`# Environment: ${meta.environment}`);
|
|
1557
|
+
lines.push("");
|
|
1558
|
+
}
|
|
1559
|
+
lines.push("services:");
|
|
1560
|
+
lines.push(" app:");
|
|
1561
|
+
lines.push(" environment:");
|
|
1562
|
+
for (const key of Object.keys(vars).sort()) {
|
|
1563
|
+
lines.push(` ${key}: ${yamlQuote(vars[key])}`);
|
|
1564
|
+
}
|
|
1565
|
+
return lines.join("\n") + "\n";
|
|
1566
|
+
}
|
|
1567
|
+
function parseDockerCompose(content) {
|
|
1568
|
+
const result = {};
|
|
1569
|
+
const lines = content.split("\n");
|
|
1570
|
+
let inEnvironment = false;
|
|
1571
|
+
let environmentIndent = -1;
|
|
1572
|
+
for (const line of lines) {
|
|
1573
|
+
const trimmed = line.trim();
|
|
1574
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
1575
|
+
const indent = line.length - line.trimStart().length;
|
|
1576
|
+
if (trimmed === "environment:") {
|
|
1577
|
+
inEnvironment = true;
|
|
1578
|
+
environmentIndent = indent;
|
|
1579
|
+
continue;
|
|
1580
|
+
}
|
|
1581
|
+
if (inEnvironment) {
|
|
1582
|
+
if (indent <= environmentIndent && trimmed !== "") {
|
|
1583
|
+
inEnvironment = false;
|
|
1584
|
+
environmentIndent = -1;
|
|
1585
|
+
continue;
|
|
1586
|
+
}
|
|
1587
|
+
if (trimmed.startsWith("- ")) {
|
|
1588
|
+
const entry = trimmed.slice(2);
|
|
1589
|
+
const eqIdx = entry.indexOf("=");
|
|
1590
|
+
if (eqIdx !== -1) {
|
|
1591
|
+
const key = entry.substring(0, eqIdx).trim();
|
|
1592
|
+
let value = entry.substring(eqIdx + 1).trim();
|
|
1593
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
1594
|
+
value = value.slice(1, -1);
|
|
1595
|
+
}
|
|
1596
|
+
if (/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {
|
|
1597
|
+
result[key] = value;
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1600
|
+
continue;
|
|
1601
|
+
}
|
|
1602
|
+
const colonIndex = trimmed.indexOf(": ");
|
|
1603
|
+
if (colonIndex !== -1) {
|
|
1604
|
+
const key = trimmed.substring(0, colonIndex).trim();
|
|
1605
|
+
let value = trimmed.substring(colonIndex + 2).trim();
|
|
1606
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
1607
|
+
value = value.slice(1, -1);
|
|
1608
|
+
if (value.includes("\\")) {
|
|
1609
|
+
value = value.replace(/\\n/g, "\n").replace(/\\r/g, "\r").replace(/\\t/g, " ").replace(/\\"/g, '"').replace(/\\\\/g, "\\");
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
if (/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {
|
|
1613
|
+
result[key] = value;
|
|
1614
|
+
}
|
|
1615
|
+
} else if (trimmed.endsWith(":")) {
|
|
1616
|
+
const key = trimmed.slice(0, -1).trim();
|
|
1617
|
+
if (/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {
|
|
1618
|
+
result[key] = "";
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1621
|
+
}
|
|
1622
|
+
}
|
|
1623
|
+
return result;
|
|
1624
|
+
}
|
|
1625
|
+
function serializeAws(vars, meta) {
|
|
1626
|
+
const prefix = meta?.prefix || `/${meta?.projectName || "app"}`;
|
|
1627
|
+
const parameters = Object.keys(vars).sort().map((key) => ({
|
|
1628
|
+
name: `${prefix}/${key}`,
|
|
1629
|
+
value: vars[key],
|
|
1630
|
+
type: "SecureString"
|
|
1631
|
+
}));
|
|
1632
|
+
return JSON.stringify({ parameters }, null, 2) + "\n";
|
|
1633
|
+
}
|
|
1634
|
+
function parseAws(content, prefix) {
|
|
1635
|
+
const data = JSON.parse(content);
|
|
1636
|
+
const result = {};
|
|
1637
|
+
const params = data.parameters || data.Parameters || [];
|
|
1638
|
+
if (!Array.isArray(params)) {
|
|
1639
|
+
throw new Error(
|
|
1640
|
+
"Expected AWS Parameter Store format with 'parameters' array"
|
|
1641
|
+
);
|
|
1642
|
+
}
|
|
1643
|
+
for (const param of params) {
|
|
1644
|
+
const name = param.name || param.Name || "";
|
|
1645
|
+
const value = param.value || param.Value || "";
|
|
1646
|
+
let key;
|
|
1647
|
+
if (prefix && name.startsWith(prefix + "/")) {
|
|
1648
|
+
key = name.substring(prefix.length + 1);
|
|
1649
|
+
} else {
|
|
1650
|
+
const lastSlash = name.lastIndexOf("/");
|
|
1651
|
+
key = lastSlash !== -1 ? name.substring(lastSlash + 1) : name;
|
|
1652
|
+
}
|
|
1653
|
+
if (key && /^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {
|
|
1654
|
+
result[key] = value;
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
return result;
|
|
1658
|
+
}
|
|
1659
|
+
function serializeVercel(vars) {
|
|
1660
|
+
const sorted = Object.keys(vars).sort().reduce(
|
|
1661
|
+
(obj, key) => {
|
|
1662
|
+
obj[key] = vars[key];
|
|
1663
|
+
return obj;
|
|
1664
|
+
},
|
|
1665
|
+
{}
|
|
1666
|
+
);
|
|
1667
|
+
return JSON.stringify({ env: sorted }, null, 2) + "\n";
|
|
1668
|
+
}
|
|
1669
|
+
function parseVercel(content) {
|
|
1670
|
+
const data = JSON.parse(content);
|
|
1671
|
+
const result = {};
|
|
1672
|
+
const env = data.env;
|
|
1673
|
+
if (!env || typeof env !== "object" || Array.isArray(env)) {
|
|
1674
|
+
throw new Error("Expected Vercel format with 'env' object");
|
|
1675
|
+
}
|
|
1676
|
+
for (const [key, value] of Object.entries(env)) {
|
|
1677
|
+
if (typeof value === "string") {
|
|
1678
|
+
result[key] = value;
|
|
1679
|
+
} else if (value !== null && value !== void 0) {
|
|
1680
|
+
result[key] = String(value);
|
|
1681
|
+
}
|
|
1682
|
+
}
|
|
1683
|
+
return result;
|
|
1684
|
+
}
|
|
1685
|
+
function tomlQuote(value) {
|
|
1686
|
+
const escaped = value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t");
|
|
1687
|
+
return `"${escaped}"`;
|
|
1688
|
+
}
|
|
1689
|
+
function serializeNetlify(vars, meta) {
|
|
1690
|
+
const lines = [];
|
|
1691
|
+
if (meta?.environment || meta?.projectName) {
|
|
1692
|
+
if (meta.projectName) lines.push(`# Project: ${meta.projectName}`);
|
|
1693
|
+
if (meta.environment) lines.push(`# Environment: ${meta.environment}`);
|
|
1694
|
+
lines.push("");
|
|
1695
|
+
}
|
|
1696
|
+
lines.push("[build.environment]");
|
|
1697
|
+
for (const key of Object.keys(vars).sort()) {
|
|
1698
|
+
lines.push(` ${key} = ${tomlQuote(vars[key])}`);
|
|
1699
|
+
}
|
|
1700
|
+
return lines.join("\n") + "\n";
|
|
1701
|
+
}
|
|
1702
|
+
function parseNetlify(content) {
|
|
1703
|
+
const result = {};
|
|
1704
|
+
const lines = content.split("\n");
|
|
1705
|
+
let inBuildEnv = false;
|
|
1706
|
+
for (const line of lines) {
|
|
1707
|
+
const trimmed = line.trim();
|
|
1708
|
+
if (trimmed.startsWith("[")) {
|
|
1709
|
+
inBuildEnv = trimmed === "[build.environment]" || trimmed === "[build.environment]";
|
|
1710
|
+
continue;
|
|
1711
|
+
}
|
|
1712
|
+
if (!inBuildEnv) continue;
|
|
1713
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
1714
|
+
const eqIndex = trimmed.indexOf("=");
|
|
1715
|
+
if (eqIndex === -1) continue;
|
|
1716
|
+
const key = trimmed.substring(0, eqIndex).trim();
|
|
1717
|
+
let value = trimmed.substring(eqIndex + 1).trim();
|
|
1718
|
+
if (value.startsWith('"') && value.endsWith('"')) {
|
|
1719
|
+
value = value.slice(1, -1);
|
|
1720
|
+
value = value.replace(/\\n/g, "\n").replace(/\\r/g, "\r").replace(/\\t/g, " ").replace(/\\"/g, '"').replace(/\\\\/g, "\\");
|
|
1721
|
+
} else if (value.startsWith("'") && value.endsWith("'")) {
|
|
1722
|
+
value = value.slice(1, -1);
|
|
1723
|
+
}
|
|
1724
|
+
if (/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {
|
|
1725
|
+
result[key] = value;
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1728
|
+
return result;
|
|
1729
|
+
}
|
|
1730
|
+
|
|
1731
|
+
// src/commands/pull.ts
|
|
1387
1732
|
var pullCommand = new Command3("pull").description("Download environment variables to local .env file").option(
|
|
1388
1733
|
"-e, --env <environment>",
|
|
1389
1734
|
"Environment (development, staging, production)"
|
|
1390
|
-
).option("-f, --file <path>", "Output file path (default: .env)").option("--force", "Overwrite without confirmation").option(
|
|
1735
|
+
).option("-f, --file <path>", "Output file path (default: .env)").option("--force", "Overwrite without confirmation").option(
|
|
1736
|
+
"--format <format>",
|
|
1737
|
+
"Output format: env, json, yaml, docker-compose, aws, vercel, netlify",
|
|
1738
|
+
"env"
|
|
1739
|
+
).option(
|
|
1740
|
+
"--prefix <prefix>",
|
|
1741
|
+
"AWS Parameter Store path prefix (default: /project-name)"
|
|
1742
|
+
).option("--dry-run", "Show what would be downloaded without writing").option("--project <name-or-id>", "Pull a specific linked project").option("--all", "Pull all linked projects").action(async (options) => {
|
|
1391
1743
|
try {
|
|
1392
1744
|
if (!isAuthenticated()) {
|
|
1393
1745
|
throw notAuthenticated();
|
|
@@ -1420,7 +1772,12 @@ var pullCommand = new Command3("pull").description("Download environment variabl
|
|
|
1420
1772
|
if (!projectConfig) throw notInitialized();
|
|
1421
1773
|
checkTrackedFiles();
|
|
1422
1774
|
const environment = options.env || projectConfig.environment || "development";
|
|
1423
|
-
const
|
|
1775
|
+
const fmt = options.format || "env";
|
|
1776
|
+
if (!ALL_FORMATS.includes(fmt)) {
|
|
1777
|
+
error(`Unknown format: ${fmt}. Supported: ${ALL_FORMATS.join(", ")}`);
|
|
1778
|
+
process.exit(1);
|
|
1779
|
+
}
|
|
1780
|
+
const outputPath = options.file || (fmt === "env" ? getEnvPathForEnvironment(environment) : getDefaultFilename(fmt, environment));
|
|
1424
1781
|
await pullProject(
|
|
1425
1782
|
{
|
|
1426
1783
|
projectId: projectConfig.projectId,
|
|
@@ -1479,7 +1836,8 @@ async function pullAllProjects(options) {
|
|
|
1479
1836
|
async function pullSingleProject(project, options) {
|
|
1480
1837
|
checkTrackedFiles();
|
|
1481
1838
|
const environment = options.env || project.environment;
|
|
1482
|
-
const
|
|
1839
|
+
const fmt = options.format || "env";
|
|
1840
|
+
const outputPath = options.file || (fmt === "env" ? getEnvPathForEnvironment(environment) : getDefaultFilename(fmt, environment));
|
|
1483
1841
|
await pullProject(
|
|
1484
1842
|
{
|
|
1485
1843
|
projectId: project.projectId,
|
|
@@ -1552,10 +1910,8 @@ async function pullProject(project, outputPath, options) {
|
|
|
1552
1910
|
}
|
|
1553
1911
|
} catch {
|
|
1554
1912
|
}
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
fs.writeFileSync(outputPath, JSON.stringify(remoteVars, null, 2) + "\n");
|
|
1558
|
-
} else {
|
|
1913
|
+
const fmt = options.format || "env";
|
|
1914
|
+
if (fmt === "env") {
|
|
1559
1915
|
const comments = {};
|
|
1560
1916
|
for (const variable of variables) {
|
|
1561
1917
|
if (variable.description) {
|
|
@@ -1563,6 +1919,14 @@ async function pullProject(project, outputPath, options) {
|
|
|
1563
1919
|
}
|
|
1564
1920
|
}
|
|
1565
1921
|
writeEnvFile(outputPath, remoteVars, { sort: true, comments });
|
|
1922
|
+
} else {
|
|
1923
|
+
const fs = await import("fs");
|
|
1924
|
+
const output = serialize(remoteVars, fmt, {
|
|
1925
|
+
projectName: project.projectId,
|
|
1926
|
+
environment: project.environment,
|
|
1927
|
+
prefix: options.prefix
|
|
1928
|
+
});
|
|
1929
|
+
fs.writeFileSync(outputPath, output, "utf-8");
|
|
1566
1930
|
}
|
|
1567
1931
|
const role = getRole();
|
|
1568
1932
|
applyFileProtection(outputPath, role, metaProjectRole);
|
|
@@ -1658,7 +2022,14 @@ function validateEnvVars(vars) {
|
|
|
1658
2022
|
var pushCommand = new Command4("push").description("Upload local .env file to cloud").option(
|
|
1659
2023
|
"-e, --env <environment>",
|
|
1660
2024
|
"Target environment (development, staging, production)"
|
|
1661
|
-
).option("-f, --file <path>", "Input file path (default: .env)").option("--merge", "Merge with existing variables (default)").option("--replace", "Replace all existing variables").option(
|
|
2025
|
+
).option("-f, --file <path>", "Input file path (default: .env)").option("--merge", "Merge with existing variables (default)").option("--replace", "Replace all existing variables").option(
|
|
2026
|
+
"--format <format>",
|
|
2027
|
+
"Input format: env, json, yaml, docker-compose, aws, vercel, netlify",
|
|
2028
|
+
"env"
|
|
2029
|
+
).option(
|
|
2030
|
+
"--prefix <prefix>",
|
|
2031
|
+
"AWS Parameter Store path prefix (default: /project-name)"
|
|
2032
|
+
).option("--dry-run", "Show what would be uploaded without making changes").option("--force", "Skip confirmation").option("--project <name-or-id>", "Push to a specific linked project").action(async (options) => {
|
|
1662
2033
|
try {
|
|
1663
2034
|
if (!isAuthenticated()) {
|
|
1664
2035
|
throw notAuthenticated();
|
|
@@ -1740,9 +2111,25 @@ var pushCommand = new Command4("push").description("Upload local .env file to cl
|
|
|
1740
2111
|
}
|
|
1741
2112
|
}
|
|
1742
2113
|
const environment = options.env || defaultEnvironment || "development";
|
|
1743
|
-
const
|
|
2114
|
+
const fmt = options.format || "env";
|
|
2115
|
+
if (!ALL_FORMATS.includes(fmt)) {
|
|
2116
|
+
error(`Unknown format: ${fmt}. Supported: ${ALL_FORMATS.join(", ")}`);
|
|
2117
|
+
process.exit(1);
|
|
2118
|
+
}
|
|
2119
|
+
const inputPath = options.file || (fmt === "env" ? getEnvPathForEnvironment(environment) : getDefaultFilename(fmt, environment));
|
|
1744
2120
|
const mode = options.replace ? "replace" : "merge";
|
|
1745
|
-
|
|
2121
|
+
let localVars;
|
|
2122
|
+
if (fmt === "env") {
|
|
2123
|
+
localVars = readEnvFile(inputPath);
|
|
2124
|
+
} else {
|
|
2125
|
+
const { readFileSync: readFileSync4, existsSync: existsSync4 } = await import("fs");
|
|
2126
|
+
if (!existsSync4(inputPath)) {
|
|
2127
|
+
localVars = null;
|
|
2128
|
+
} else {
|
|
2129
|
+
const content = readFileSync4(inputPath, "utf-8");
|
|
2130
|
+
localVars = parse(content, fmt, { prefix: options.prefix });
|
|
2131
|
+
}
|
|
2132
|
+
}
|
|
1746
2133
|
if (!localVars) {
|
|
1747
2134
|
throw fileNotFound(inputPath);
|
|
1748
2135
|
}
|
|
@@ -2224,7 +2611,7 @@ var listCommand = new Command6("list").description("List resources").argument(
|
|
|
2224
2611
|
"[resource]",
|
|
2225
2612
|
"Resource type: projects, organizations, variables, linked",
|
|
2226
2613
|
"projects"
|
|
2227
|
-
).option("-o, --organization <id>", "Organization ID (for projects/variables)").option("-p, --project <id>", "Project ID (for variables)").option("-e, --env <environment>", "Environment filter (for variables)").option("--show-values", "Show actual variable values (masked by default)").option("--json", "Output as JSON").action(async (resource, options) => {
|
|
2614
|
+
).option("-o, --organization <id>", "Organization ID (for projects/variables)").option("-p, --project <id>", "Project ID (for variables)").option("-e, --env <environment>", "Environment filter (for variables)").option("-t, --tag <name>", "Filter by tag name (for variables)").option("--show-values", "Show actual variable values (masked by default)").option("--json", "Output as JSON").action(async (resource, options) => {
|
|
2228
2615
|
try {
|
|
2229
2616
|
if (!isAuthenticated()) {
|
|
2230
2617
|
throw notAuthenticated();
|
|
@@ -2408,36 +2795,47 @@ async function listVariables(api, projectConfig, options) {
|
|
|
2408
2795
|
metaProjectRole = response.meta?.projectRole;
|
|
2409
2796
|
return response.data || [];
|
|
2410
2797
|
});
|
|
2411
|
-
|
|
2412
|
-
|
|
2798
|
+
const tagFilter = options.tag?.toLowerCase();
|
|
2799
|
+
const filtered = tagFilter ? variables.filter(
|
|
2800
|
+
(v) => v.tags?.some((t) => t.name.toLowerCase() === tagFilter)
|
|
2801
|
+
) : variables;
|
|
2802
|
+
if (filtered.length === 0) {
|
|
2803
|
+
info(
|
|
2804
|
+
`No variables found${environment ? ` for ${environment}` : ""}${tagFilter ? ` with tag "${options.tag}"` : ""}.`
|
|
2805
|
+
);
|
|
2413
2806
|
return;
|
|
2414
2807
|
}
|
|
2415
2808
|
if (options.json) {
|
|
2416
|
-
const output =
|
|
2809
|
+
const output = filtered.map((v) => ({
|
|
2417
2810
|
...v,
|
|
2418
2811
|
value: options.showValues ? v.value : maskValue(v.value)
|
|
2419
2812
|
}));
|
|
2420
2813
|
console.log(JSON.stringify(output, null, 2));
|
|
2421
2814
|
return;
|
|
2422
2815
|
}
|
|
2423
|
-
header(
|
|
2816
|
+
header(
|
|
2817
|
+
`Variables${environment ? ` (${environment})` : ""}${tagFilter ? ` [tag: ${options.tag}]` : ""}`
|
|
2818
|
+
);
|
|
2424
2819
|
console.log();
|
|
2820
|
+
const hasTags = filtered.some((v) => v.tags && v.tags.length > 0);
|
|
2425
2821
|
table(
|
|
2426
|
-
|
|
2822
|
+
filtered.map((variable) => ({
|
|
2427
2823
|
key: variable.key,
|
|
2428
2824
|
value: options.showValues ? variable.value : maskValue(variable.value),
|
|
2429
2825
|
sensitive: variable.isSensitive ? chalk8.yellow("\u25CF") : "",
|
|
2826
|
+
tags: hasTags ? variable.tags?.map((t) => t.name).join(", ") || chalk8.dim("-") : "",
|
|
2430
2827
|
version: `v${variable.version}`
|
|
2431
2828
|
})),
|
|
2432
2829
|
[
|
|
2433
2830
|
{ key: "key", header: "Key" },
|
|
2434
2831
|
{ key: "value", header: "Value", width: 40 },
|
|
2435
2832
|
{ key: "sensitive", header: "" },
|
|
2833
|
+
...hasTags ? [{ key: "tags", header: "Tags", width: 25 }] : [],
|
|
2436
2834
|
{ key: "version", header: "Ver" }
|
|
2437
2835
|
]
|
|
2438
2836
|
);
|
|
2439
2837
|
console.log();
|
|
2440
|
-
console.log(chalk8.dim(`Total: ${
|
|
2838
|
+
console.log(chalk8.dim(`Total: ${filtered.length} variables`));
|
|
2441
2839
|
const role = getRole();
|
|
2442
2840
|
if (role) {
|
|
2443
2841
|
console.log(chalk8.dim(`Your org role: ${formatRole(role)}`));
|
|
@@ -3141,7 +3539,7 @@ var usageCommand = new Command11("usage").description("Show plan usage and limit
|
|
|
3141
3539
|
// src/lib/version-check.ts
|
|
3142
3540
|
import chalk13 from "chalk";
|
|
3143
3541
|
import Conf2 from "conf";
|
|
3144
|
-
var CLI_VERSION = true ? "1.
|
|
3542
|
+
var CLI_VERSION = true ? "1.4.1" : "0.0.0";
|
|
3145
3543
|
var CHECK_INTERVAL = 60 * 60 * 1e3;
|
|
3146
3544
|
var _cache = null;
|
|
3147
3545
|
function getCache() {
|