@chriscode/hush 5.0.4 → 5.0.6

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.
@@ -1 +1 @@
1
- {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/commands/run.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,UAAU,EAAmC,WAAW,EAAE,MAAM,aAAa,CAAC;AAkD5F,wBAAsB,UAAU,CAAC,GAAG,EAAE,WAAW,EAAE,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CA6GrF"}
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/commands/run.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,UAAU,EAAmC,WAAW,EAAE,MAAM,aAAa,CAAC;AA4C5F,wBAAsB,UAAU,CAAC,GAAG,EAAE,WAAW,EAAE,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAmHrF"}
@@ -26,11 +26,7 @@ function getDecryptedSecrets(ctx, projectRoot, env, config) {
26
26
  varSources.push(parseEnvContent(content));
27
27
  }
28
28
  if (varSources.length === 0) {
29
- throw new Error(`No encrypted files found at project root.\n` +
30
- ` Expected: ${sharedEncrypted}\n` +
31
- ` Project root: ${projectRoot}\n\n` +
32
- ` If you haven't encrypted yet, run: npx hush encrypt\n` +
33
- ` If running from a subdirectory, ensure hush.yaml exists at the project root.`);
29
+ return [];
34
30
  }
35
31
  const merged = mergeVars(...varSources);
36
32
  return interpolateVars(merged);
@@ -61,6 +57,10 @@ export async function runCommand(ctx, options) {
61
57
  const { projectRoot } = projectInfo;
62
58
  const config = ctx.config.loadConfig(projectRoot);
63
59
  const rootSecrets = getDecryptedSecrets(ctx, projectRoot, env, config);
60
+ if (rootSecrets.length === 0) {
61
+ ctx.logger.warn(pc.yellow('No encrypted files found. Running command without secrets.'));
62
+ ctx.logger.warn(pc.dim(' To encrypt secrets, run: npx hush encrypt'));
63
+ }
64
64
  const rootSecretsRecord = getRootSecretsAsRecord(rootSecrets);
65
65
  const localTemplate = loadLocalTemplates(contextDir, env, ctx.fs);
66
66
  // 1. Resolve Template Vars
@@ -1 +1 @@
1
- {"version":3,"file":"sops.d.ts","sourceRoot":"","sources":["../../src/core/sops.ts"],"names":[],"mappings":"AA0BA,wBAAgB,eAAe,IAAI,OAAO,CAUzC;AAED,wBAAgB,kBAAkB,IAAI,OAAO,CAE5C;AAED,wBAAgB,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CA6BhD;AAED,wBAAgB,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAsBnE;AAED,wBAAgB,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAsB3C;AAED,wBAAgB,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CA+CzE"}
1
+ {"version":3,"file":"sops.d.ts","sourceRoot":"","sources":["../../src/core/sops.ts"],"names":[],"mappings":"AA0DA,wBAAgB,eAAe,IAAI,OAAO,CAUzC;AAED,wBAAgB,kBAAkB,IAAI,OAAO,CAE5C;AAED,wBAAgB,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAkChD;AAED,wBAAgB,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAsBnE;AAED,wBAAgB,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAsB3C;AAED,wBAAgB,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CA+CzE"}
package/dist/core/sops.js CHANGED
@@ -2,10 +2,40 @@ import { execSync, spawnSync } from 'node:child_process';
2
2
  import { fs } from '../lib/fs.js';
3
3
  import { join } from 'node:path';
4
4
  import { tmpdir, homedir } from 'node:os';
5
+ import { loadConfig, findProjectRoot } from '../config/loader.js';
6
+ import { keyExists, keyPath } from '../lib/age.js';
7
+ function getProjectIdentifier(root) {
8
+ const config = loadConfig(root);
9
+ if (config.project) {
10
+ return config.project;
11
+ }
12
+ const pkgPath = join(root, 'package.json');
13
+ if (fs.existsSync(pkgPath)) {
14
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
15
+ if (typeof pkg.repository === 'string') {
16
+ const match = pkg.repository.match(/github\.com[/:]([\w-]+\/[\w-]+)/);
17
+ if (match)
18
+ return match[1];
19
+ }
20
+ if (pkg.repository?.url) {
21
+ const match = pkg.repository.url.match(/github\.com[/:]([\w-]+\/[\w-]+)/);
22
+ if (match)
23
+ return match[1];
24
+ }
25
+ }
26
+ return undefined;
27
+ }
5
28
  function getAgeKeyFile() {
6
29
  if (process.env.SOPS_AGE_KEY_FILE) {
7
30
  return process.env.SOPS_AGE_KEY_FILE;
8
31
  }
32
+ const projectRoot = findProjectRoot(process.cwd())?.projectRoot;
33
+ if (projectRoot) {
34
+ const project = getProjectIdentifier(projectRoot);
35
+ if (project && keyExists(project)) {
36
+ return keyPath(project);
37
+ }
38
+ }
9
39
  const defaultPath = join(homedir(), '.config', 'sops', 'age', 'key.txt');
10
40
  if (fs.existsSync(defaultPath)) {
11
41
  return defaultPath;
@@ -52,8 +82,13 @@ export function decrypt(filePath) {
52
82
  catch (error) {
53
83
  const err = error;
54
84
  if (err.stderr?.includes('No identity matched')) {
85
+ const projectRoot = findProjectRoot(process.cwd())?.projectRoot;
86
+ const project = projectRoot ? getProjectIdentifier(projectRoot) : undefined;
87
+ const keyLocation = project
88
+ ? `~/.config/sops/age/keys/${project.replace(/\//g, '-')}.txt`
89
+ : '~/.config/sops/age/key.txt';
55
90
  throw new Error('SOPS decryption failed: No matching age key found.\n' +
56
- 'Ensure your age key is at ~/.config/sops/age/key.txt');
91
+ `Ensure your age key is at ${keyLocation}`);
57
92
  }
58
93
  throw new Error(`SOPS decryption failed: ${err.stderr || err.message}`);
59
94
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chriscode/hush",
3
- "version": "5.0.4",
3
+ "version": "5.0.6",
4
4
  "description": "SOPS-based secrets management for monorepos. Encrypt once, decrypt everywhere.",
5
5
  "type": "module",
6
6
  "bin": {