@clef-sh/core 0.1.22 → 0.1.24-beta.166
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/artifact/packer.d.ts.map +1 -1
- package/dist/artifact/resolve.d.ts +7 -3
- package/dist/artifact/resolve.d.ts.map +1 -1
- package/dist/git/integration.d.ts +17 -0
- package/dist/git/integration.d.ts.map +1 -1
- package/dist/hsm/keyservice.d.ts +1 -1
- package/dist/index.js +78 -16
- package/dist/index.js.map +2 -2
- package/dist/index.mjs +78 -16
- package/dist/index.mjs.map +2 -2
- package/dist/manifest/parser.d.ts.map +1 -1
- package/dist/matrix/manager.d.ts +8 -2
- package/dist/matrix/manager.d.ts.map +1 -1
- package/dist/sops/hsm-arn.d.ts +2 -2
- package/dist/sync/manager.d.ts +3 -3
- package/dist/tx/errors.d.ts +2 -2
- package/dist/tx/errors.d.ts.map +1 -1
- package/dist/tx/transaction-manager.d.ts.map +1 -1
- package/dist/types/index.d.ts +5 -1
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -2268,6 +2268,7 @@ function keyPreview(key) {
|
|
|
2268
2268
|
var CLEF_MANIFEST_FILENAME = "clef.yaml";
|
|
2269
2269
|
var VALID_BACKENDS = ["age", "awskms", "gcpkms", "azurekv", "pgp", "hsm"];
|
|
2270
2270
|
var PKCS11_URI_PATTERN = /^pkcs11:[a-zA-Z][a-zA-Z0-9_-]*=[^;]+/;
|
|
2271
|
+
var AWS_KMS_ARN_PATTERN = /^arn:aws(?:-[a-z]+)*:kms:[a-z0-9-]+:\d+:(key|alias)\/.+$/;
|
|
2271
2272
|
var VALID_TOP_LEVEL_KEYS = [
|
|
2272
2273
|
"version",
|
|
2273
2274
|
"environments",
|
|
@@ -2790,11 +2791,22 @@ var ManifestParser = class {
|
|
|
2790
2791
|
"service_identities"
|
|
2791
2792
|
);
|
|
2792
2793
|
}
|
|
2794
|
+
if (kmsObj.provider === "aws" && !AWS_KMS_ARN_PATTERN.test(kmsObj.keyId)) {
|
|
2795
|
+
throw new ManifestValidationError(
|
|
2796
|
+
`Service identity '${siName}' environment '${envName}': kms.keyId must be a full AWS KMS ARN (e.g. arn:aws:kms:us-east-1:123456789012:key/abcd-1234), got '${kmsObj.keyId}'.`,
|
|
2797
|
+
"service_identities"
|
|
2798
|
+
);
|
|
2799
|
+
}
|
|
2800
|
+
if (Object.prototype.hasOwnProperty.call(kmsObj, "region")) {
|
|
2801
|
+
throw new ManifestValidationError(
|
|
2802
|
+
`Service identity '${siName}' environment '${envName}': kms.region is no longer accepted; the region is read from the AWS KMS key ARN. Remove this field.`,
|
|
2803
|
+
"service_identities"
|
|
2804
|
+
);
|
|
2805
|
+
}
|
|
2793
2806
|
parsedEnvs[envName] = {
|
|
2794
2807
|
kms: {
|
|
2795
2808
|
provider: kmsObj.provider,
|
|
2796
|
-
keyId: kmsObj.keyId
|
|
2797
|
-
region: typeof kmsObj.region === "string" ? kmsObj.region : void 0
|
|
2809
|
+
keyId: kmsObj.keyId
|
|
2798
2810
|
}
|
|
2799
2811
|
};
|
|
2800
2812
|
}
|
|
@@ -3439,11 +3451,17 @@ var MatrixManager = class {
|
|
|
3439
3451
|
await sopsClient.encrypt(cell.filePath, {}, manifest, cell.environment);
|
|
3440
3452
|
}
|
|
3441
3453
|
/**
|
|
3442
|
-
*
|
|
3454
|
+
* Read each cell and return key counts, pending counts, and cross-environment issues.
|
|
3455
|
+
*
|
|
3456
|
+
* The SOPS client parameter is currently unused — keys are read from the
|
|
3457
|
+
* plaintext YAML structure directly, no decryption needed. It is retained
|
|
3458
|
+
* in the signature for back-compat with callers that may need to swap to a
|
|
3459
|
+
* decrypt-based implementation later (e.g. for backends that don't expose
|
|
3460
|
+
* key names without decryption).
|
|
3443
3461
|
*
|
|
3444
3462
|
* @param manifest - Parsed manifest.
|
|
3445
3463
|
* @param repoRoot - Absolute path to the repository root.
|
|
3446
|
-
* @param
|
|
3464
|
+
* @param _sopsClient - Reserved for future use; pass any `EncryptionBackend`.
|
|
3447
3465
|
*/
|
|
3448
3466
|
async getMatrixStatus(manifest, repoRoot, _sopsClient) {
|
|
3449
3467
|
const cells = this.resolveMatrix(manifest, repoRoot);
|
|
@@ -4200,6 +4218,42 @@ var GitIntegration = class {
|
|
|
4200
4218
|
}
|
|
4201
4219
|
return result.stdout;
|
|
4202
4220
|
}
|
|
4221
|
+
/**
|
|
4222
|
+
* Of the given paths, return those that exist on disk but are untracked by git.
|
|
4223
|
+
*
|
|
4224
|
+
* Used by {@link TransactionManager} to refuse mutations whose declared paths
|
|
4225
|
+
* include untracked-but-existing files. Rollback uses `git reset --hard` to
|
|
4226
|
+
* restore content, which can only restore files that exist in a commit. An
|
|
4227
|
+
* untracked file in the declared paths would be silently destroyed by the
|
|
4228
|
+
* rollback's `git clean` — so we refuse upfront.
|
|
4229
|
+
*
|
|
4230
|
+
* @param repoRoot - Working directory for the git command.
|
|
4231
|
+
* @param paths - Paths to check (relative to repoRoot).
|
|
4232
|
+
* @returns Subset of `paths` that are untracked. Non-existent paths are
|
|
4233
|
+
* excluded; tracked paths are excluded; directories are reported by
|
|
4234
|
+
* their porcelain entry (with trailing slash if untracked-as-dir).
|
|
4235
|
+
* @throws {@link GitOperationError} On failure.
|
|
4236
|
+
*/
|
|
4237
|
+
async getUntrackedAmongPaths(repoRoot, paths) {
|
|
4238
|
+
if (paths.length === 0) return [];
|
|
4239
|
+
const result = await this.runner.run("git", ["status", "--porcelain", "--", ...paths], {
|
|
4240
|
+
cwd: repoRoot
|
|
4241
|
+
});
|
|
4242
|
+
if (result.exitCode !== 0) {
|
|
4243
|
+
throw new GitOperationError(
|
|
4244
|
+
`Failed to check tracked status: ${result.stderr.trim()}`,
|
|
4245
|
+
"Inspect with 'git status' and resolve any repository errors."
|
|
4246
|
+
);
|
|
4247
|
+
}
|
|
4248
|
+
const untracked = [];
|
|
4249
|
+
for (const line of result.stdout.split("\n")) {
|
|
4250
|
+
if (line === "") continue;
|
|
4251
|
+
if (line[0] === "?") {
|
|
4252
|
+
untracked.push(line.substring(3));
|
|
4253
|
+
}
|
|
4254
|
+
}
|
|
4255
|
+
return untracked;
|
|
4256
|
+
}
|
|
4203
4257
|
/**
|
|
4204
4258
|
* Parse `git status --porcelain` into staged, unstaged, and untracked lists.
|
|
4205
4259
|
*
|
|
@@ -4474,6 +4528,14 @@ var TransactionManager = class {
|
|
|
4474
4528
|
"Commit or stash your changes first, or pass --allow-dirty to proceed (rollback will be best-effort)."
|
|
4475
4529
|
);
|
|
4476
4530
|
}
|
|
4531
|
+
const untrackedDeclared = await this.git.getUntrackedAmongPaths(repoRoot, opts.paths);
|
|
4532
|
+
if (untrackedDeclared.length > 0) {
|
|
4533
|
+
throw new TransactionPreflightError(
|
|
4534
|
+
"untracked-paths",
|
|
4535
|
+
`Refusing to mutate: the following declared paths are untracked and would not survive rollback: ${untrackedDeclared.join(", ")}`,
|
|
4536
|
+
"Commit these files first (`git add` + `git commit`), then retry. Rollback can only restore content that exists in a commit."
|
|
4537
|
+
);
|
|
4538
|
+
}
|
|
4477
4539
|
try {
|
|
4478
4540
|
await opts.mutate();
|
|
4479
4541
|
} catch (mutateErr) {
|
|
@@ -8192,23 +8254,18 @@ async function resolveIdentitySecrets(identityName, environment, manifest, repoR
|
|
|
8192
8254
|
const cells = matrixManager.resolveMatrix(manifest, repoRoot).filter(
|
|
8193
8255
|
(c) => c.exists && identity.namespaces.includes(c.namespace) && c.environment === environment
|
|
8194
8256
|
);
|
|
8195
|
-
const isMultiNamespace = identity.namespaces.length > 1;
|
|
8196
|
-
const collisions = [];
|
|
8197
8257
|
for (const cell of cells) {
|
|
8198
8258
|
const decrypted = await encryption.decrypt(cell.filePath);
|
|
8259
|
+
const bucket = allValues[cell.namespace] ??= {};
|
|
8199
8260
|
for (const [key, value] of Object.entries(decrypted.values)) {
|
|
8200
|
-
|
|
8201
|
-
|
|
8202
|
-
|
|
8261
|
+
if (key in bucket && bucket[key] !== value) {
|
|
8262
|
+
throw new Error(
|
|
8263
|
+
`Key collision in namespace '${cell.namespace}': '${key}' set to different values.`
|
|
8264
|
+
);
|
|
8203
8265
|
}
|
|
8204
|
-
|
|
8266
|
+
bucket[key] = value;
|
|
8205
8267
|
}
|
|
8206
8268
|
}
|
|
8207
|
-
if (collisions.length > 0) {
|
|
8208
|
-
throw new Error(
|
|
8209
|
-
`Key collision detected in bundle: ${collisions.join(", ")}. Keys with the same name but different values exist across namespaces.`
|
|
8210
|
-
);
|
|
8211
|
-
}
|
|
8212
8269
|
return {
|
|
8213
8270
|
values: allValues,
|
|
8214
8271
|
identity,
|
|
@@ -8451,7 +8508,12 @@ var ArtifactPacker = class {
|
|
|
8451
8508
|
const json = JSON.stringify(artifact, null, 2);
|
|
8452
8509
|
const output = config.output ?? new FilePackOutput(config.outputPath ?? "artifact.json");
|
|
8453
8510
|
await output.write(artifact, json);
|
|
8454
|
-
const keys =
|
|
8511
|
+
const keys = [];
|
|
8512
|
+
for (const [ns, bucket] of Object.entries(resolved.values)) {
|
|
8513
|
+
for (const k of Object.keys(bucket)) {
|
|
8514
|
+
keys.push(`${ns}__${k}`);
|
|
8515
|
+
}
|
|
8516
|
+
}
|
|
8455
8517
|
return {
|
|
8456
8518
|
outputPath: config.outputPath ?? "",
|
|
8457
8519
|
namespaceCount: resolved.identity.namespaces.length,
|