@chriscode/hush 5.0.0 → 5.0.2
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/cli.js +39 -26
- package/dist/commands/check.d.ts +3 -3
- package/dist/commands/check.d.ts.map +1 -1
- package/dist/commands/check.js +27 -31
- package/dist/commands/decrypt.d.ts +2 -2
- package/dist/commands/decrypt.d.ts.map +1 -1
- package/dist/commands/decrypt.js +52 -55
- package/dist/commands/edit.d.ts +2 -2
- package/dist/commands/edit.d.ts.map +1 -1
- package/dist/commands/edit.js +10 -12
- package/dist/commands/encrypt.d.ts +2 -2
- package/dist/commands/encrypt.d.ts.map +1 -1
- package/dist/commands/encrypt.js +27 -29
- package/dist/commands/expansions.d.ts +2 -2
- package/dist/commands/expansions.d.ts.map +1 -1
- package/dist/commands/expansions.js +46 -44
- package/dist/commands/has.d.ts +2 -2
- package/dist/commands/has.d.ts.map +1 -1
- package/dist/commands/has.js +12 -15
- package/dist/commands/init.d.ts +2 -2
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +92 -100
- package/dist/commands/inspect.d.ts +2 -2
- package/dist/commands/inspect.d.ts.map +1 -1
- package/dist/commands/inspect.js +14 -16
- package/dist/commands/keys.d.ts +2 -1
- package/dist/commands/keys.d.ts.map +1 -1
- package/dist/commands/keys.js +47 -49
- package/dist/commands/list.d.ts +2 -2
- package/dist/commands/list.d.ts.map +1 -1
- package/dist/commands/list.js +11 -14
- package/dist/commands/migrate.d.ts +2 -1
- package/dist/commands/migrate.d.ts.map +1 -1
- package/dist/commands/migrate.js +38 -37
- package/dist/commands/push.d.ts +2 -2
- package/dist/commands/push.d.ts.map +1 -1
- package/dist/commands/push.js +41 -45
- package/dist/commands/resolve.d.ts +2 -2
- package/dist/commands/resolve.d.ts.map +1 -1
- package/dist/commands/resolve.js +25 -28
- package/dist/commands/run.d.ts +2 -2
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js +35 -39
- package/dist/commands/set.d.ts +2 -2
- package/dist/commands/set.d.ts.map +1 -1
- package/dist/commands/set.js +61 -70
- package/dist/commands/skill.d.ts +2 -2
- package/dist/commands/skill.d.ts.map +1 -1
- package/dist/commands/skill.js +149 -459
- package/dist/commands/status.d.ts +2 -2
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +48 -52
- package/dist/commands/template.d.ts +2 -2
- package/dist/commands/template.d.ts.map +1 -1
- package/dist/commands/template.js +36 -39
- package/dist/commands/trace.d.ts +2 -2
- package/dist/commands/trace.d.ts.map +1 -1
- package/dist/commands/trace.js +16 -19
- package/dist/config/loader.js +3 -3
- package/dist/context.d.ts +3 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +60 -0
- package/dist/core/parse.js +3 -3
- package/dist/core/sops.js +9 -9
- package/dist/core/template.d.ts +2 -2
- package/dist/core/template.d.ts.map +1 -1
- package/dist/core/template.js +11 -12
- package/dist/lib/age.js +9 -9
- package/dist/lib/fs.d.ts +25 -0
- package/dist/lib/fs.d.ts.map +1 -0
- package/dist/lib/fs.js +36 -0
- package/dist/lib/onepassword.d.ts.map +1 -1
- package/dist/lib/onepassword.js +41 -4
- package/dist/types.d.ts +92 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/version-check.js +5 -5
- package/package.json +3 -2
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { HushContext } from '../types.js';
|
|
1
2
|
export interface MigrateOptions {
|
|
2
3
|
root: string;
|
|
3
4
|
dryRun: boolean;
|
|
4
5
|
}
|
|
5
|
-
export declare function migrateCommand(options: MigrateOptions): Promise<void>;
|
|
6
|
+
export declare function migrateCommand(ctx: HushContext, options: MigrateOptions): Promise<void>;
|
|
6
7
|
//# sourceMappingURL=migrate.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../../src/commands/migrate.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../../src/commands/migrate.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;CACjB;AA+DD,wBAAsB,cAAc,CAAC,GAAG,EAAE,WAAW,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CA4E7F"}
|
package/dist/commands/migrate.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import { existsSync, renameSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
1
|
import { join } from 'node:path';
|
|
3
2
|
import pc from 'picocolors';
|
|
4
3
|
import { parse as parseYaml, stringify as stringifyYaml } from 'yaml';
|
|
5
|
-
import { findConfigPath } from '../config/loader.js';
|
|
6
4
|
const FILE_MIGRATIONS = [
|
|
7
5
|
{ from: '.env.encrypted', to: '.hush.encrypted' },
|
|
8
6
|
{ from: '.env.development.encrypted', to: '.hush.development.encrypted' },
|
|
@@ -15,18 +13,19 @@ const SOURCE_MIGRATIONS = {
|
|
|
15
13
|
'.env.production': '.hush.production',
|
|
16
14
|
'.env.local': '.hush.local',
|
|
17
15
|
};
|
|
18
|
-
function getMigrationFiles(root) {
|
|
16
|
+
function getMigrationFiles(ctx, root) {
|
|
19
17
|
return FILE_MIGRATIONS.map(({ from, to }) => ({
|
|
20
18
|
from,
|
|
21
19
|
to,
|
|
22
|
-
exists: existsSync(join(root, from)),
|
|
20
|
+
exists: ctx.fs.existsSync(join(root, from)),
|
|
23
21
|
}));
|
|
24
22
|
}
|
|
25
|
-
function migrateConfig(root, dryRun) {
|
|
26
|
-
const
|
|
27
|
-
if (!
|
|
23
|
+
function migrateConfig(ctx, root, dryRun) {
|
|
24
|
+
const projectInfo = ctx.config.findProjectRoot(root);
|
|
25
|
+
if (!projectInfo)
|
|
28
26
|
return false;
|
|
29
|
-
const
|
|
27
|
+
const configPath = projectInfo.configPath;
|
|
28
|
+
const content = ctx.fs.readFileSync(configPath, 'utf-8');
|
|
30
29
|
const config = parseYaml(content);
|
|
31
30
|
let modified = false;
|
|
32
31
|
const sources = config.sources;
|
|
@@ -45,43 +44,43 @@ function migrateConfig(root, dryRun) {
|
|
|
45
44
|
if (modified && !dryRun) {
|
|
46
45
|
const schemaComment = content.startsWith('#') ? content.split('\n')[0] + '\n' : '';
|
|
47
46
|
const newContent = schemaComment + stringifyYaml(config, { indent: 2 });
|
|
48
|
-
writeFileSync(configPath, newContent, 'utf-8');
|
|
47
|
+
ctx.fs.writeFileSync(configPath, newContent, 'utf-8');
|
|
49
48
|
}
|
|
50
49
|
return modified;
|
|
51
50
|
}
|
|
52
|
-
export async function migrateCommand(options) {
|
|
51
|
+
export async function migrateCommand(ctx, options) {
|
|
53
52
|
const { root, dryRun } = options;
|
|
54
|
-
|
|
53
|
+
ctx.logger.log(pc.blue('Hush v4 → v5 Migration\n'));
|
|
55
54
|
if (dryRun) {
|
|
56
|
-
|
|
55
|
+
ctx.logger.log(pc.yellow('DRY RUN - no changes will be made\n'));
|
|
57
56
|
}
|
|
58
|
-
const migrations = getMigrationFiles(root);
|
|
57
|
+
const migrations = getMigrationFiles(ctx, root);
|
|
59
58
|
const filesToMigrate = migrations.filter(m => m.exists);
|
|
60
59
|
if (filesToMigrate.length === 0) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
const configNeedsMigration = migrateConfig(root, true);
|
|
60
|
+
ctx.logger.log(pc.dim('No v4 encrypted files found (.env.encrypted, etc.)'));
|
|
61
|
+
ctx.logger.log(pc.dim('Already on v5 or no encrypted files exist.\n'));
|
|
62
|
+
const configNeedsMigration = migrateConfig(ctx, root, true);
|
|
64
63
|
if (configNeedsMigration) {
|
|
65
|
-
|
|
64
|
+
ctx.logger.log(pc.yellow('hush.yaml contains v4 source paths that need updating.\n'));
|
|
66
65
|
if (!dryRun) {
|
|
67
|
-
migrateConfig(root, false);
|
|
68
|
-
|
|
66
|
+
migrateConfig(ctx, root, false);
|
|
67
|
+
ctx.logger.log(pc.green('Updated hush.yaml source paths to v5 format.\n'));
|
|
69
68
|
}
|
|
70
69
|
}
|
|
71
70
|
return;
|
|
72
71
|
}
|
|
73
|
-
|
|
72
|
+
ctx.logger.log(pc.bold('Files to migrate:'));
|
|
74
73
|
for (const { from, to, exists } of migrations) {
|
|
75
74
|
if (exists) {
|
|
76
|
-
|
|
75
|
+
ctx.logger.log(` ${pc.yellow(from)} → ${pc.green(to)}`);
|
|
77
76
|
}
|
|
78
77
|
else {
|
|
79
|
-
|
|
78
|
+
ctx.logger.log(pc.dim(` ${from} (not found, skipping)`));
|
|
80
79
|
}
|
|
81
80
|
}
|
|
82
|
-
|
|
81
|
+
ctx.logger.log('');
|
|
83
82
|
if (dryRun) {
|
|
84
|
-
|
|
83
|
+
ctx.logger.log(pc.dim('Run without --dry-run to apply changes.'));
|
|
85
84
|
return;
|
|
86
85
|
}
|
|
87
86
|
let migratedCount = 0;
|
|
@@ -90,27 +89,29 @@ export async function migrateCommand(options) {
|
|
|
90
89
|
continue;
|
|
91
90
|
const fromPath = join(root, from);
|
|
92
91
|
const toPath = join(root, to);
|
|
93
|
-
if (existsSync(toPath)) {
|
|
94
|
-
|
|
92
|
+
if (ctx.fs.existsSync(toPath)) {
|
|
93
|
+
ctx.logger.log(pc.yellow(` Skipping ${from}: ${to} already exists`));
|
|
95
94
|
continue;
|
|
96
95
|
}
|
|
97
|
-
|
|
98
|
-
|
|
96
|
+
const content = ctx.fs.readFileSync(fromPath, 'utf-8');
|
|
97
|
+
ctx.fs.writeFileSync(toPath, content);
|
|
98
|
+
ctx.fs.unlinkSync(fromPath);
|
|
99
|
+
ctx.logger.log(pc.green(` Migrated ${from} → ${to}`));
|
|
99
100
|
migratedCount++;
|
|
100
101
|
}
|
|
101
|
-
const configUpdated = migrateConfig(root, false);
|
|
102
|
+
const configUpdated = migrateConfig(ctx, root, false);
|
|
102
103
|
if (configUpdated) {
|
|
103
|
-
|
|
104
|
+
ctx.logger.log(pc.green(' Updated hush.yaml source paths'));
|
|
104
105
|
}
|
|
105
|
-
|
|
106
|
+
ctx.logger.log('');
|
|
106
107
|
if (migratedCount > 0 || configUpdated) {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
108
|
+
ctx.logger.log(pc.green(pc.bold(`Migration complete.`)));
|
|
109
|
+
ctx.logger.log(pc.dim('\nNext steps:'));
|
|
110
|
+
ctx.logger.log(pc.dim(' 1. git add .hush.encrypted .hush.*.encrypted hush.yaml'));
|
|
111
|
+
ctx.logger.log(pc.dim(' 2. git rm .env.encrypted .env.*.encrypted (if tracked)'));
|
|
112
|
+
ctx.logger.log(pc.dim(' 3. git commit -m "chore: migrate to Hush v5 format"'));
|
|
112
113
|
}
|
|
113
114
|
else {
|
|
114
|
-
|
|
115
|
+
ctx.logger.log(pc.dim('No changes made.'));
|
|
115
116
|
}
|
|
116
117
|
}
|
package/dist/commands/push.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import type { PushOptions } from '../types.js';
|
|
2
|
-
export declare function pushCommand(options: PushOptions): Promise<void>;
|
|
1
|
+
import type { PushOptions, HushContext } from '../types.js';
|
|
2
|
+
export declare function pushCommand(ctx: HushContext, options: PushOptions): Promise<void>;
|
|
3
3
|
//# sourceMappingURL=push.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"push.d.ts","sourceRoot":"","sources":["../../src/commands/push.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"push.d.ts","sourceRoot":"","sources":["../../src/commands/push.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAU,WAAW,EAAqC,WAAW,EAAE,MAAM,aAAa,CAAC;AAiHvG,wBAAsB,WAAW,CAAC,GAAG,EAAE,WAAW,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CA6GvF"}
|
package/dist/commands/push.js
CHANGED
|
@@ -1,26 +1,22 @@
|
|
|
1
|
-
import { execSync } from 'node:child_process';
|
|
2
|
-
import { existsSync, writeFileSync, unlinkSync } from 'node:fs';
|
|
3
1
|
import { join } from 'node:path';
|
|
4
2
|
import pc from 'picocolors';
|
|
5
|
-
import { loadConfig } from '../config/loader.js';
|
|
6
3
|
import { filterVarsForTarget } from '../core/filter.js';
|
|
7
4
|
import { interpolateVars } from '../core/interpolate.js';
|
|
8
5
|
import { mergeVars } from '../core/merge.js';
|
|
9
6
|
import { parseEnvContent } from '../core/parse.js';
|
|
10
|
-
import { decrypt as sopsDecrypt } from '../core/sops.js';
|
|
11
7
|
import { loadLocalTemplates, resolveTemplateVars } from '../core/template.js';
|
|
12
|
-
function pushWorkerSecret(key, value, targetDir, dryRun, verbose) {
|
|
8
|
+
function pushWorkerSecret(ctx, key, value, targetDir, dryRun, verbose) {
|
|
13
9
|
if (dryRun) {
|
|
14
10
|
if (verbose) {
|
|
15
|
-
|
|
11
|
+
ctx.logger.log(pc.green(` + ${key}`));
|
|
16
12
|
}
|
|
17
13
|
else {
|
|
18
|
-
|
|
14
|
+
ctx.logger.log(pc.dim(` [dry-run] ${key}`));
|
|
19
15
|
}
|
|
20
16
|
return true;
|
|
21
17
|
}
|
|
22
18
|
try {
|
|
23
|
-
execSync(`echo "${value}" | wrangler secret put ${key}`, {
|
|
19
|
+
ctx.exec.execSync(`echo "${value}" | wrangler secret put ${key}`, {
|
|
24
20
|
cwd: targetDir,
|
|
25
21
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
26
22
|
shell: '/bin/bash',
|
|
@@ -29,18 +25,18 @@ function pushWorkerSecret(key, value, targetDir, dryRun, verbose) {
|
|
|
29
25
|
}
|
|
30
26
|
catch (error) {
|
|
31
27
|
const err = error;
|
|
32
|
-
|
|
28
|
+
ctx.logger.error(pc.red(` Failed: ${key} - ${err.stderr || err.message}`));
|
|
33
29
|
return false;
|
|
34
30
|
}
|
|
35
31
|
}
|
|
36
|
-
function pushPagesSecrets(vars, projectName, targetDir, dryRun, verbose) {
|
|
32
|
+
function pushPagesSecrets(ctx, vars, projectName, targetDir, dryRun, verbose) {
|
|
37
33
|
if (dryRun) {
|
|
38
34
|
for (const { key } of vars) {
|
|
39
35
|
if (verbose) {
|
|
40
|
-
|
|
36
|
+
ctx.logger.log(pc.green(` + ${key}`));
|
|
41
37
|
}
|
|
42
38
|
else {
|
|
43
|
-
|
|
39
|
+
ctx.logger.log(pc.dim(` [dry-run] ${key}`));
|
|
44
40
|
}
|
|
45
41
|
}
|
|
46
42
|
return { success: vars.length, failed: 0 };
|
|
@@ -51,26 +47,26 @@ function pushPagesSecrets(vars, projectName, targetDir, dryRun, verbose) {
|
|
|
51
47
|
}
|
|
52
48
|
const tempFile = join(targetDir, '.hush-secrets-temp.json');
|
|
53
49
|
try {
|
|
54
|
-
writeFileSync(tempFile, JSON.stringify(secretsJson, null, 2));
|
|
55
|
-
execSync(`wrangler pages secret bulk "${tempFile}" --project-name "${projectName}"`, {
|
|
50
|
+
ctx.fs.writeFileSync(tempFile, JSON.stringify(secretsJson, null, 2));
|
|
51
|
+
ctx.exec.execSync(`wrangler pages secret bulk "${tempFile}" --project-name "${projectName}"`, {
|
|
56
52
|
cwd: targetDir,
|
|
57
53
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
58
54
|
shell: '/bin/bash',
|
|
59
55
|
});
|
|
60
56
|
for (const { key } of vars) {
|
|
61
|
-
|
|
57
|
+
ctx.logger.log(pc.green(` ${key}`));
|
|
62
58
|
}
|
|
63
59
|
return { success: vars.length, failed: 0 };
|
|
64
60
|
}
|
|
65
61
|
catch (error) {
|
|
66
62
|
const err = error;
|
|
67
63
|
const stderrStr = err.stderr instanceof Buffer ? err.stderr.toString() : (err.stderr || err.message || 'Unknown error');
|
|
68
|
-
|
|
64
|
+
ctx.logger.error(pc.red(` Failed to push secrets: ${stderrStr}`));
|
|
69
65
|
return { success: 0, failed: vars.length };
|
|
70
66
|
}
|
|
71
67
|
finally {
|
|
72
|
-
if (existsSync(tempFile)) {
|
|
73
|
-
unlinkSync(tempFile);
|
|
68
|
+
if (ctx.fs.existsSync(tempFile)) {
|
|
69
|
+
ctx.fs.unlinkSync(tempFile);
|
|
74
70
|
}
|
|
75
71
|
}
|
|
76
72
|
}
|
|
@@ -103,30 +99,30 @@ function getPagesProject(target) {
|
|
|
103
99
|
}
|
|
104
100
|
throw new Error(`Target "${target.name}" is not configured for Cloudflare Pages`);
|
|
105
101
|
}
|
|
106
|
-
export async function pushCommand(options) {
|
|
102
|
+
export async function pushCommand(ctx, options) {
|
|
107
103
|
const { root, dryRun, verbose, target: targetFilter } = options;
|
|
108
|
-
const config = loadConfig(root);
|
|
109
|
-
|
|
104
|
+
const config = ctx.config.loadConfig(root);
|
|
105
|
+
ctx.logger.log(pc.blue('Pushing production secrets to Cloudflare...'));
|
|
110
106
|
if (dryRun) {
|
|
111
|
-
|
|
107
|
+
ctx.logger.log(pc.yellow('(dry-run mode)'));
|
|
112
108
|
if (verbose) {
|
|
113
|
-
|
|
109
|
+
ctx.logger.log(pc.dim('(verbose output enabled)'));
|
|
114
110
|
}
|
|
115
111
|
}
|
|
116
112
|
const sharedEncrypted = join(root, config.sources.shared + '.encrypted');
|
|
117
113
|
const prodEncrypted = join(root, config.sources.production + '.encrypted');
|
|
118
114
|
const varSources = [];
|
|
119
|
-
if (existsSync(sharedEncrypted)) {
|
|
120
|
-
const content =
|
|
115
|
+
if (ctx.fs.existsSync(sharedEncrypted)) {
|
|
116
|
+
const content = ctx.sops.decrypt(sharedEncrypted);
|
|
121
117
|
varSources.push(parseEnvContent(content));
|
|
122
118
|
}
|
|
123
|
-
if (existsSync(prodEncrypted)) {
|
|
124
|
-
const content =
|
|
119
|
+
if (ctx.fs.existsSync(prodEncrypted)) {
|
|
120
|
+
const content = ctx.sops.decrypt(prodEncrypted);
|
|
125
121
|
varSources.push(parseEnvContent(content));
|
|
126
122
|
}
|
|
127
123
|
if (varSources.length === 0) {
|
|
128
|
-
|
|
129
|
-
process.exit(1);
|
|
124
|
+
ctx.logger.error(pc.red('No encrypted files found'));
|
|
125
|
+
ctx.process.exit(1);
|
|
130
126
|
}
|
|
131
127
|
const merged = mergeVars(...varSources);
|
|
132
128
|
const interpolated = interpolateVars(merged);
|
|
@@ -139,47 +135,47 @@ export async function pushCommand(options) {
|
|
|
139
135
|
pushableTargets = getTargetsWithPush(config, targetFilter);
|
|
140
136
|
}
|
|
141
137
|
catch (error) {
|
|
142
|
-
|
|
143
|
-
process.exit(1);
|
|
138
|
+
ctx.logger.error(pc.red(error.message));
|
|
139
|
+
ctx.process.exit(1);
|
|
144
140
|
}
|
|
145
141
|
if (pushableTargets.length === 0) {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
process.exit(1);
|
|
142
|
+
ctx.logger.error(pc.red('No targets configured for push'));
|
|
143
|
+
ctx.logger.error(pc.dim('Add format: wrangler or push_to: { type: cloudflare-pages, project: ... } to a target'));
|
|
144
|
+
ctx.process.exit(1);
|
|
149
145
|
}
|
|
150
146
|
for (const target of pushableTargets) {
|
|
151
147
|
const targetDir = join(root, target.path);
|
|
152
148
|
const pushType = getPushType(target);
|
|
153
149
|
let filtered = filterVarsForTarget(interpolated, target);
|
|
154
|
-
const localTemplate = loadLocalTemplates(targetDir, 'production');
|
|
150
|
+
const localTemplate = loadLocalTemplates(targetDir, 'production', ctx.fs);
|
|
155
151
|
if (localTemplate.hasTemplate) {
|
|
156
|
-
const templateVars = resolveTemplateVars(localTemplate.vars, rootSecretsRecord, { processEnv: process.env });
|
|
152
|
+
const templateVars = resolveTemplateVars(localTemplate.vars, rootSecretsRecord, { processEnv: ctx.process.env });
|
|
157
153
|
filtered = mergeVars(filtered, templateVars);
|
|
158
154
|
}
|
|
159
155
|
if (filtered.length === 0) {
|
|
160
|
-
|
|
156
|
+
ctx.logger.log(pc.dim(`\n${target.name} - no matching vars, skipped`));
|
|
161
157
|
continue;
|
|
162
158
|
}
|
|
163
159
|
const typeLabel = pushType === 'cloudflare-pages' ? 'Pages' : 'Workers';
|
|
164
160
|
if (dryRun && verbose) {
|
|
165
|
-
|
|
161
|
+
ctx.logger.log(pc.blue(`\n[DRY RUN] Would push to ${target.name} (${typeLabel}, ${target.path}/):`));
|
|
166
162
|
}
|
|
167
163
|
else {
|
|
168
|
-
|
|
164
|
+
ctx.logger.log(pc.blue(`\n${target.name} (${typeLabel}, ${target.path}/)`));
|
|
169
165
|
}
|
|
170
166
|
let success = 0;
|
|
171
167
|
let failed = 0;
|
|
172
168
|
if (pushType === 'cloudflare-pages') {
|
|
173
169
|
const projectName = getPagesProject(target);
|
|
174
|
-
const result = pushPagesSecrets(filtered, projectName, targetDir, dryRun, verbose);
|
|
170
|
+
const result = pushPagesSecrets(ctx, filtered, projectName, targetDir, dryRun, verbose);
|
|
175
171
|
success = result.success;
|
|
176
172
|
failed = result.failed;
|
|
177
173
|
}
|
|
178
174
|
else {
|
|
179
175
|
for (const { key, value } of filtered) {
|
|
180
|
-
if (pushWorkerSecret(key, value, targetDir, dryRun, verbose)) {
|
|
176
|
+
if (pushWorkerSecret(ctx, key, value, targetDir, dryRun, verbose)) {
|
|
181
177
|
if (!dryRun)
|
|
182
|
-
|
|
178
|
+
ctx.logger.log(pc.green(` ${key}`));
|
|
183
179
|
success++;
|
|
184
180
|
}
|
|
185
181
|
else {
|
|
@@ -187,12 +183,12 @@ export async function pushCommand(options) {
|
|
|
187
183
|
}
|
|
188
184
|
}
|
|
189
185
|
}
|
|
190
|
-
|
|
186
|
+
ctx.logger.log(pc.dim(` ${success} pushed, ${failed} failed`));
|
|
191
187
|
}
|
|
192
188
|
if (dryRun) {
|
|
193
|
-
|
|
189
|
+
ctx.logger.log(pc.yellow('\n[dry-run] No secrets were pushed'));
|
|
194
190
|
}
|
|
195
191
|
else {
|
|
196
|
-
|
|
192
|
+
ctx.logger.log(pc.green('\nPush complete'));
|
|
197
193
|
}
|
|
198
194
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type { Environment } from '../types.js';
|
|
1
|
+
import type { Environment, HushContext } from '../types.js';
|
|
2
2
|
export interface ResolveOptions {
|
|
3
3
|
root: string;
|
|
4
4
|
env: Environment;
|
|
5
5
|
target: string;
|
|
6
6
|
}
|
|
7
|
-
export declare function resolveCommand(options: ResolveOptions): Promise<void>;
|
|
7
|
+
export declare function resolveCommand(ctx: HushContext, options: ResolveOptions): Promise<void>;
|
|
8
8
|
//# sourceMappingURL=resolve.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../src/commands/resolve.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../src/commands/resolve.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAU,WAAW,EAAU,WAAW,EAAE,MAAM,aAAa,CAAC;AAG5E,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,WAAW,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AA6BD,wBAAsB,cAAc,CAAC,GAAG,EAAE,WAAW,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAmI7F"}
|
package/dist/commands/resolve.js
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
|
-
import { existsSync } from 'node:fs';
|
|
2
1
|
import { join } from 'node:path';
|
|
3
2
|
import pc from 'picocolors';
|
|
4
|
-
import { loadConfig } from '../config/loader.js';
|
|
5
3
|
import { interpolateVars } from '../core/interpolate.js';
|
|
6
4
|
import { mergeVars } from '../core/merge.js';
|
|
7
5
|
import { parseEnvContent } from '../core/parse.js';
|
|
8
|
-
import { decrypt as sopsDecrypt } from '../core/sops.js';
|
|
9
6
|
import { loadLocalTemplates } from '../core/template.js';
|
|
10
7
|
import { FORMAT_OUTPUT_FILES } from '../types.js';
|
|
11
8
|
function matchesPattern(key, pattern) {
|
|
@@ -26,14 +23,14 @@ function getOutputFilename(target, env) {
|
|
|
26
23
|
}
|
|
27
24
|
return FORMAT_OUTPUT_FILES[target.format][env];
|
|
28
25
|
}
|
|
29
|
-
export async function resolveCommand(options) {
|
|
26
|
+
export async function resolveCommand(ctx, options) {
|
|
30
27
|
const { root, env, target: targetName } = options;
|
|
31
|
-
const config = loadConfig(root);
|
|
28
|
+
const config = ctx.config.loadConfig(root);
|
|
32
29
|
const target = config.targets.find(t => t.name === targetName);
|
|
33
30
|
if (!target) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
process.exit(1);
|
|
31
|
+
ctx.logger.error(`Target not found: ${targetName}`);
|
|
32
|
+
ctx.logger.error(pc.dim('Available targets: ' + config.targets.map(t => t.name).join(', ')));
|
|
33
|
+
ctx.process.exit(1);
|
|
37
34
|
}
|
|
38
35
|
const sharedEncrypted = join(root, config.sources.shared + '.encrypted');
|
|
39
36
|
const envEncrypted = join(root, config.sources[env] + '.encrypted');
|
|
@@ -41,9 +38,9 @@ export async function resolveCommand(options) {
|
|
|
41
38
|
const varsBySource = new Map();
|
|
42
39
|
const allVars = new Map();
|
|
43
40
|
const loadSource = (path, sourceName) => {
|
|
44
|
-
if (!existsSync(path))
|
|
41
|
+
if (!ctx.fs.existsSync(path))
|
|
45
42
|
return;
|
|
46
|
-
const content =
|
|
43
|
+
const content = ctx.sops.decrypt(path);
|
|
47
44
|
const vars = parseEnvContent(content);
|
|
48
45
|
const sourceVars = [];
|
|
49
46
|
for (const v of vars) {
|
|
@@ -57,8 +54,8 @@ export async function resolveCommand(options) {
|
|
|
57
54
|
loadSource(envEncrypted, config.sources[env]);
|
|
58
55
|
loadSource(localEncrypted, config.sources.local);
|
|
59
56
|
if (allVars.size === 0) {
|
|
60
|
-
|
|
61
|
-
process.exit(1);
|
|
57
|
+
ctx.logger.error('No encrypted files found');
|
|
58
|
+
ctx.process.exit(1);
|
|
62
59
|
}
|
|
63
60
|
const merged = mergeVars(...Array.from(varsBySource.values()).map(sources => sources.map(s => ({ key: s.key, value: s.value }))));
|
|
64
61
|
const interpolated = interpolateVars(merged);
|
|
@@ -84,44 +81,44 @@ export async function resolveCommand(options) {
|
|
|
84
81
|
included.push({ key: v.key, source });
|
|
85
82
|
}
|
|
86
83
|
const outputFile = getOutputFilename(target, env);
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
84
|
+
ctx.logger.log(pc.bold(`\nTarget: ${pc.cyan(target.name)}`));
|
|
85
|
+
ctx.logger.log(`Path: ${pc.dim(target.path + '/')}`);
|
|
86
|
+
ctx.logger.log(`Format: ${pc.dim(target.format)} ${pc.dim(`(${outputFile})`)}`);
|
|
87
|
+
ctx.logger.log(`Environment: ${pc.dim(env)}`);
|
|
88
|
+
ctx.logger.log(pc.green(`\n✅ ROOT SECRETS (Matched Filters) (${included.length}):`));
|
|
92
89
|
if (included.length === 0) {
|
|
93
|
-
|
|
90
|
+
ctx.logger.log(pc.dim(' (none)'));
|
|
94
91
|
}
|
|
95
92
|
else {
|
|
96
93
|
const maxKeyLen = Math.max(...included.map(v => v.key.length));
|
|
97
94
|
for (const v of included) {
|
|
98
|
-
|
|
95
|
+
ctx.logger.log(` ${v.key.padEnd(maxKeyLen)} ${pc.dim(`(source: ${v.source})`)}`);
|
|
99
96
|
}
|
|
100
97
|
}
|
|
101
|
-
|
|
98
|
+
ctx.logger.log(pc.red(`\n🚫 EXCLUDED VARIABLES (${excluded.length}):`));
|
|
102
99
|
if (excluded.length === 0) {
|
|
103
|
-
|
|
100
|
+
ctx.logger.log(pc.dim(' (none)'));
|
|
104
101
|
}
|
|
105
102
|
else {
|
|
106
103
|
const maxKeyLen = Math.max(...excluded.map(v => v.key.length));
|
|
107
104
|
for (const v of excluded) {
|
|
108
|
-
|
|
105
|
+
ctx.logger.log(` ${v.key.padEnd(maxKeyLen)} ${pc.dim(`(matches: ${v.pattern})`)}`);
|
|
109
106
|
}
|
|
110
107
|
}
|
|
111
108
|
const targetAbsPath = join(root, target.path);
|
|
112
|
-
const localTemplate = loadLocalTemplates(targetAbsPath, env);
|
|
109
|
+
const localTemplate = loadLocalTemplates(targetAbsPath, env, ctx.fs);
|
|
113
110
|
if (localTemplate.hasTemplate) {
|
|
114
|
-
|
|
111
|
+
ctx.logger.log(pc.blue(`\n📄 TEMPLATE EXPANSIONS (${pc.dim(join(target.path, '.hush'))}):`));
|
|
115
112
|
const maxKeyLen = Math.max(...localTemplate.vars.map(v => v.key.length));
|
|
116
113
|
for (const v of localTemplate.vars) {
|
|
117
|
-
|
|
114
|
+
ctx.logger.log(` ${v.key.padEnd(maxKeyLen)} ${pc.dim(`← ${v.value}`)}`);
|
|
118
115
|
}
|
|
119
116
|
// Calculate final merged list for clarity
|
|
120
117
|
const finalKeys = new Set([
|
|
121
118
|
...included.map(v => v.key),
|
|
122
119
|
...localTemplate.vars.map(v => v.key)
|
|
123
120
|
]);
|
|
124
|
-
|
|
121
|
+
ctx.logger.log(pc.magenta(`\n📦 FINAL INJECTION (${finalKeys.size} total):`));
|
|
125
122
|
const sortedKeys = Array.from(finalKeys).sort();
|
|
126
123
|
for (const key of sortedKeys) {
|
|
127
124
|
const isTemplate = localTemplate.vars.some(v => v.key === key);
|
|
@@ -133,8 +130,8 @@ export async function resolveCommand(options) {
|
|
|
133
130
|
sourceInfo = pc.dim('(template)');
|
|
134
131
|
else if (isRoot)
|
|
135
132
|
sourceInfo = pc.dim('(root)');
|
|
136
|
-
|
|
133
|
+
ctx.logger.log(` ${key} ${sourceInfo}`);
|
|
137
134
|
}
|
|
138
135
|
}
|
|
139
|
-
|
|
136
|
+
ctx.logger.log('');
|
|
140
137
|
}
|
package/dist/commands/run.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import type { RunOptions } from '../types.js';
|
|
2
|
-
export declare function runCommand(options: RunOptions): Promise<void>;
|
|
1
|
+
import type { RunOptions, HushContext } from '../types.js';
|
|
2
|
+
export declare function runCommand(ctx: HushContext, options: RunOptions): Promise<void>;
|
|
3
3
|
//# sourceMappingURL=run.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/commands/run.ts"],"names":[],"mappings":"
|
|
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,CA8GrF"}
|