@ductape/cli 0.2.22 → 0.3.0
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/CHANGELOG.md +7 -0
- package/README.md +7 -0
- package/dist/commands/generate.js +1 -1
- package/dist/commands/migrate-codebase.d.ts +176 -0
- package/dist/commands/migrate-codebase.js +753 -0
- package/dist/commands/migration-artifact.d.ts +32 -0
- package/dist/commands/migration-artifact.js +48 -0
- package/dist/commands/migration-assurance.d.ts +10 -0
- package/dist/commands/migration-assurance.js +120 -0
- package/dist/commands/migration-database.d.ts +21 -0
- package/dist/commands/migration-database.js +162 -0
- package/dist/commands/migration-e2e.d.ts +68 -0
- package/dist/commands/migration-e2e.js +189 -0
- package/dist/commands/migration-environments.d.ts +20 -0
- package/dist/commands/migration-environments.js +58 -0
- package/dist/commands/migration-generated.d.ts +68 -0
- package/dist/commands/migration-generated.js +217 -0
- package/dist/commands/migration-portfolio.d.ts +88 -0
- package/dist/commands/migration-portfolio.js +223 -0
- package/dist/commands/migration-products.d.ts +23 -0
- package/dist/commands/migration-products.js +88 -0
- package/dist/commands/migration-review.d.ts +248 -0
- package/dist/commands/migration-review.js +624 -0
- package/dist/commands/migration-secrets.d.ts +24 -0
- package/dist/commands/migration-secrets.js +93 -0
- package/dist/commands/migration-slice.d.ts +132 -0
- package/dist/commands/migration-slice.js +266 -0
- package/dist/commands/migration-verification.d.ts +12 -0
- package/dist/commands/migration-verification.js +194 -0
- package/dist/commands/products.d.ts +1 -0
- package/dist/commands/products.js +33 -1
- package/dist/commands/secrets.d.ts +8 -0
- package/dist/commands/secrets.js +44 -0
- package/dist/index.js +354 -2
- package/dist/lib/integrations.js +6 -1
- package/dist/lib/migration-artifact.d.ts +13 -0
- package/dist/lib/migration-artifact.js +103 -0
- package/package.json +8 -1
- package/API_PARITY.md +0 -58
- package/DB_MIGRATE_PLAN.md +0 -223
- package/ductape.example.ts +0 -18
- package/src/commands/apply.ts +0 -153
- package/src/commands/apps-import.ts +0 -43
- package/src/commands/apps.ts +0 -92
- package/src/commands/cloud.ts +0 -68
- package/src/commands/completion.ts +0 -119
- package/src/commands/db-migrate.ts +0 -209
- package/src/commands/db-schema.ts +0 -429
- package/src/commands/db.ts +0 -63
- package/src/commands/generate.ts +0 -74
- package/src/commands/graph.ts +0 -51
- package/src/commands/init.ts +0 -39
- package/src/commands/install.ts +0 -76
- package/src/commands/link.ts +0 -84
- package/src/commands/login.ts +0 -145
- package/src/commands/logout.ts +0 -7
- package/src/commands/platform.ts +0 -123
- package/src/commands/products.ts +0 -228
- package/src/commands/profiles.ts +0 -23
- package/src/commands/resources.ts +0 -213
- package/src/commands/secrets.ts +0 -46
- package/src/commands/unlink.ts +0 -15
- package/src/commands/whoami.ts +0 -27
- package/src/commands/workspaces.ts +0 -98
- package/src/index.ts +0 -523
- package/src/lib/api-client.ts +0 -88
- package/src/lib/app-import.ts +0 -82
- package/src/lib/apply-loaders.ts +0 -30
- package/src/lib/config.ts +0 -230
- package/src/lib/context-store.ts +0 -77
- package/src/lib/db-methods.ts +0 -56
- package/src/lib/db-types.ts +0 -104
- package/src/lib/encryption.ts +0 -12
- package/src/lib/forms/enums.ts +0 -52
- package/src/lib/forms/index.ts +0 -8
- package/src/lib/forms/prompt.ts +0 -209
- package/src/lib/forms/schemas.ts +0 -288
- package/src/lib/forms/types.ts +0 -27
- package/src/lib/http.ts +0 -76
- package/src/lib/integrations.ts +0 -105
- package/src/lib/interactive-opts.ts +0 -13
- package/src/lib/migration-files.ts +0 -58
- package/src/lib/output.ts +0 -39
- package/src/lib/platform-api.ts +0 -209
- package/src/lib/proxy/context.ts +0 -94
- package/src/lib/proxy/db-proxy.ts +0 -40
- package/src/lib/proxy/graph-proxy.ts +0 -64
- package/src/lib/proxy/sdk-proxy.ts +0 -58
- package/src/lib/read-body.ts +0 -57
- package/src/lib/resources.ts +0 -160
- package/src/lib/schema-loader.ts +0 -152
- package/src/lib/snippets.ts +0 -165
- package/src/lib/templates.ts +0 -157
- package/src/lib/workspace-context.ts +0 -43
- package/src/lib/workspaces.ts +0 -283
- package/test/apply-loaders.test.ts +0 -40
- package/test/encryption.test.ts +0 -20
- package/test/forms.test.ts +0 -25
- package/test/http.test.ts +0 -15
- package/test/platform-api.test.ts +0 -42
- package/test/resources.test.ts +0 -23
- package/test/workspace-resolve.test.ts +0 -29
- package/tsconfig.json +0 -15
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import crypto from 'node:crypto';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { printJson } from '../lib/output.js';
|
|
5
|
+
import { readMigrationJson } from '../lib/migration-artifact.js';
|
|
6
|
+
const REQUIRED = {
|
|
7
|
+
large_codebase: [
|
|
8
|
+
'dependency_ordered_partitions', 'token_byte_estimates', 'scc_preserving_split',
|
|
9
|
+
'cited_service_summaries', 'transitive_summary_invalidation', 'resumable_checkpoints',
|
|
10
|
+
'multiple_repositories', 'cross_repository_contracts',
|
|
11
|
+
],
|
|
12
|
+
parity: [
|
|
13
|
+
'category_schemas', 'before_after_normalized', 'nondeterminism_tolerances',
|
|
14
|
+
'test_strength', 'mutation_signal', 'safe_shadow_dual_run', 'residual_uncertainty_decision',
|
|
15
|
+
],
|
|
16
|
+
frontend: [
|
|
17
|
+
'angular_svelte_integration', 'browser_automation', 'accessibility_thresholds',
|
|
18
|
+
'visual_baseline_approval', 'web_vitals_bundle_size', 'browser_version_matrix',
|
|
19
|
+
'offline_background_reconnect', 'browser_ssr_security', 'native_mobile',
|
|
20
|
+
],
|
|
21
|
+
database_baseline: [
|
|
22
|
+
'relational_fields', 'indexes_constraints', 'views_triggers_procedures',
|
|
23
|
+
'mongodb_documents', 'security_roles_encryption_audit', 'tenancy_partition_sharding',
|
|
24
|
+
'timezone_collation_encoding_precision', 'query_plan_performance', 'multiple_database_order',
|
|
25
|
+
],
|
|
26
|
+
database_data: [
|
|
27
|
+
'deterministic_selection_order', 'backfill_throttling', 'retry_poison_records',
|
|
28
|
+
'idempotency_keys', 'transformation_versioning', 'quality_thresholds',
|
|
29
|
+
'pii_least_privilege', 'large_binary_objects', 'cdc_outbox_coordination',
|
|
30
|
+
'pause_resume_abort_drills', 'resumable_execution_evidence',
|
|
31
|
+
],
|
|
32
|
+
database_cutover: [
|
|
33
|
+
'old_new_schema_compatibility', 'dual_write_conflict_resolution',
|
|
34
|
+
'migration_lock_concurrency', 'backup_identity_encryption_retention',
|
|
35
|
+
'restore_rehearsal', 'irreversible_approval', 'monitoring_observation_thresholds',
|
|
36
|
+
'post_cutover_reconciliation', 'slice_portfolio_link', 'event_feature_cdc_order',
|
|
37
|
+
'sandbox_readonly_authorization',
|
|
38
|
+
],
|
|
39
|
+
assets: [
|
|
40
|
+
'component_designs', 'live_inventory_reconciliation', 'environment_configuration_gaps',
|
|
41
|
+
'provider_capability_matrix', 'payload_sdk_schema_validation', 'asset_dependency_order',
|
|
42
|
+
],
|
|
43
|
+
third_party: ['client_family_context_review', 'app_action_design_templates'],
|
|
44
|
+
};
|
|
45
|
+
function read(file) {
|
|
46
|
+
return readMigrationJson(file);
|
|
47
|
+
}
|
|
48
|
+
function digest(file) {
|
|
49
|
+
return crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex');
|
|
50
|
+
}
|
|
51
|
+
function text(value) {
|
|
52
|
+
return typeof value === 'string' && value.trim().length > 0;
|
|
53
|
+
}
|
|
54
|
+
function recordComplete(record) {
|
|
55
|
+
const errors = [];
|
|
56
|
+
if (record.status !== 'passed')
|
|
57
|
+
errors.push('status must be passed');
|
|
58
|
+
if (!record.evidence?.length || !record.evidence.every(text))
|
|
59
|
+
errors.push('evidence is required');
|
|
60
|
+
if (!record.tests?.length || !record.tests.every(text))
|
|
61
|
+
errors.push('tests are required');
|
|
62
|
+
if (!record.sources?.length)
|
|
63
|
+
errors.push('source citations are required');
|
|
64
|
+
if (!record.automation || !text(record.automation.tool) || !text(record.automation.command) ||
|
|
65
|
+
!text(record.automation.result) || !text(record.automation.environment)) {
|
|
66
|
+
errors.push('automation tool, command, result, and environment are required');
|
|
67
|
+
}
|
|
68
|
+
if (!record.details || typeof record.details !== 'object' || Array.isArray(record.details) ||
|
|
69
|
+
Object.keys(record.details).length === 0) {
|
|
70
|
+
errors.push('structured details are required');
|
|
71
|
+
}
|
|
72
|
+
return errors;
|
|
73
|
+
}
|
|
74
|
+
function validateSpecial(record) {
|
|
75
|
+
const errors = [];
|
|
76
|
+
const details = record.details;
|
|
77
|
+
if (record.requirement === 'before_after_normalized') {
|
|
78
|
+
for (const field of ['before', 'after', 'normalization', 'comparison']) {
|
|
79
|
+
if (!text(details[field]))
|
|
80
|
+
errors.push(`details.${field} is required`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (record.requirement === 'nondeterminism_tolerances') {
|
|
84
|
+
if (!Array.isArray(details.fields) || !details.fields.length || !text(details.approved_differences)) {
|
|
85
|
+
errors.push('nondeterministic fields and approved differences are required');
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (record.requirement === 'dependency_ordered_partitions' ||
|
|
89
|
+
record.requirement === 'asset_dependency_order' ||
|
|
90
|
+
record.requirement === 'multiple_database_order' ||
|
|
91
|
+
record.requirement === 'event_feature_cdc_order') {
|
|
92
|
+
if (!Array.isArray(details.nodes) || !details.nodes.length ||
|
|
93
|
+
!Array.isArray(details.edges) || !Array.isArray(details.order) || !details.order.length) {
|
|
94
|
+
errors.push('dependency nodes, edges, and order are required');
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (record.requirement === 'token_byte_estimates') {
|
|
98
|
+
if (!Number.isFinite(details.max_tokens) || Number(details.max_tokens) <= 0 ||
|
|
99
|
+
!Number.isFinite(details.max_bytes) || Number(details.max_bytes) <= 0) {
|
|
100
|
+
errors.push('positive token and byte limits are required');
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (record.requirement === 'cited_service_summaries' && record.sources.length < 2) {
|
|
104
|
+
errors.push('service summaries require at least two cited source ranges');
|
|
105
|
+
}
|
|
106
|
+
if (record.requirement === 'irreversible_approval' ||
|
|
107
|
+
record.requirement === 'visual_baseline_approval' ||
|
|
108
|
+
record.requirement === 'residual_uncertainty_decision') {
|
|
109
|
+
if (!record.approval || !text(record.approval.actor) ||
|
|
110
|
+
!text(record.approval.authority) || !text(record.approval.evidence)) {
|
|
111
|
+
errors.push('trusted approval evidence is required');
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return errors;
|
|
115
|
+
}
|
|
116
|
+
export function validateMigrationVerification(file) {
|
|
117
|
+
const resolved = path.resolve(file);
|
|
118
|
+
const matrix = read(resolved);
|
|
119
|
+
if (matrix.artifact_kind !== 'ductape_migration_verification_matrix' || matrix.version !== 1) {
|
|
120
|
+
throw new Error('Unsupported migration verification matrix.');
|
|
121
|
+
}
|
|
122
|
+
const blockers = [];
|
|
123
|
+
const invalid = new Set();
|
|
124
|
+
const recordsById = new Map();
|
|
125
|
+
for (const [category, requirements] of Object.entries(REQUIRED)) {
|
|
126
|
+
const records = matrix.categories?.[category];
|
|
127
|
+
if (!Array.isArray(records)) {
|
|
128
|
+
blockers.push(`${category}: category is missing`);
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
const byRequirement = new Map(records.map((record) => [record.requirement, record]));
|
|
132
|
+
for (const requirement of requirements) {
|
|
133
|
+
const record = byRequirement.get(requirement);
|
|
134
|
+
if (!record) {
|
|
135
|
+
blockers.push(`${category}.${requirement}: verification record is missing`);
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
const recordId = `${category}.${requirement}`;
|
|
139
|
+
recordsById.set(recordId, record);
|
|
140
|
+
for (const error of [...recordComplete(record), ...validateSpecial(record)]) {
|
|
141
|
+
blockers.push(`${category}.${requirement}: ${error}`);
|
|
142
|
+
invalid.add(recordId);
|
|
143
|
+
}
|
|
144
|
+
for (const [index, source] of (record.sources ?? []).entries()) {
|
|
145
|
+
if (!path.isAbsolute(source.repository) || path.isAbsolute(source.file) ||
|
|
146
|
+
!Number.isInteger(source.start_line) || !Number.isInteger(source.end_line) ||
|
|
147
|
+
source.start_line < 1 || source.end_line < source.start_line ||
|
|
148
|
+
!/^[a-f0-9]{64}$/.test(source.checksum)) {
|
|
149
|
+
blockers.push(`${category}.${requirement}.sources[${index}]: invalid citation`);
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
const sourceFile = path.resolve(source.repository, source.file);
|
|
153
|
+
if (!fs.existsSync(sourceFile) || digest(sourceFile) !== source.checksum) {
|
|
154
|
+
blockers.push(`${category}.${requirement}.sources[${index}]: source is stale or missing`);
|
|
155
|
+
invalid.add(recordId);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
const duplicates = records.map((record) => record.requirement)
|
|
160
|
+
.filter((requirement, index, all) => all.indexOf(requirement) !== index);
|
|
161
|
+
if (duplicates.length)
|
|
162
|
+
blockers.push(`${category}: duplicate requirements: ${[...new Set(duplicates)].join(', ')}`);
|
|
163
|
+
}
|
|
164
|
+
let expanded = true;
|
|
165
|
+
while (expanded) {
|
|
166
|
+
expanded = false;
|
|
167
|
+
for (const [recordId, record] of recordsById) {
|
|
168
|
+
if (invalid.has(recordId))
|
|
169
|
+
continue;
|
|
170
|
+
const dependencies = Array.isArray(record.details?.depends_on)
|
|
171
|
+
? record.details.depends_on.filter((value) => typeof value === 'string')
|
|
172
|
+
: [];
|
|
173
|
+
if (dependencies.some((dependency) => invalid.has(dependency))) {
|
|
174
|
+
invalid.add(recordId);
|
|
175
|
+
blockers.push(`${recordId}: invalidated by stale dependency`);
|
|
176
|
+
expanded = true;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return {
|
|
181
|
+
file: resolved,
|
|
182
|
+
ready: blockers.length === 0,
|
|
183
|
+
categories: Object.keys(REQUIRED).length,
|
|
184
|
+
requirements: Object.values(REQUIRED).flat().length,
|
|
185
|
+
blockers,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
export function runMigrationVerificationValidate(opts) {
|
|
189
|
+
const result = validateMigrationVerification(opts.file);
|
|
190
|
+
printJson(result, Boolean(opts.json));
|
|
191
|
+
if (opts.strict && !result.ready) {
|
|
192
|
+
throw new Error('Migration verification matrix is not ready: resolve every reported blocker.');
|
|
193
|
+
}
|
|
194
|
+
}
|
|
@@ -160,7 +160,7 @@ export async function runProductComponents(verb, opts) {
|
|
|
160
160
|
}
|
|
161
161
|
export async function runProductEnvironments(verb, opts, extraArgs) {
|
|
162
162
|
const v = verb.toLowerCase();
|
|
163
|
-
const allowed = ['list', 'get', 'fetch'];
|
|
163
|
+
const allowed = ['list', 'get', 'fetch', 'create', 'update'];
|
|
164
164
|
if (!allowed.includes(v)) {
|
|
165
165
|
throw new Error(`Unknown environments verb "${verb}". Use: ${allowed.join(', ')}`);
|
|
166
166
|
}
|
|
@@ -174,6 +174,38 @@ export async function runProductEnvironments(verb, opts, extraArgs) {
|
|
|
174
174
|
printJson(result, Boolean(opts.json));
|
|
175
175
|
return;
|
|
176
176
|
}
|
|
177
|
+
if (v === 'create' || v === 'update') {
|
|
178
|
+
if (!opts.file)
|
|
179
|
+
throw new Error(`${v} requires --file <environment.json>`);
|
|
180
|
+
const body = JSON.parse((await import('node:fs')).readFileSync(opts.file, 'utf8'));
|
|
181
|
+
if (v === 'create') {
|
|
182
|
+
for (const field of ['env_name', 'description', 'slug']) {
|
|
183
|
+
if (typeof body[field] !== 'string' || !body[field].trim()) {
|
|
184
|
+
throw new Error(`Environment create requires non-empty ${field}.`);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
const existing = await proxy.execute('product', 'environments.fetch', [productTag, body.slug]);
|
|
188
|
+
if (existing) {
|
|
189
|
+
printJson({ product: productTag, slug: body.slug, created: false, existing }, Boolean(opts.json));
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
await proxy.execute('product', 'environments.create', [productTag, body]);
|
|
193
|
+
const verified = await proxy.execute('product', 'environments.fetch', [productTag, body.slug]);
|
|
194
|
+
if (!verified)
|
|
195
|
+
throw new Error(`Environment "${body.slug}" was not found after creation.`);
|
|
196
|
+
printJson({ product: productTag, slug: body.slug, created: true, environment: verified }, Boolean(opts.json));
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
const updateSlug = opts.slug ?? opts.tag ?? extraArgs[1];
|
|
200
|
+
if (!updateSlug)
|
|
201
|
+
throw new Error('Environment update requires --slug <slug>.');
|
|
202
|
+
await proxy.execute('product', 'environments.update', [productTag, updateSlug, body]);
|
|
203
|
+
const verified = await proxy.execute('product', 'environments.fetch', [productTag, body.slug ?? updateSlug]);
|
|
204
|
+
if (!verified)
|
|
205
|
+
throw new Error(`Environment "${updateSlug}" was not found after update.`);
|
|
206
|
+
printJson({ product: productTag, slug: body.slug ?? updateSlug, updated: true, environment: verified }, Boolean(opts.json));
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
177
209
|
const slug = opts.slug ?? opts.tag ?? extraArgs[1] ?? extraArgs[0];
|
|
178
210
|
if (!slug)
|
|
179
211
|
throw new Error('Provide the environment slug as the second argument or --slug <slug>');
|
|
@@ -4,3 +4,11 @@ export declare function runSecrets(verb: string, opts: {
|
|
|
4
4
|
file?: string;
|
|
5
5
|
json?: boolean;
|
|
6
6
|
} & CommandInteractiveFlags): Promise<void>;
|
|
7
|
+
export declare function runSecretImportEnv(opts: {
|
|
8
|
+
envFile: string;
|
|
9
|
+
sourceKey: string;
|
|
10
|
+
key?: string;
|
|
11
|
+
env?: string;
|
|
12
|
+
description?: string;
|
|
13
|
+
json?: boolean;
|
|
14
|
+
}): Promise<void>;
|
package/dist/commands/secrets.js
CHANGED
|
@@ -4,6 +4,8 @@ import { bodyRequiredHint, resolveBody } from '../lib/read-body.js';
|
|
|
4
4
|
import { buildCrudParams, proxyMethodForVerb } from '../lib/resources.js';
|
|
5
5
|
import { getSdkProxy, requireSession } from '../lib/proxy/context.js';
|
|
6
6
|
import { printJson } from '../lib/output.js';
|
|
7
|
+
import fs from 'node:fs';
|
|
8
|
+
import path from 'node:path';
|
|
7
9
|
const VERBS = ['create', 'list', 'get', 'update', 'delete'];
|
|
8
10
|
export async function runSecrets(verb, opts) {
|
|
9
11
|
const crud = verb.toLowerCase();
|
|
@@ -36,3 +38,45 @@ export async function runSecrets(verb, opts) {
|
|
|
36
38
|
const result = await getSdkProxy(session).execute('secrets', method, params);
|
|
37
39
|
printJson(result, Boolean(opts.json));
|
|
38
40
|
}
|
|
41
|
+
function readEnvValue(file, sourceKey) {
|
|
42
|
+
const resolved = path.resolve(file);
|
|
43
|
+
const line = fs.readFileSync(resolved, 'utf8')
|
|
44
|
+
.split(/\r?\n/)
|
|
45
|
+
.find((candidate) => new RegExp(`^\\s*(?:export\\s+)?${sourceKey.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\s*=`).test(candidate));
|
|
46
|
+
if (!line)
|
|
47
|
+
throw new Error(`Environment key "${sourceKey}" was not found in ${resolved}`);
|
|
48
|
+
let value = line.slice(line.indexOf('=') + 1).trim();
|
|
49
|
+
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
50
|
+
(value.startsWith("'") && value.endsWith("'")))
|
|
51
|
+
value = value.slice(1, -1);
|
|
52
|
+
if (!value)
|
|
53
|
+
throw new Error(`Environment key "${sourceKey}" is empty in ${resolved}`);
|
|
54
|
+
return value.replace(/\\n/g, '\n');
|
|
55
|
+
}
|
|
56
|
+
function redactSecretResult(value) {
|
|
57
|
+
if (Array.isArray(value))
|
|
58
|
+
return value.map(redactSecretResult);
|
|
59
|
+
if (!value || typeof value !== 'object')
|
|
60
|
+
return value;
|
|
61
|
+
const output = {};
|
|
62
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
63
|
+
output[key] = /^(?:value|encrypted_value|private_key)$/i.test(key)
|
|
64
|
+
? '[REDACTED]'
|
|
65
|
+
: redactSecretResult(entry);
|
|
66
|
+
}
|
|
67
|
+
return output;
|
|
68
|
+
}
|
|
69
|
+
export async function runSecretImportEnv(opts) {
|
|
70
|
+
const session = requireSession();
|
|
71
|
+
const key = opts.key ?? opts.sourceKey;
|
|
72
|
+
const value = readEnvValue(opts.envFile, opts.sourceKey);
|
|
73
|
+
const body = {
|
|
74
|
+
key,
|
|
75
|
+
value,
|
|
76
|
+
description: opts.description ?? `Imported locally from ${path.basename(opts.envFile)}:${opts.sourceKey}`,
|
|
77
|
+
...(opts.env ? { envs: [opts.env] } : {}),
|
|
78
|
+
};
|
|
79
|
+
const params = buildCrudParams('secrets', 'create', session.project.product_tag, { tag: key, body });
|
|
80
|
+
const result = await getSdkProxy(session).execute('secrets', 'create', params);
|
|
81
|
+
printJson(redactSecretResult(result), Boolean(opts.json));
|
|
82
|
+
}
|