@clef-sh/core 0.1.10 → 0.1.11-beta.62
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.d.mts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +391 -199
- package/dist/index.js.map +4 -4
- package/dist/index.mjs +377 -166
- package/dist/index.mjs.map +4 -4
- package/dist/manifest/io.d.ts +3 -0
- package/dist/manifest/io.d.ts.map +1 -0
- package/dist/migration/backend.d.ts +41 -0
- package/dist/migration/backend.d.ts.map +1 -0
- package/dist/recipients/index.d.ts.map +1 -1
- package/dist/types/index.d.ts +5 -3
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -695,9 +695,21 @@ var ManifestParser = class {
|
|
|
695
695
|
}
|
|
696
696
|
};
|
|
697
697
|
|
|
698
|
+
// src/manifest/io.ts
|
|
699
|
+
import * as fs2 from "fs";
|
|
700
|
+
import * as path from "path";
|
|
701
|
+
import * as YAML2 from "yaml";
|
|
702
|
+
function readManifestYaml(repoRoot) {
|
|
703
|
+
const raw = fs2.readFileSync(path.join(repoRoot, CLEF_MANIFEST_FILENAME), "utf-8");
|
|
704
|
+
return YAML2.parse(raw);
|
|
705
|
+
}
|
|
706
|
+
function writeManifestYaml(repoRoot, doc) {
|
|
707
|
+
fs2.writeFileSync(path.join(repoRoot, CLEF_MANIFEST_FILENAME), YAML2.stringify(doc), "utf-8");
|
|
708
|
+
}
|
|
709
|
+
|
|
698
710
|
// src/scanner/index.ts
|
|
699
|
-
import * as
|
|
700
|
-
import * as
|
|
711
|
+
import * as fs4 from "fs";
|
|
712
|
+
import * as path3 from "path";
|
|
701
713
|
|
|
702
714
|
// src/scanner/patterns.ts
|
|
703
715
|
var PATTERNS = [
|
|
@@ -757,12 +769,12 @@ function matchPatterns(line, lineNumber, filePath) {
|
|
|
757
769
|
}
|
|
758
770
|
|
|
759
771
|
// src/scanner/ignore.ts
|
|
760
|
-
import * as
|
|
761
|
-
import * as
|
|
772
|
+
import * as fs3 from "fs";
|
|
773
|
+
import * as path2 from "path";
|
|
762
774
|
function loadIgnoreRules(repoRoot) {
|
|
763
|
-
const ignorePath =
|
|
775
|
+
const ignorePath = path2.join(repoRoot, ".clefignore");
|
|
764
776
|
try {
|
|
765
|
-
const content =
|
|
777
|
+
const content = fs3.readFileSync(ignorePath, "utf-8");
|
|
766
778
|
return parseIgnoreContent(content);
|
|
767
779
|
} catch {
|
|
768
780
|
return { files: [], patterns: [], paths: [] };
|
|
@@ -845,9 +857,9 @@ var ScanRunner = class {
|
|
|
845
857
|
for (const ns of manifest.namespaces) {
|
|
846
858
|
for (const env of manifest.environments) {
|
|
847
859
|
const relPath = manifest.file_pattern.replace("{namespace}", ns.name).replace("{environment}", env.name);
|
|
848
|
-
const absPath =
|
|
849
|
-
if (
|
|
850
|
-
const content =
|
|
860
|
+
const absPath = path3.join(repoRoot, relPath);
|
|
861
|
+
if (fs4.existsSync(absPath)) {
|
|
862
|
+
const content = fs4.readFileSync(absPath, "utf-8");
|
|
851
863
|
if (!content.includes("sops:") && !content.includes('"sops"')) {
|
|
852
864
|
unencryptedMatrixFiles.push(relPath);
|
|
853
865
|
}
|
|
@@ -863,8 +875,8 @@ var ScanRunner = class {
|
|
|
863
875
|
filesToScan = await this.getAllTrackedFiles(repoRoot);
|
|
864
876
|
}
|
|
865
877
|
for (const relFile of filesToScan) {
|
|
866
|
-
const absFile =
|
|
867
|
-
const relPath =
|
|
878
|
+
const absFile = path3.isAbsolute(relFile) ? relFile : path3.join(repoRoot, relFile);
|
|
879
|
+
const relPath = path3.relative(repoRoot, absFile).replace(/\\/g, "/");
|
|
868
880
|
if (this.shouldAlwaysSkip(relPath)) {
|
|
869
881
|
filesSkipped++;
|
|
870
882
|
continue;
|
|
@@ -873,13 +885,13 @@ var ScanRunner = class {
|
|
|
873
885
|
filesSkipped++;
|
|
874
886
|
continue;
|
|
875
887
|
}
|
|
876
|
-
if (!
|
|
888
|
+
if (!fs4.existsSync(absFile)) {
|
|
877
889
|
filesSkipped++;
|
|
878
890
|
continue;
|
|
879
891
|
}
|
|
880
892
|
let stat;
|
|
881
893
|
try {
|
|
882
|
-
stat =
|
|
894
|
+
stat = fs4.statSync(absFile);
|
|
883
895
|
} catch {
|
|
884
896
|
filesSkipped++;
|
|
885
897
|
continue;
|
|
@@ -893,7 +905,7 @@ var ScanRunner = class {
|
|
|
893
905
|
continue;
|
|
894
906
|
}
|
|
895
907
|
filesScanned++;
|
|
896
|
-
const content =
|
|
908
|
+
const content = fs4.readFileSync(absFile, "utf-8");
|
|
897
909
|
const lines = content.split("\n");
|
|
898
910
|
for (let i = 0; i < lines.length; i++) {
|
|
899
911
|
const line = lines[i];
|
|
@@ -935,10 +947,10 @@ var ScanRunner = class {
|
|
|
935
947
|
}
|
|
936
948
|
isBinary(filePath) {
|
|
937
949
|
try {
|
|
938
|
-
const fd =
|
|
950
|
+
const fd = fs4.openSync(filePath, "r");
|
|
939
951
|
const buf = Buffer.alloc(512);
|
|
940
|
-
const bytesRead =
|
|
941
|
-
|
|
952
|
+
const bytesRead = fs4.readSync(fd, buf, 0, 512, 0);
|
|
953
|
+
fs4.closeSync(fd);
|
|
942
954
|
for (let i = 0; i < bytesRead; i++) {
|
|
943
955
|
if (buf[i] === 0) return true;
|
|
944
956
|
}
|
|
@@ -978,13 +990,13 @@ var ScanRunner = class {
|
|
|
978
990
|
async getFilesInPaths(repoRoot, paths) {
|
|
979
991
|
const files = [];
|
|
980
992
|
for (const p of paths) {
|
|
981
|
-
const absPath =
|
|
982
|
-
if (!
|
|
983
|
-
const stat =
|
|
993
|
+
const absPath = path3.isAbsolute(p) ? p : path3.join(repoRoot, p);
|
|
994
|
+
if (!fs4.existsSync(absPath)) continue;
|
|
995
|
+
const stat = fs4.statSync(absPath);
|
|
984
996
|
if (stat.isDirectory()) {
|
|
985
997
|
files.push(...this.walkDir(absPath, repoRoot));
|
|
986
998
|
} else {
|
|
987
|
-
files.push(
|
|
999
|
+
files.push(path3.relative(repoRoot, absPath).replace(/\\/g, "/"));
|
|
988
1000
|
}
|
|
989
1001
|
}
|
|
990
1002
|
return files;
|
|
@@ -1000,13 +1012,13 @@ var ScanRunner = class {
|
|
|
1000
1012
|
const files = [];
|
|
1001
1013
|
let entries;
|
|
1002
1014
|
try {
|
|
1003
|
-
entries =
|
|
1015
|
+
entries = fs4.readdirSync(dir, { withFileTypes: true });
|
|
1004
1016
|
} catch {
|
|
1005
1017
|
return files;
|
|
1006
1018
|
}
|
|
1007
1019
|
for (const entry of entries) {
|
|
1008
|
-
const fullPath =
|
|
1009
|
-
const relPath =
|
|
1020
|
+
const fullPath = path3.join(dir, entry.name);
|
|
1021
|
+
const relPath = path3.relative(repoRoot, fullPath).replace(/\\/g, "/");
|
|
1010
1022
|
if (entry.isDirectory()) {
|
|
1011
1023
|
if (!ALWAYS_SKIP_DIRS.includes(entry.name)) {
|
|
1012
1024
|
files.push(...this.walkDir(fullPath, repoRoot));
|
|
@@ -1020,29 +1032,29 @@ var ScanRunner = class {
|
|
|
1020
1032
|
};
|
|
1021
1033
|
|
|
1022
1034
|
// src/matrix/manager.ts
|
|
1023
|
-
import * as
|
|
1024
|
-
import * as
|
|
1025
|
-
import * as
|
|
1035
|
+
import * as fs7 from "fs";
|
|
1036
|
+
import * as path5 from "path";
|
|
1037
|
+
import * as YAML5 from "yaml";
|
|
1026
1038
|
|
|
1027
1039
|
// src/pending/metadata.ts
|
|
1028
|
-
import * as
|
|
1029
|
-
import * as
|
|
1040
|
+
import * as fs5 from "fs";
|
|
1041
|
+
import * as path4 from "path";
|
|
1030
1042
|
import * as crypto from "crypto";
|
|
1031
|
-
import * as
|
|
1043
|
+
import * as YAML3 from "yaml";
|
|
1032
1044
|
function metadataPath(encryptedFilePath) {
|
|
1033
|
-
const dir =
|
|
1034
|
-
const base =
|
|
1035
|
-
return
|
|
1045
|
+
const dir = path4.dirname(encryptedFilePath);
|
|
1046
|
+
const base = path4.basename(encryptedFilePath).replace(/\.enc\.(yaml|json)$/, "");
|
|
1047
|
+
return path4.join(dir, `${base}.clef-meta.yaml`);
|
|
1036
1048
|
}
|
|
1037
1049
|
var HEADER_COMMENT = "# Managed by Clef. Do not edit manually.\n";
|
|
1038
1050
|
async function loadMetadata(filePath) {
|
|
1039
1051
|
const metaPath = metadataPath(filePath);
|
|
1040
1052
|
try {
|
|
1041
|
-
if (!
|
|
1053
|
+
if (!fs5.existsSync(metaPath)) {
|
|
1042
1054
|
return { version: 1, pending: [] };
|
|
1043
1055
|
}
|
|
1044
|
-
const content =
|
|
1045
|
-
const parsed =
|
|
1056
|
+
const content = fs5.readFileSync(metaPath, "utf-8");
|
|
1057
|
+
const parsed = YAML3.parse(content);
|
|
1046
1058
|
if (!parsed || !Array.isArray(parsed.pending)) {
|
|
1047
1059
|
return { version: 1, pending: [] };
|
|
1048
1060
|
}
|
|
@@ -1060,9 +1072,9 @@ async function loadMetadata(filePath) {
|
|
|
1060
1072
|
}
|
|
1061
1073
|
async function saveMetadata(filePath, metadata) {
|
|
1062
1074
|
const metaPath = metadataPath(filePath);
|
|
1063
|
-
const dir =
|
|
1064
|
-
if (!
|
|
1065
|
-
|
|
1075
|
+
const dir = path4.dirname(metaPath);
|
|
1076
|
+
if (!fs5.existsSync(dir)) {
|
|
1077
|
+
fs5.mkdirSync(dir, { recursive: true });
|
|
1066
1078
|
}
|
|
1067
1079
|
const data = {
|
|
1068
1080
|
version: metadata.version,
|
|
@@ -1072,7 +1084,7 @@ async function saveMetadata(filePath, metadata) {
|
|
|
1072
1084
|
setBy: p.setBy
|
|
1073
1085
|
}))
|
|
1074
1086
|
};
|
|
1075
|
-
|
|
1087
|
+
fs5.writeFileSync(metaPath, HEADER_COMMENT + YAML3.stringify(data), "utf-8");
|
|
1076
1088
|
}
|
|
1077
1089
|
async function markPending(filePath, keys, setBy) {
|
|
1078
1090
|
const metadata = await loadMetadata(filePath);
|
|
@@ -1113,12 +1125,12 @@ async function markPendingWithRetry(filePath, keys, setBy, retryDelayMs = 200) {
|
|
|
1113
1125
|
}
|
|
1114
1126
|
|
|
1115
1127
|
// src/sops/keys.ts
|
|
1116
|
-
import * as
|
|
1117
|
-
import * as
|
|
1128
|
+
import * as fs6 from "fs";
|
|
1129
|
+
import * as YAML4 from "yaml";
|
|
1118
1130
|
function readSopsKeyNames(filePath) {
|
|
1119
1131
|
try {
|
|
1120
|
-
const raw =
|
|
1121
|
-
const parsed =
|
|
1132
|
+
const raw = fs6.readFileSync(filePath, "utf-8");
|
|
1133
|
+
const parsed = YAML4.parse(raw);
|
|
1122
1134
|
if (parsed === null || parsed === void 0 || typeof parsed !== "object") return null;
|
|
1123
1135
|
return Object.keys(parsed).filter((k) => k !== "sops");
|
|
1124
1136
|
} catch {
|
|
@@ -1140,12 +1152,12 @@ var MatrixManager = class {
|
|
|
1140
1152
|
for (const ns of manifest.namespaces) {
|
|
1141
1153
|
for (const env of manifest.environments) {
|
|
1142
1154
|
const relativePath = manifest.file_pattern.replace("{namespace}", ns.name).replace("{environment}", env.name);
|
|
1143
|
-
const filePath =
|
|
1155
|
+
const filePath = path5.join(repoRoot, relativePath);
|
|
1144
1156
|
cells.push({
|
|
1145
1157
|
namespace: ns.name,
|
|
1146
1158
|
environment: env.name,
|
|
1147
1159
|
filePath,
|
|
1148
|
-
exists:
|
|
1160
|
+
exists: fs7.existsSync(filePath)
|
|
1149
1161
|
});
|
|
1150
1162
|
}
|
|
1151
1163
|
}
|
|
@@ -1168,9 +1180,9 @@ var MatrixManager = class {
|
|
|
1168
1180
|
* @param manifest - Parsed manifest used to determine the encryption backend.
|
|
1169
1181
|
*/
|
|
1170
1182
|
async scaffoldCell(cell, sopsClient, manifest) {
|
|
1171
|
-
const dir =
|
|
1172
|
-
if (!
|
|
1173
|
-
|
|
1183
|
+
const dir = path5.dirname(cell.filePath);
|
|
1184
|
+
if (!fs7.existsSync(dir)) {
|
|
1185
|
+
fs7.mkdirSync(dir, { recursive: true });
|
|
1174
1186
|
}
|
|
1175
1187
|
await sopsClient.encrypt(cell.filePath, {}, manifest, cell.environment);
|
|
1176
1188
|
}
|
|
@@ -1246,8 +1258,8 @@ var MatrixManager = class {
|
|
|
1246
1258
|
*/
|
|
1247
1259
|
readLastModified(filePath) {
|
|
1248
1260
|
try {
|
|
1249
|
-
const raw =
|
|
1250
|
-
const parsed =
|
|
1261
|
+
const raw = fs7.readFileSync(filePath, "utf-8");
|
|
1262
|
+
const parsed = YAML5.parse(raw);
|
|
1251
1263
|
const sops = parsed?.sops;
|
|
1252
1264
|
if (sops?.lastmodified) return new Date(String(sops.lastmodified));
|
|
1253
1265
|
return null;
|
|
@@ -1268,8 +1280,8 @@ var MatrixManager = class {
|
|
|
1268
1280
|
};
|
|
1269
1281
|
|
|
1270
1282
|
// src/schema/validator.ts
|
|
1271
|
-
import * as
|
|
1272
|
-
import * as
|
|
1283
|
+
import * as fs8 from "fs";
|
|
1284
|
+
import * as YAML6 from "yaml";
|
|
1273
1285
|
var SchemaValidator = class {
|
|
1274
1286
|
/**
|
|
1275
1287
|
* Read and parse a YAML schema file from disk.
|
|
@@ -1281,13 +1293,13 @@ var SchemaValidator = class {
|
|
|
1281
1293
|
loadSchema(filePath) {
|
|
1282
1294
|
let raw;
|
|
1283
1295
|
try {
|
|
1284
|
-
raw =
|
|
1296
|
+
raw = fs8.readFileSync(filePath, "utf-8");
|
|
1285
1297
|
} catch {
|
|
1286
1298
|
throw new SchemaLoadError(`Could not read schema file at '${filePath}'.`, filePath);
|
|
1287
1299
|
}
|
|
1288
1300
|
let parsed;
|
|
1289
1301
|
try {
|
|
1290
|
-
parsed =
|
|
1302
|
+
parsed = YAML6.parse(raw);
|
|
1291
1303
|
} catch {
|
|
1292
1304
|
throw new SchemaLoadError(`Schema file '${filePath}' contains invalid YAML.`, filePath);
|
|
1293
1305
|
}
|
|
@@ -1415,7 +1427,7 @@ var SchemaValidator = class {
|
|
|
1415
1427
|
};
|
|
1416
1428
|
|
|
1417
1429
|
// src/diff/engine.ts
|
|
1418
|
-
import * as
|
|
1430
|
+
import * as path6 from "path";
|
|
1419
1431
|
var DiffEngine = class {
|
|
1420
1432
|
/**
|
|
1421
1433
|
* Compare two in-memory value maps and produce a sorted diff result.
|
|
@@ -1472,11 +1484,11 @@ var DiffEngine = class {
|
|
|
1472
1484
|
* @throws {@link SopsDecryptionError} If either file cannot be decrypted.
|
|
1473
1485
|
*/
|
|
1474
1486
|
async diffFiles(namespace, envA, envB, manifest, sopsClient, repoRoot) {
|
|
1475
|
-
const fileA =
|
|
1487
|
+
const fileA = path6.join(
|
|
1476
1488
|
repoRoot,
|
|
1477
1489
|
manifest.file_pattern.replace("{namespace}", namespace).replace("{environment}", envA)
|
|
1478
1490
|
);
|
|
1479
|
-
const fileB =
|
|
1491
|
+
const fileB = path6.join(
|
|
1480
1492
|
repoRoot,
|
|
1481
1493
|
manifest.file_pattern.replace("{namespace}", namespace).replace("{environment}", envB)
|
|
1482
1494
|
);
|
|
@@ -1489,7 +1501,7 @@ var DiffEngine = class {
|
|
|
1489
1501
|
};
|
|
1490
1502
|
|
|
1491
1503
|
// src/bulk/ops.ts
|
|
1492
|
-
import * as
|
|
1504
|
+
import * as path7 from "path";
|
|
1493
1505
|
var BulkOps = class {
|
|
1494
1506
|
/**
|
|
1495
1507
|
* Set a key to different values in multiple environments at once.
|
|
@@ -1508,7 +1520,7 @@ var BulkOps = class {
|
|
|
1508
1520
|
if (!(env.name in values)) {
|
|
1509
1521
|
continue;
|
|
1510
1522
|
}
|
|
1511
|
-
const filePath =
|
|
1523
|
+
const filePath = path7.join(
|
|
1512
1524
|
repoRoot,
|
|
1513
1525
|
manifest.file_pattern.replace("{namespace}", namespace).replace("{environment}", env.name)
|
|
1514
1526
|
);
|
|
@@ -1542,7 +1554,7 @@ Successfully updated ${Object.keys(values).length - errors.length} environment(s
|
|
|
1542
1554
|
async deleteAcrossEnvironments(namespace, key, manifest, sopsClient, repoRoot) {
|
|
1543
1555
|
const errors = [];
|
|
1544
1556
|
for (const env of manifest.environments) {
|
|
1545
|
-
const filePath =
|
|
1557
|
+
const filePath = path7.join(
|
|
1546
1558
|
repoRoot,
|
|
1547
1559
|
manifest.file_pattern.replace("{namespace}", namespace).replace("{environment}", env.name)
|
|
1548
1560
|
);
|
|
@@ -1588,8 +1600,8 @@ ${details}`
|
|
|
1588
1600
|
};
|
|
1589
1601
|
|
|
1590
1602
|
// src/git/integration.ts
|
|
1591
|
-
import * as
|
|
1592
|
-
import * as
|
|
1603
|
+
import * as fs9 from "fs";
|
|
1604
|
+
import * as path8 from "path";
|
|
1593
1605
|
var PRE_COMMIT_HOOK = `#!/bin/sh
|
|
1594
1606
|
# Clef pre-commit hook \u2014 blocks commits of files missing SOPS encryption metadata
|
|
1595
1607
|
# and scans staged files for plaintext secrets.
|
|
@@ -1796,15 +1808,15 @@ var GitIntegration = class {
|
|
|
1796
1808
|
cwd: repoRoot
|
|
1797
1809
|
});
|
|
1798
1810
|
const gitConfig = configResult.exitCode === 0 && configResult.stdout.trim().length > 0;
|
|
1799
|
-
const attrFilePath =
|
|
1800
|
-
const attrContent =
|
|
1811
|
+
const attrFilePath = path8.join(repoRoot, ".gitattributes");
|
|
1812
|
+
const attrContent = fs9.existsSync(attrFilePath) ? fs9.readFileSync(attrFilePath, "utf-8") : "";
|
|
1801
1813
|
const gitattributes = attrContent.includes("merge=sops");
|
|
1802
1814
|
return { gitConfig, gitattributes };
|
|
1803
1815
|
}
|
|
1804
1816
|
async ensureGitattributes(repoRoot) {
|
|
1805
|
-
const attrPath =
|
|
1817
|
+
const attrPath = path8.join(repoRoot, ".gitattributes");
|
|
1806
1818
|
const mergeRule = "*.enc.yaml merge=sops\n*.enc.json merge=sops";
|
|
1807
|
-
const existing =
|
|
1819
|
+
const existing = fs9.existsSync(attrPath) ? fs9.readFileSync(attrPath, "utf-8") : "";
|
|
1808
1820
|
if (existing.includes("merge=sops")) {
|
|
1809
1821
|
return;
|
|
1810
1822
|
}
|
|
@@ -1831,7 +1843,7 @@ ${mergeRule}
|
|
|
1831
1843
|
* @throws {@link GitOperationError} On failure.
|
|
1832
1844
|
*/
|
|
1833
1845
|
async installPreCommitHook(repoRoot) {
|
|
1834
|
-
const hookPath =
|
|
1846
|
+
const hookPath = path8.join(repoRoot, ".git", "hooks", "pre-commit");
|
|
1835
1847
|
const result = await this.runner.run("tee", [hookPath], {
|
|
1836
1848
|
stdin: PRE_COMMIT_HOOK,
|
|
1837
1849
|
cwd: repoRoot
|
|
@@ -1852,18 +1864,18 @@ ${mergeRule}
|
|
|
1852
1864
|
};
|
|
1853
1865
|
|
|
1854
1866
|
// src/sops/client.ts
|
|
1855
|
-
import * as
|
|
1867
|
+
import * as fs12 from "fs";
|
|
1856
1868
|
import * as net from "net";
|
|
1857
1869
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
1858
|
-
import * as
|
|
1870
|
+
import * as YAML7 from "yaml";
|
|
1859
1871
|
|
|
1860
1872
|
// src/sops/resolver.ts
|
|
1861
|
-
import * as
|
|
1862
|
-
import * as
|
|
1873
|
+
import * as fs11 from "fs";
|
|
1874
|
+
import * as path10 from "path";
|
|
1863
1875
|
|
|
1864
1876
|
// src/sops/bundled.ts
|
|
1865
|
-
import * as
|
|
1866
|
-
import * as
|
|
1877
|
+
import * as fs10 from "fs";
|
|
1878
|
+
import * as path9 from "path";
|
|
1867
1879
|
function tryBundled() {
|
|
1868
1880
|
const platform = process.platform;
|
|
1869
1881
|
const arch = process.arch;
|
|
@@ -1875,9 +1887,9 @@ function tryBundled() {
|
|
|
1875
1887
|
const binName = platform === "win32" ? "sops.exe" : "sops";
|
|
1876
1888
|
try {
|
|
1877
1889
|
const packageMain = __require.resolve(`${packageName}/package.json`);
|
|
1878
|
-
const packageDir =
|
|
1879
|
-
const binPath =
|
|
1880
|
-
return
|
|
1890
|
+
const packageDir = path9.dirname(packageMain);
|
|
1891
|
+
const binPath = path9.join(packageDir, "bin", binName);
|
|
1892
|
+
return fs10.existsSync(binPath) ? binPath : null;
|
|
1881
1893
|
} catch {
|
|
1882
1894
|
return null;
|
|
1883
1895
|
}
|
|
@@ -1885,7 +1897,7 @@ function tryBundled() {
|
|
|
1885
1897
|
|
|
1886
1898
|
// src/sops/resolver.ts
|
|
1887
1899
|
function validateSopsPath(candidate) {
|
|
1888
|
-
if (!
|
|
1900
|
+
if (!path10.isAbsolute(candidate)) {
|
|
1889
1901
|
throw new Error(`CLEF_SOPS_PATH must be an absolute path, got '${candidate}'.`);
|
|
1890
1902
|
}
|
|
1891
1903
|
const segments = candidate.split(/[/\\]/);
|
|
@@ -1901,7 +1913,7 @@ function resolveSopsPath() {
|
|
|
1901
1913
|
const envPath = process.env.CLEF_SOPS_PATH?.trim();
|
|
1902
1914
|
if (envPath) {
|
|
1903
1915
|
validateSopsPath(envPath);
|
|
1904
|
-
if (!
|
|
1916
|
+
if (!fs11.existsSync(envPath)) {
|
|
1905
1917
|
throw new Error(`CLEF_SOPS_PATH points to '${envPath}' but the file does not exist.`);
|
|
1906
1918
|
}
|
|
1907
1919
|
cached = { path: envPath, source: "env" };
|
|
@@ -2109,7 +2121,7 @@ var SopsClient = class {
|
|
|
2109
2121
|
}
|
|
2110
2122
|
let parsed;
|
|
2111
2123
|
try {
|
|
2112
|
-
parsed =
|
|
2124
|
+
parsed = YAML7.parse(result.stdout) ?? {};
|
|
2113
2125
|
} catch {
|
|
2114
2126
|
throw new SopsDecryptionError(
|
|
2115
2127
|
`Decrypted content of '${filePath}' is not valid YAML.`,
|
|
@@ -2136,7 +2148,7 @@ var SopsClient = class {
|
|
|
2136
2148
|
async encrypt(filePath, values, manifest, environment) {
|
|
2137
2149
|
await assertSops(this.runner, this.sopsCommand);
|
|
2138
2150
|
const fmt = formatFromPath(filePath);
|
|
2139
|
-
const content = fmt === "json" ? JSON.stringify(values, null, 2) :
|
|
2151
|
+
const content = fmt === "json" ? JSON.stringify(values, null, 2) : YAML7.stringify(values);
|
|
2140
2152
|
const args = this.buildEncryptArgs(filePath, manifest, environment);
|
|
2141
2153
|
const env = this.buildSopsEnv();
|
|
2142
2154
|
let inputArg;
|
|
@@ -2180,7 +2192,7 @@ var SopsClient = class {
|
|
|
2180
2192
|
);
|
|
2181
2193
|
}
|
|
2182
2194
|
try {
|
|
2183
|
-
|
|
2195
|
+
fs12.writeFileSync(filePath, result.stdout);
|
|
2184
2196
|
} catch {
|
|
2185
2197
|
throw new SopsEncryptionError(`Failed to write encrypted data to '${filePath}'.`, filePath);
|
|
2186
2198
|
}
|
|
@@ -2296,7 +2308,7 @@ var SopsClient = class {
|
|
|
2296
2308
|
if (!this.ageKey && !this.ageKeyFile) return "key-not-found";
|
|
2297
2309
|
let keyContent;
|
|
2298
2310
|
try {
|
|
2299
|
-
keyContent = this.ageKey ??
|
|
2311
|
+
keyContent = this.ageKey ?? fs12.readFileSync(this.ageKeyFile, "utf-8");
|
|
2300
2312
|
} catch {
|
|
2301
2313
|
return "key-not-found";
|
|
2302
2314
|
}
|
|
@@ -2313,7 +2325,7 @@ var SopsClient = class {
|
|
|
2313
2325
|
parseMetadataFromFile(filePath) {
|
|
2314
2326
|
let content;
|
|
2315
2327
|
try {
|
|
2316
|
-
content =
|
|
2328
|
+
content = fs12.readFileSync(filePath, "utf-8");
|
|
2317
2329
|
} catch {
|
|
2318
2330
|
throw new SopsDecryptionError(
|
|
2319
2331
|
`Could not read file '${filePath}' to extract SOPS metadata.`,
|
|
@@ -2322,7 +2334,7 @@ var SopsClient = class {
|
|
|
2322
2334
|
}
|
|
2323
2335
|
let parsed;
|
|
2324
2336
|
try {
|
|
2325
|
-
parsed =
|
|
2337
|
+
parsed = YAML7.parse(content);
|
|
2326
2338
|
} catch {
|
|
2327
2339
|
throw new SopsDecryptionError(
|
|
2328
2340
|
`File '${filePath}' is not valid YAML. Cannot extract SOPS metadata.`,
|
|
@@ -2417,7 +2429,7 @@ var SopsClient = class {
|
|
|
2417
2429
|
};
|
|
2418
2430
|
|
|
2419
2431
|
// src/lint/runner.ts
|
|
2420
|
-
import * as
|
|
2432
|
+
import * as path11 from "path";
|
|
2421
2433
|
var LintRunner = class {
|
|
2422
2434
|
constructor(matrixManager, schemaValidator, sopsClient) {
|
|
2423
2435
|
this.matrixManager = matrixManager;
|
|
@@ -2517,7 +2529,7 @@ var LintRunner = class {
|
|
|
2517
2529
|
}
|
|
2518
2530
|
const ns = manifest.namespaces.find((n) => n.name === cell.namespace);
|
|
2519
2531
|
if (ns?.schema) {
|
|
2520
|
-
const schemaPath =
|
|
2532
|
+
const schemaPath = path11.join(repoRoot, ns.schema);
|
|
2521
2533
|
try {
|
|
2522
2534
|
const schema = this.schemaValidator.loadSchema(schemaPath);
|
|
2523
2535
|
const result = this.schemaValidator.validate(decrypted.values, schema);
|
|
@@ -2750,14 +2762,14 @@ Use 'clef exec' to inject secrets directly into a process, or 'clef export --for
|
|
|
2750
2762
|
};
|
|
2751
2763
|
|
|
2752
2764
|
// src/import/index.ts
|
|
2753
|
-
import * as
|
|
2765
|
+
import * as path13 from "path";
|
|
2754
2766
|
|
|
2755
2767
|
// src/import/parsers.ts
|
|
2756
|
-
import * as
|
|
2757
|
-
import * as
|
|
2768
|
+
import * as path12 from "path";
|
|
2769
|
+
import * as YAML8 from "yaml";
|
|
2758
2770
|
function detectFormat(filePath, content) {
|
|
2759
|
-
const base =
|
|
2760
|
-
const ext =
|
|
2771
|
+
const base = path12.basename(filePath);
|
|
2772
|
+
const ext = path12.extname(filePath).toLowerCase();
|
|
2761
2773
|
if (base === ".env" || base.startsWith(".env.")) {
|
|
2762
2774
|
return "dotenv";
|
|
2763
2775
|
}
|
|
@@ -2778,7 +2790,7 @@ function detectFormat(filePath, content) {
|
|
|
2778
2790
|
} catch {
|
|
2779
2791
|
}
|
|
2780
2792
|
try {
|
|
2781
|
-
const parsed =
|
|
2793
|
+
const parsed = YAML8.parse(content);
|
|
2782
2794
|
if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
2783
2795
|
return "yaml";
|
|
2784
2796
|
}
|
|
@@ -2859,7 +2871,7 @@ function parseJson(content) {
|
|
|
2859
2871
|
function parseYaml(content) {
|
|
2860
2872
|
let parsed;
|
|
2861
2873
|
try {
|
|
2862
|
-
parsed =
|
|
2874
|
+
parsed = YAML8.parse(content);
|
|
2863
2875
|
} catch (err) {
|
|
2864
2876
|
throw new Error(`Invalid YAML: ${err.message}`);
|
|
2865
2877
|
}
|
|
@@ -2893,7 +2905,7 @@ function parseYaml(content) {
|
|
|
2893
2905
|
}
|
|
2894
2906
|
return { pairs, format: "yaml", skipped, warnings };
|
|
2895
2907
|
}
|
|
2896
|
-
function
|
|
2908
|
+
function parse9(content, format, filePath) {
|
|
2897
2909
|
const resolved = format === "auto" ? detectFormat(filePath ?? "", content) : format;
|
|
2898
2910
|
switch (resolved) {
|
|
2899
2911
|
case "dotenv":
|
|
@@ -2922,11 +2934,11 @@ var ImportRunner = class {
|
|
|
2922
2934
|
*/
|
|
2923
2935
|
async import(target, sourcePath, content, manifest, repoRoot, options) {
|
|
2924
2936
|
const [ns, env] = target.split("/");
|
|
2925
|
-
const filePath =
|
|
2937
|
+
const filePath = path13.join(
|
|
2926
2938
|
repoRoot,
|
|
2927
2939
|
manifest.file_pattern.replace("{namespace}", ns).replace("{environment}", env)
|
|
2928
2940
|
);
|
|
2929
|
-
const parsed =
|
|
2941
|
+
const parsed = parse9(content, options.format ?? "auto", sourcePath ?? "");
|
|
2930
2942
|
let candidates = Object.entries(parsed.pairs);
|
|
2931
2943
|
if (options.prefix) {
|
|
2932
2944
|
const prefix = options.prefix;
|
|
@@ -2980,9 +2992,8 @@ var ImportRunner = class {
|
|
|
2980
2992
|
};
|
|
2981
2993
|
|
|
2982
2994
|
// src/recipients/index.ts
|
|
2983
|
-
import * as
|
|
2984
|
-
import * as
|
|
2985
|
-
import * as YAML8 from "yaml";
|
|
2995
|
+
import * as fs13 from "fs";
|
|
2996
|
+
import * as path14 from "path";
|
|
2986
2997
|
function parseRecipientEntry(entry) {
|
|
2987
2998
|
if (typeof entry === "string") {
|
|
2988
2999
|
return { key: entry };
|
|
@@ -3003,15 +3014,6 @@ function toRecipient(entry) {
|
|
|
3003
3014
|
...entry.label ? { label: entry.label } : {}
|
|
3004
3015
|
};
|
|
3005
3016
|
}
|
|
3006
|
-
function readManifestYaml(repoRoot) {
|
|
3007
|
-
const manifestPath = path13.join(repoRoot, CLEF_MANIFEST_FILENAME);
|
|
3008
|
-
const raw = fs12.readFileSync(manifestPath, "utf-8");
|
|
3009
|
-
return YAML8.parse(raw);
|
|
3010
|
-
}
|
|
3011
|
-
function writeManifestYaml(repoRoot, doc) {
|
|
3012
|
-
const manifestPath = path13.join(repoRoot, CLEF_MANIFEST_FILENAME);
|
|
3013
|
-
fs12.writeFileSync(manifestPath, YAML8.stringify(doc), "utf-8");
|
|
3014
|
-
}
|
|
3015
3017
|
function getRecipientsArray(doc) {
|
|
3016
3018
|
const sops = doc.sops;
|
|
3017
3019
|
if (!sops) return [];
|
|
@@ -3110,8 +3112,8 @@ var RecipientManager = class {
|
|
|
3110
3112
|
if (currentKeys.includes(normalizedKey)) {
|
|
3111
3113
|
throw new Error(`Recipient '${keyPreview(normalizedKey)}' is already present.`);
|
|
3112
3114
|
}
|
|
3113
|
-
const manifestPath =
|
|
3114
|
-
const manifestBackup =
|
|
3115
|
+
const manifestPath = path14.join(repoRoot, CLEF_MANIFEST_FILENAME);
|
|
3116
|
+
const manifestBackup = fs13.readFileSync(manifestPath, "utf-8");
|
|
3115
3117
|
const recipients = environment ? ensureEnvironmentRecipientsArray(doc, environment) : ensureRecipientsArray(doc);
|
|
3116
3118
|
if (label) {
|
|
3117
3119
|
recipients.push({ key: normalizedKey, label });
|
|
@@ -3126,16 +3128,16 @@ var RecipientManager = class {
|
|
|
3126
3128
|
const fileBackups = /* @__PURE__ */ new Map();
|
|
3127
3129
|
for (const cell of cells) {
|
|
3128
3130
|
try {
|
|
3129
|
-
fileBackups.set(cell.filePath,
|
|
3131
|
+
fileBackups.set(cell.filePath, fs13.readFileSync(cell.filePath, "utf-8"));
|
|
3130
3132
|
await this.encryption.addRecipient(cell.filePath, normalizedKey);
|
|
3131
3133
|
reEncryptedFiles.push(cell.filePath);
|
|
3132
3134
|
} catch {
|
|
3133
3135
|
failedFiles.push(cell.filePath);
|
|
3134
|
-
|
|
3136
|
+
fs13.writeFileSync(manifestPath, manifestBackup, "utf-8");
|
|
3135
3137
|
for (const reEncryptedFile of reEncryptedFiles) {
|
|
3136
3138
|
const backup = fileBackups.get(reEncryptedFile);
|
|
3137
3139
|
if (backup) {
|
|
3138
|
-
|
|
3140
|
+
fs13.writeFileSync(reEncryptedFile, backup, "utf-8");
|
|
3139
3141
|
}
|
|
3140
3142
|
}
|
|
3141
3143
|
const restoredDoc = readManifestYaml(repoRoot);
|
|
@@ -3188,8 +3190,8 @@ var RecipientManager = class {
|
|
|
3188
3190
|
throw new Error(`Recipient '${keyPreview(trimmedKey)}' is not in the manifest.`);
|
|
3189
3191
|
}
|
|
3190
3192
|
const removedEntry = parsed[matchIndex];
|
|
3191
|
-
const manifestPath =
|
|
3192
|
-
const manifestBackup =
|
|
3193
|
+
const manifestPath = path14.join(repoRoot, CLEF_MANIFEST_FILENAME);
|
|
3194
|
+
const manifestBackup = fs13.readFileSync(manifestPath, "utf-8");
|
|
3193
3195
|
const recipients = environment ? ensureEnvironmentRecipientsArray(doc, environment) : ensureRecipientsArray(doc);
|
|
3194
3196
|
recipients.splice(matchIndex, 1);
|
|
3195
3197
|
writeManifestYaml(repoRoot, doc);
|
|
@@ -3200,16 +3202,16 @@ var RecipientManager = class {
|
|
|
3200
3202
|
const fileBackups = /* @__PURE__ */ new Map();
|
|
3201
3203
|
for (const cell of cells) {
|
|
3202
3204
|
try {
|
|
3203
|
-
fileBackups.set(cell.filePath,
|
|
3205
|
+
fileBackups.set(cell.filePath, fs13.readFileSync(cell.filePath, "utf-8"));
|
|
3204
3206
|
await this.encryption.removeRecipient(cell.filePath, trimmedKey);
|
|
3205
3207
|
reEncryptedFiles.push(cell.filePath);
|
|
3206
3208
|
} catch {
|
|
3207
3209
|
failedFiles.push(cell.filePath);
|
|
3208
|
-
|
|
3210
|
+
fs13.writeFileSync(manifestPath, manifestBackup, "utf-8");
|
|
3209
3211
|
for (const reEncryptedFile of reEncryptedFiles) {
|
|
3210
3212
|
const backup = fileBackups.get(reEncryptedFile);
|
|
3211
3213
|
if (backup) {
|
|
3212
|
-
|
|
3214
|
+
fs13.writeFileSync(reEncryptedFile, backup, "utf-8");
|
|
3213
3215
|
}
|
|
3214
3216
|
}
|
|
3215
3217
|
const restoredDoc = readManifestYaml(repoRoot);
|
|
@@ -3243,19 +3245,19 @@ var RecipientManager = class {
|
|
|
3243
3245
|
};
|
|
3244
3246
|
|
|
3245
3247
|
// src/recipients/requests.ts
|
|
3246
|
-
import * as
|
|
3247
|
-
import * as
|
|
3248
|
+
import * as fs14 from "fs";
|
|
3249
|
+
import * as path15 from "path";
|
|
3248
3250
|
import * as YAML9 from "yaml";
|
|
3249
3251
|
var REQUESTS_FILENAME = ".clef-requests.yaml";
|
|
3250
3252
|
var HEADER_COMMENT2 = "# Pending recipient access requests. Approve with: clef recipients approve <label>\n";
|
|
3251
3253
|
function requestsFilePath(repoRoot) {
|
|
3252
|
-
return
|
|
3254
|
+
return path15.join(repoRoot, REQUESTS_FILENAME);
|
|
3253
3255
|
}
|
|
3254
3256
|
function loadRequests(repoRoot) {
|
|
3255
3257
|
const filePath = requestsFilePath(repoRoot);
|
|
3256
3258
|
try {
|
|
3257
|
-
if (!
|
|
3258
|
-
const content =
|
|
3259
|
+
if (!fs14.existsSync(filePath)) return [];
|
|
3260
|
+
const content = fs14.readFileSync(filePath, "utf-8");
|
|
3259
3261
|
const parsed = YAML9.parse(content);
|
|
3260
3262
|
if (!parsed || !Array.isArray(parsed.requests)) return [];
|
|
3261
3263
|
return parsed.requests.map((r) => ({
|
|
@@ -3272,7 +3274,7 @@ function saveRequests(repoRoot, requests) {
|
|
|
3272
3274
|
const filePath = requestsFilePath(repoRoot);
|
|
3273
3275
|
if (requests.length === 0) {
|
|
3274
3276
|
try {
|
|
3275
|
-
|
|
3277
|
+
fs14.unlinkSync(filePath);
|
|
3276
3278
|
} catch {
|
|
3277
3279
|
}
|
|
3278
3280
|
return;
|
|
@@ -3288,7 +3290,7 @@ function saveRequests(repoRoot, requests) {
|
|
|
3288
3290
|
return raw;
|
|
3289
3291
|
})
|
|
3290
3292
|
};
|
|
3291
|
-
|
|
3293
|
+
fs14.writeFileSync(filePath, HEADER_COMMENT2 + YAML9.stringify(data), "utf-8");
|
|
3292
3294
|
}
|
|
3293
3295
|
function upsertRequest(repoRoot, key, label, environment) {
|
|
3294
3296
|
const requests = loadRequests(repoRoot);
|
|
@@ -3324,7 +3326,7 @@ function findInList(requests, identifier) {
|
|
|
3324
3326
|
}
|
|
3325
3327
|
|
|
3326
3328
|
// src/drift/detector.ts
|
|
3327
|
-
import * as
|
|
3329
|
+
import * as path16 from "path";
|
|
3328
3330
|
var DriftDetector = class {
|
|
3329
3331
|
parser = new ManifestParser();
|
|
3330
3332
|
matrix = new MatrixManager();
|
|
@@ -3337,8 +3339,8 @@ var DriftDetector = class {
|
|
|
3337
3339
|
* @returns Drift result with any issues found.
|
|
3338
3340
|
*/
|
|
3339
3341
|
detect(localRoot, remoteRoot, namespaceFilter) {
|
|
3340
|
-
const localManifest = this.parser.parse(
|
|
3341
|
-
const remoteManifest = this.parser.parse(
|
|
3342
|
+
const localManifest = this.parser.parse(path16.join(localRoot, CLEF_MANIFEST_FILENAME));
|
|
3343
|
+
const remoteManifest = this.parser.parse(path16.join(remoteRoot, CLEF_MANIFEST_FILENAME));
|
|
3342
3344
|
const localCells = this.matrix.resolveMatrix(localManifest, localRoot);
|
|
3343
3345
|
const remoteCells = this.matrix.resolveMatrix(remoteManifest, remoteRoot);
|
|
3344
3346
|
const localEnvNames = localManifest.environments.map((e) => e.name);
|
|
@@ -3402,7 +3404,7 @@ var DriftDetector = class {
|
|
|
3402
3404
|
};
|
|
3403
3405
|
|
|
3404
3406
|
// src/report/generator.ts
|
|
3405
|
-
import * as
|
|
3407
|
+
import * as path17 from "path";
|
|
3406
3408
|
|
|
3407
3409
|
// src/report/sanitizer.ts
|
|
3408
3410
|
var ReportSanitizer = class {
|
|
@@ -3558,7 +3560,7 @@ var ReportGenerator = class {
|
|
|
3558
3560
|
let manifest = null;
|
|
3559
3561
|
try {
|
|
3560
3562
|
const parser = new ManifestParser();
|
|
3561
|
-
manifest = parser.parse(
|
|
3563
|
+
manifest = parser.parse(path17.join(repoRoot, "clef.yaml"));
|
|
3562
3564
|
} catch {
|
|
3563
3565
|
const emptyManifest = {
|
|
3564
3566
|
manifestVersion: 0,
|
|
@@ -4036,9 +4038,9 @@ var SopsMergeDriver = class {
|
|
|
4036
4038
|
};
|
|
4037
4039
|
|
|
4038
4040
|
// src/service-identity/manager.ts
|
|
4039
|
-
import * as
|
|
4041
|
+
import * as fs15 from "fs";
|
|
4040
4042
|
import * as os from "os";
|
|
4041
|
-
import * as
|
|
4043
|
+
import * as path18 from "path";
|
|
4042
4044
|
import * as YAML10 from "yaml";
|
|
4043
4045
|
var PartialRotationError = class extends Error {
|
|
4044
4046
|
constructor(message, rotatedKeys) {
|
|
@@ -4090,8 +4092,8 @@ var ServiceIdentityManager = class {
|
|
|
4090
4092
|
environments
|
|
4091
4093
|
};
|
|
4092
4094
|
await this.registerRecipients(definition, manifest, repoRoot);
|
|
4093
|
-
const manifestPath =
|
|
4094
|
-
const raw =
|
|
4095
|
+
const manifestPath = path18.join(repoRoot, CLEF_MANIFEST_FILENAME);
|
|
4096
|
+
const raw = fs15.readFileSync(manifestPath, "utf-8");
|
|
4095
4097
|
const doc = YAML10.parse(raw);
|
|
4096
4098
|
if (!Array.isArray(doc.service_identities)) {
|
|
4097
4099
|
doc.service_identities = [];
|
|
@@ -4102,13 +4104,13 @@ var ServiceIdentityManager = class {
|
|
|
4102
4104
|
namespaces,
|
|
4103
4105
|
environments
|
|
4104
4106
|
});
|
|
4105
|
-
const tmpCreate =
|
|
4107
|
+
const tmpCreate = path18.join(os.tmpdir(), `clef-manifest-${process.pid}-${Date.now()}.tmp`);
|
|
4106
4108
|
try {
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
+
fs15.writeFileSync(tmpCreate, YAML10.stringify(doc), "utf-8");
|
|
4110
|
+
fs15.renameSync(tmpCreate, manifestPath);
|
|
4109
4111
|
} finally {
|
|
4110
4112
|
try {
|
|
4111
|
-
|
|
4113
|
+
fs15.unlinkSync(tmpCreate);
|
|
4112
4114
|
} catch {
|
|
4113
4115
|
}
|
|
4114
4116
|
}
|
|
@@ -4146,8 +4148,8 @@ var ServiceIdentityManager = class {
|
|
|
4146
4148
|
} catch {
|
|
4147
4149
|
}
|
|
4148
4150
|
}
|
|
4149
|
-
const manifestPath =
|
|
4150
|
-
const raw =
|
|
4151
|
+
const manifestPath = path18.join(repoRoot, CLEF_MANIFEST_FILENAME);
|
|
4152
|
+
const raw = fs15.readFileSync(manifestPath, "utf-8");
|
|
4151
4153
|
const doc = YAML10.parse(raw);
|
|
4152
4154
|
const identities = doc.service_identities;
|
|
4153
4155
|
if (Array.isArray(identities)) {
|
|
@@ -4155,13 +4157,13 @@ var ServiceIdentityManager = class {
|
|
|
4155
4157
|
(si) => si.name !== name
|
|
4156
4158
|
);
|
|
4157
4159
|
}
|
|
4158
|
-
const tmp =
|
|
4160
|
+
const tmp = path18.join(os.tmpdir(), `clef-manifest-${process.pid}-${Date.now()}.tmp`);
|
|
4159
4161
|
try {
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
+
fs15.writeFileSync(tmp, YAML10.stringify(doc), "utf-8");
|
|
4163
|
+
fs15.renameSync(tmp, manifestPath);
|
|
4162
4164
|
} finally {
|
|
4163
4165
|
try {
|
|
4164
|
-
|
|
4166
|
+
fs15.unlinkSync(tmp);
|
|
4165
4167
|
} catch {
|
|
4166
4168
|
}
|
|
4167
4169
|
}
|
|
@@ -4176,8 +4178,8 @@ var ServiceIdentityManager = class {
|
|
|
4176
4178
|
if (!identity) {
|
|
4177
4179
|
throw new Error(`Service identity '${name}' not found.`);
|
|
4178
4180
|
}
|
|
4179
|
-
const manifestPath =
|
|
4180
|
-
const raw =
|
|
4181
|
+
const manifestPath = path18.join(repoRoot, CLEF_MANIFEST_FILENAME);
|
|
4182
|
+
const raw = fs15.readFileSync(manifestPath, "utf-8");
|
|
4181
4183
|
const doc = YAML10.parse(raw);
|
|
4182
4184
|
const identities = doc.service_identities;
|
|
4183
4185
|
const siDoc = identities.find((si) => si.name === name);
|
|
@@ -4203,13 +4205,13 @@ var ServiceIdentityManager = class {
|
|
|
4203
4205
|
envs[envName] = { kms: kmsConfig };
|
|
4204
4206
|
identity.environments[envName] = { kms: kmsConfig };
|
|
4205
4207
|
}
|
|
4206
|
-
const tmp =
|
|
4208
|
+
const tmp = path18.join(os.tmpdir(), `clef-manifest-${process.pid}-${Date.now()}.tmp`);
|
|
4207
4209
|
try {
|
|
4208
|
-
|
|
4209
|
-
|
|
4210
|
+
fs15.writeFileSync(tmp, YAML10.stringify(doc), "utf-8");
|
|
4211
|
+
fs15.renameSync(tmp, manifestPath);
|
|
4210
4212
|
} finally {
|
|
4211
4213
|
try {
|
|
4212
|
-
|
|
4214
|
+
fs15.unlinkSync(tmp);
|
|
4213
4215
|
} catch {
|
|
4214
4216
|
}
|
|
4215
4217
|
}
|
|
@@ -4245,8 +4247,8 @@ var ServiceIdentityManager = class {
|
|
|
4245
4247
|
if (!identity) {
|
|
4246
4248
|
throw new Error(`Service identity '${name}' not found.`);
|
|
4247
4249
|
}
|
|
4248
|
-
const manifestPath =
|
|
4249
|
-
const raw =
|
|
4250
|
+
const manifestPath = path18.join(repoRoot, CLEF_MANIFEST_FILENAME);
|
|
4251
|
+
const raw = fs15.readFileSync(manifestPath, "utf-8");
|
|
4250
4252
|
const doc = YAML10.parse(raw);
|
|
4251
4253
|
const identities = doc.service_identities;
|
|
4252
4254
|
const siDoc = identities.find((si) => si.name === name);
|
|
@@ -4300,13 +4302,13 @@ var ServiceIdentityManager = class {
|
|
|
4300
4302
|
}
|
|
4301
4303
|
throw err;
|
|
4302
4304
|
}
|
|
4303
|
-
const tmpRotate =
|
|
4305
|
+
const tmpRotate = path18.join(os.tmpdir(), `clef-manifest-${process.pid}-${Date.now()}.tmp`);
|
|
4304
4306
|
try {
|
|
4305
|
-
|
|
4306
|
-
|
|
4307
|
+
fs15.writeFileSync(tmpRotate, YAML10.stringify(doc), "utf-8");
|
|
4308
|
+
fs15.renameSync(tmpRotate, manifestPath);
|
|
4307
4309
|
} finally {
|
|
4308
4310
|
try {
|
|
4309
|
-
|
|
4311
|
+
fs15.unlinkSync(tmpRotate);
|
|
4310
4312
|
} catch {
|
|
4311
4313
|
}
|
|
4312
4314
|
}
|
|
@@ -4427,8 +4429,8 @@ async function resolveIdentitySecrets(identityName, environment, manifest, repoR
|
|
|
4427
4429
|
}
|
|
4428
4430
|
|
|
4429
4431
|
// src/artifact/packer.ts
|
|
4430
|
-
import * as
|
|
4431
|
-
import * as
|
|
4432
|
+
import * as fs16 from "fs";
|
|
4433
|
+
import * as path19 from "path";
|
|
4432
4434
|
import * as crypto3 from "crypto";
|
|
4433
4435
|
|
|
4434
4436
|
// src/artifact/signer.ts
|
|
@@ -4603,9 +4605,9 @@ var ArtifactPacker = class {
|
|
|
4603
4605
|
keys: Object.keys(resolved.values)
|
|
4604
4606
|
};
|
|
4605
4607
|
}
|
|
4606
|
-
const outputDir =
|
|
4607
|
-
if (!
|
|
4608
|
-
|
|
4608
|
+
const outputDir = path19.dirname(config.outputPath);
|
|
4609
|
+
if (!fs16.existsSync(outputDir)) {
|
|
4610
|
+
fs16.mkdirSync(outputDir, { recursive: true });
|
|
4609
4611
|
}
|
|
4610
4612
|
if (config.ttl && config.ttl > 0) {
|
|
4611
4613
|
artifact.expiresAt = new Date(Date.now() + config.ttl * 1e3).toISOString();
|
|
@@ -4624,8 +4626,8 @@ var ArtifactPacker = class {
|
|
|
4624
4626
|
}
|
|
4625
4627
|
const json = JSON.stringify(artifact, null, 2);
|
|
4626
4628
|
const tmpOutput = `${config.outputPath}.tmp.${process.pid}`;
|
|
4627
|
-
|
|
4628
|
-
|
|
4629
|
+
fs16.writeFileSync(tmpOutput, json, "utf-8");
|
|
4630
|
+
fs16.renameSync(tmpOutput, config.outputPath);
|
|
4629
4631
|
return {
|
|
4630
4632
|
outputPath: config.outputPath,
|
|
4631
4633
|
namespaceCount: resolved.identity.namespaces.length,
|
|
@@ -4638,8 +4640,215 @@ var ArtifactPacker = class {
|
|
|
4638
4640
|
|
|
4639
4641
|
// src/kms/types.ts
|
|
4640
4642
|
var VALID_KMS_PROVIDERS = ["aws", "gcp", "azure"];
|
|
4643
|
+
|
|
4644
|
+
// src/migration/backend.ts
|
|
4645
|
+
import * as fs17 from "fs";
|
|
4646
|
+
import * as path20 from "path";
|
|
4647
|
+
import * as YAML11 from "yaml";
|
|
4648
|
+
var BACKEND_KEY_FIELDS = {
|
|
4649
|
+
age: void 0,
|
|
4650
|
+
awskms: "aws_kms_arn",
|
|
4651
|
+
gcpkms: "gcp_kms_resource_id",
|
|
4652
|
+
azurekv: "azure_kv_url",
|
|
4653
|
+
pgp: "pgp_fingerprint"
|
|
4654
|
+
};
|
|
4655
|
+
var ALL_KEY_FIELDS = Object.values(BACKEND_KEY_FIELDS).filter(
|
|
4656
|
+
(v) => v !== void 0
|
|
4657
|
+
);
|
|
4658
|
+
function metadataMatchesTarget(meta, target) {
|
|
4659
|
+
if (meta.backend !== target.backend) return false;
|
|
4660
|
+
if (!target.key) return true;
|
|
4661
|
+
return meta.recipients.includes(target.key);
|
|
4662
|
+
}
|
|
4663
|
+
var BackendMigrator = class {
|
|
4664
|
+
constructor(encryption, matrixManager) {
|
|
4665
|
+
this.encryption = encryption;
|
|
4666
|
+
this.matrixManager = matrixManager;
|
|
4667
|
+
}
|
|
4668
|
+
async migrate(manifest, repoRoot, options, callbacks, onProgress) {
|
|
4669
|
+
const { target, environment, dryRun, skipVerify } = options;
|
|
4670
|
+
if (environment) {
|
|
4671
|
+
const env = manifest.environments.find((e) => e.name === environment);
|
|
4672
|
+
if (!env) {
|
|
4673
|
+
throw new Error(`Environment '${environment}' not found in manifest.`);
|
|
4674
|
+
}
|
|
4675
|
+
}
|
|
4676
|
+
const allCells = this.matrixManager.resolveMatrix(manifest, repoRoot).filter((c) => c.exists);
|
|
4677
|
+
const targetCells = environment ? allCells.filter((c) => c.environment === environment) : allCells;
|
|
4678
|
+
if (targetCells.length === 0) {
|
|
4679
|
+
return {
|
|
4680
|
+
migratedFiles: [],
|
|
4681
|
+
skippedFiles: [],
|
|
4682
|
+
rolledBack: false,
|
|
4683
|
+
verifiedFiles: [],
|
|
4684
|
+
warnings: ["No encrypted files found to migrate."]
|
|
4685
|
+
};
|
|
4686
|
+
}
|
|
4687
|
+
const toMigrate = [];
|
|
4688
|
+
const skippedFiles = [];
|
|
4689
|
+
for (const cell of targetCells) {
|
|
4690
|
+
const meta = await this.encryption.getMetadata(cell.filePath);
|
|
4691
|
+
if (metadataMatchesTarget(meta, target)) {
|
|
4692
|
+
skippedFiles.push(cell.filePath);
|
|
4693
|
+
onProgress?.({
|
|
4694
|
+
type: "skip",
|
|
4695
|
+
file: cell.filePath,
|
|
4696
|
+
message: `${cell.namespace}/${cell.environment}: already on ${target.backend}, skipping`
|
|
4697
|
+
});
|
|
4698
|
+
} else {
|
|
4699
|
+
toMigrate.push(cell);
|
|
4700
|
+
}
|
|
4701
|
+
}
|
|
4702
|
+
if (toMigrate.length === 0) {
|
|
4703
|
+
return {
|
|
4704
|
+
migratedFiles: [],
|
|
4705
|
+
skippedFiles,
|
|
4706
|
+
rolledBack: false,
|
|
4707
|
+
verifiedFiles: [],
|
|
4708
|
+
warnings: ["All files already use the target backend and key. Nothing to migrate."]
|
|
4709
|
+
};
|
|
4710
|
+
}
|
|
4711
|
+
if (dryRun) {
|
|
4712
|
+
const warnings2 = [];
|
|
4713
|
+
for (const cell of toMigrate) {
|
|
4714
|
+
onProgress?.({
|
|
4715
|
+
type: "info",
|
|
4716
|
+
file: cell.filePath,
|
|
4717
|
+
message: `Would migrate ${cell.namespace}/${cell.environment} to ${target.backend}`
|
|
4718
|
+
});
|
|
4719
|
+
}
|
|
4720
|
+
if (environment) {
|
|
4721
|
+
warnings2.push(
|
|
4722
|
+
`Would add per-environment backend override for '${environment}' \u2192 ${target.backend}`
|
|
4723
|
+
);
|
|
4724
|
+
} else {
|
|
4725
|
+
warnings2.push(`Would update global default_backend \u2192 ${target.backend}`);
|
|
4726
|
+
}
|
|
4727
|
+
this.checkAgeRecipientsWarning(manifest, target, environment, warnings2);
|
|
4728
|
+
return {
|
|
4729
|
+
migratedFiles: [],
|
|
4730
|
+
skippedFiles,
|
|
4731
|
+
rolledBack: false,
|
|
4732
|
+
verifiedFiles: [],
|
|
4733
|
+
warnings: warnings2
|
|
4734
|
+
};
|
|
4735
|
+
}
|
|
4736
|
+
const manifestPath = path20.join(repoRoot, CLEF_MANIFEST_FILENAME);
|
|
4737
|
+
const manifestBackup = fs17.readFileSync(manifestPath, "utf-8");
|
|
4738
|
+
const sopsYamlPath = path20.join(repoRoot, ".sops.yaml");
|
|
4739
|
+
const sopsYamlBackup = fs17.existsSync(sopsYamlPath) ? fs17.readFileSync(sopsYamlPath, "utf-8") : void 0;
|
|
4740
|
+
const fileBackups = /* @__PURE__ */ new Map();
|
|
4741
|
+
const doc = readManifestYaml(repoRoot);
|
|
4742
|
+
this.updateManifestDoc(doc, target, environment);
|
|
4743
|
+
writeManifestYaml(repoRoot, doc);
|
|
4744
|
+
const updatedManifest = YAML11.parse(YAML11.stringify(doc));
|
|
4745
|
+
callbacks.regenerateSopsConfig();
|
|
4746
|
+
const migratedFiles = [];
|
|
4747
|
+
for (const cell of toMigrate) {
|
|
4748
|
+
try {
|
|
4749
|
+
fileBackups.set(cell.filePath, fs17.readFileSync(cell.filePath, "utf-8"));
|
|
4750
|
+
onProgress?.({
|
|
4751
|
+
type: "migrate",
|
|
4752
|
+
file: cell.filePath,
|
|
4753
|
+
message: `Migrating ${cell.namespace}/${cell.environment}...`
|
|
4754
|
+
});
|
|
4755
|
+
const decrypted = await this.encryption.decrypt(cell.filePath);
|
|
4756
|
+
await this.encryption.encrypt(
|
|
4757
|
+
cell.filePath,
|
|
4758
|
+
decrypted.values,
|
|
4759
|
+
updatedManifest,
|
|
4760
|
+
cell.environment
|
|
4761
|
+
);
|
|
4762
|
+
migratedFiles.push(cell.filePath);
|
|
4763
|
+
} catch (err) {
|
|
4764
|
+
this.rollback(manifestPath, manifestBackup, sopsYamlPath, sopsYamlBackup, fileBackups);
|
|
4765
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
4766
|
+
onProgress?.({
|
|
4767
|
+
type: "warn",
|
|
4768
|
+
file: cell.filePath,
|
|
4769
|
+
message: `Migration failed: ${errorMsg}. All changes rolled back.`
|
|
4770
|
+
});
|
|
4771
|
+
return {
|
|
4772
|
+
migratedFiles: [],
|
|
4773
|
+
skippedFiles,
|
|
4774
|
+
rolledBack: true,
|
|
4775
|
+
error: `Failed on ${cell.namespace}/${cell.environment}: ${errorMsg}`,
|
|
4776
|
+
verifiedFiles: [],
|
|
4777
|
+
warnings: ["All changes have been rolled back."]
|
|
4778
|
+
};
|
|
4779
|
+
}
|
|
4780
|
+
}
|
|
4781
|
+
const verifiedFiles = [];
|
|
4782
|
+
const warnings = [];
|
|
4783
|
+
if (!skipVerify) {
|
|
4784
|
+
for (const cell of toMigrate) {
|
|
4785
|
+
try {
|
|
4786
|
+
onProgress?.({
|
|
4787
|
+
type: "verify",
|
|
4788
|
+
file: cell.filePath,
|
|
4789
|
+
message: `Verifying ${cell.namespace}/${cell.environment}...`
|
|
4790
|
+
});
|
|
4791
|
+
await this.encryption.decrypt(cell.filePath);
|
|
4792
|
+
verifiedFiles.push(cell.filePath);
|
|
4793
|
+
} catch (err) {
|
|
4794
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
4795
|
+
warnings.push(
|
|
4796
|
+
`Verification failed for ${cell.namespace}/${cell.environment}: ${errorMsg}`
|
|
4797
|
+
);
|
|
4798
|
+
}
|
|
4799
|
+
}
|
|
4800
|
+
}
|
|
4801
|
+
this.checkAgeRecipientsWarning(manifest, target, environment, warnings);
|
|
4802
|
+
return { migratedFiles, skippedFiles, rolledBack: false, verifiedFiles, warnings };
|
|
4803
|
+
}
|
|
4804
|
+
// ── Private helpers ──────────────────────────────────────────────────
|
|
4805
|
+
updateManifestDoc(doc, target, environment) {
|
|
4806
|
+
const keyField = BACKEND_KEY_FIELDS[target.backend];
|
|
4807
|
+
if (environment) {
|
|
4808
|
+
const environments = doc.environments;
|
|
4809
|
+
const envDoc = environments.find(
|
|
4810
|
+
(e) => e.name === environment
|
|
4811
|
+
);
|
|
4812
|
+
const sopsOverride = { backend: target.backend };
|
|
4813
|
+
if (keyField && target.key) {
|
|
4814
|
+
sopsOverride[keyField] = target.key;
|
|
4815
|
+
}
|
|
4816
|
+
envDoc.sops = sopsOverride;
|
|
4817
|
+
} else {
|
|
4818
|
+
const sops = doc.sops;
|
|
4819
|
+
sops.default_backend = target.backend;
|
|
4820
|
+
for (const field of ALL_KEY_FIELDS) {
|
|
4821
|
+
delete sops[field];
|
|
4822
|
+
}
|
|
4823
|
+
if (keyField && target.key) {
|
|
4824
|
+
sops[keyField] = target.key;
|
|
4825
|
+
}
|
|
4826
|
+
}
|
|
4827
|
+
}
|
|
4828
|
+
rollback(manifestPath, manifestBackup, sopsYamlPath, sopsYamlBackup, fileBackups) {
|
|
4829
|
+
for (const [filePath, backup] of fileBackups) {
|
|
4830
|
+
fs17.writeFileSync(filePath, backup, "utf-8");
|
|
4831
|
+
}
|
|
4832
|
+
if (sopsYamlBackup !== void 0) {
|
|
4833
|
+
fs17.writeFileSync(sopsYamlPath, sopsYamlBackup, "utf-8");
|
|
4834
|
+
} else if (fs17.existsSync(sopsYamlPath)) {
|
|
4835
|
+
fs17.unlinkSync(sopsYamlPath);
|
|
4836
|
+
}
|
|
4837
|
+
fs17.writeFileSync(manifestPath, manifestBackup, "utf-8");
|
|
4838
|
+
}
|
|
4839
|
+
checkAgeRecipientsWarning(manifest, target, environment, warnings) {
|
|
4840
|
+
if (target.backend === "age") return;
|
|
4841
|
+
const hasRecipients = environment ? manifest.environments.find((e) => e.name === environment)?.recipients?.length : manifest.environments.some((e) => e.recipients?.length);
|
|
4842
|
+
if (hasRecipients) {
|
|
4843
|
+
warnings.push(
|
|
4844
|
+
"Per-environment age recipients are no longer used for encryption on the migrated environments. Consider removing them from clef.yaml if they are no longer needed."
|
|
4845
|
+
);
|
|
4846
|
+
}
|
|
4847
|
+
}
|
|
4848
|
+
};
|
|
4641
4849
|
export {
|
|
4642
4850
|
ArtifactPacker,
|
|
4851
|
+
BackendMigrator,
|
|
4643
4852
|
BulkOps,
|
|
4644
4853
|
CLEF_MANIFEST_FILENAME,
|
|
4645
4854
|
CLEF_REPORT_SCHEMA_VERSION,
|
|
@@ -4702,11 +4911,12 @@ export {
|
|
|
4702
4911
|
markResolved,
|
|
4703
4912
|
matchPatterns,
|
|
4704
4913
|
metadataPath,
|
|
4705
|
-
|
|
4914
|
+
parse9 as parse,
|
|
4706
4915
|
parseDotenv,
|
|
4707
4916
|
parseIgnoreContent,
|
|
4708
4917
|
parseJson,
|
|
4709
4918
|
parseYaml,
|
|
4919
|
+
readManifestYaml,
|
|
4710
4920
|
redactValue,
|
|
4711
4921
|
removeRequest as removeAccessRequest,
|
|
4712
4922
|
requestsFilePath,
|
|
@@ -4724,6 +4934,7 @@ export {
|
|
|
4724
4934
|
signKms,
|
|
4725
4935
|
upsertRequest,
|
|
4726
4936
|
validateAgePublicKey,
|
|
4727
|
-
verifySignature
|
|
4937
|
+
verifySignature,
|
|
4938
|
+
writeManifestYaml
|
|
4728
4939
|
};
|
|
4729
4940
|
//# sourceMappingURL=index.mjs.map
|