@ductape/cli 0.2.23 → 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/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/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 -110
- 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,23 @@
|
|
|
1
|
+
export declare function initializeProductMap(opts: {
|
|
2
|
+
analysis: string;
|
|
3
|
+
definition: string;
|
|
4
|
+
output: string;
|
|
5
|
+
}): {
|
|
6
|
+
path: string;
|
|
7
|
+
};
|
|
8
|
+
export declare function validateProductMap(file: string): {
|
|
9
|
+
artifact: string;
|
|
10
|
+
ready: boolean;
|
|
11
|
+
blockers: string[];
|
|
12
|
+
};
|
|
13
|
+
export declare function runMigrationProductsInit(opts: {
|
|
14
|
+
analysis: string;
|
|
15
|
+
definition: string;
|
|
16
|
+
output: string;
|
|
17
|
+
json?: boolean;
|
|
18
|
+
}): void;
|
|
19
|
+
export declare function runMigrationProductsValidate(opts: {
|
|
20
|
+
file: string;
|
|
21
|
+
strict?: boolean;
|
|
22
|
+
json?: boolean;
|
|
23
|
+
}): void;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { printJson } from '../lib/output.js';
|
|
4
|
+
import { readMigrationJson, writeMigrationJson } from '../lib/migration-artifact.js';
|
|
5
|
+
function read(file) {
|
|
6
|
+
return readMigrationJson(file);
|
|
7
|
+
}
|
|
8
|
+
export function initializeProductMap(opts) {
|
|
9
|
+
const analysisPath = path.resolve(opts.analysis);
|
|
10
|
+
const definition = read(opts.definition);
|
|
11
|
+
if (!Array.isArray(definition.mappings) || !Array.isArray(definition.assets) ||
|
|
12
|
+
!Array.isArray(definition.promotions)) {
|
|
13
|
+
throw new Error('Definition requires mappings, assets, and promotions arrays.');
|
|
14
|
+
}
|
|
15
|
+
const artifact = {
|
|
16
|
+
version: 1,
|
|
17
|
+
artifact_kind: 'ductape_service_product_map',
|
|
18
|
+
analysis: analysisPath,
|
|
19
|
+
...definition,
|
|
20
|
+
};
|
|
21
|
+
const output = path.resolve(opts.output);
|
|
22
|
+
if (fs.existsSync(output))
|
|
23
|
+
throw new Error(`Service-to-product map already exists: ${output}`);
|
|
24
|
+
fs.mkdirSync(path.dirname(output), { recursive: true });
|
|
25
|
+
writeMigrationJson(output, artifact, { createOnly: true });
|
|
26
|
+
return { path: output };
|
|
27
|
+
}
|
|
28
|
+
export function validateProductMap(file) {
|
|
29
|
+
const resolved = path.resolve(file);
|
|
30
|
+
const artifact = read(resolved);
|
|
31
|
+
if (artifact.artifact_kind !== 'ductape_service_product_map')
|
|
32
|
+
throw new Error('Invalid service-to-product map.');
|
|
33
|
+
const analysis = read(artifact.analysis);
|
|
34
|
+
const blockers = [];
|
|
35
|
+
const assignment = new Map();
|
|
36
|
+
for (const mapping of artifact.mappings) {
|
|
37
|
+
if (!mapping.service || !mapping.product_tag || !mapping.environments.length || !mapping.evidence) {
|
|
38
|
+
blockers.push(`mapping ${mapping.service || '<unknown>'}: service, product, environments, and evidence are required`);
|
|
39
|
+
}
|
|
40
|
+
for (const manifest of mapping.manifests) {
|
|
41
|
+
assignment.set(manifest, [...(assignment.get(manifest) ?? []), mapping.service]);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
for (const unit of analysis.workspace_units ?? []) {
|
|
45
|
+
const owners = assignment.get(unit.manifest) ?? [];
|
|
46
|
+
if (owners.length === 0)
|
|
47
|
+
blockers.push(`workspace unit is unmapped: ${unit.manifest}`);
|
|
48
|
+
if (owners.length > 1)
|
|
49
|
+
blockers.push(`workspace unit has multiple service owners: ${unit.manifest}`);
|
|
50
|
+
}
|
|
51
|
+
for (const manifest of assignment.keys()) {
|
|
52
|
+
if (!(analysis.workspace_units ?? []).some((unit) => unit.manifest === manifest)) {
|
|
53
|
+
blockers.push(`mapping references unknown workspace unit: ${manifest}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
for (const asset of artifact.assets) {
|
|
57
|
+
if (!asset.kind || !asset.tag || !asset.product_tag || !asset.inventory_evidence) {
|
|
58
|
+
blockers.push(`asset ${asset.tag || '<unknown>'}: identity and inventory evidence are required`);
|
|
59
|
+
}
|
|
60
|
+
for (const environment of asset.required_environments) {
|
|
61
|
+
if (!asset.configured_environments.includes(environment)) {
|
|
62
|
+
blockers.push(`asset ${asset.kind}:${asset.tag} missing environment configuration: ${environment}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
for (const promotion of artifact.promotions) {
|
|
67
|
+
if (!promotion.product_tag || !promotion.from || !promotion.to ||
|
|
68
|
+
!promotion.entry_conditions.length || !promotion.validation.length || !promotion.rollback.length) {
|
|
69
|
+
blockers.push(`promotion ${promotion.from || '?'} -> ${promotion.to || '?'} is incomplete`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const mappedProducts = new Set(artifact.mappings.map((mapping) => mapping.product_tag));
|
|
73
|
+
for (const product of mappedProducts) {
|
|
74
|
+
if (!artifact.promotions.some((promotion) => promotion.product_tag === product)) {
|
|
75
|
+
blockers.push(`product has no promotion plan: ${product}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return { artifact: resolved, ready: blockers.length === 0, blockers };
|
|
79
|
+
}
|
|
80
|
+
export function runMigrationProductsInit(opts) {
|
|
81
|
+
printJson(initializeProductMap(opts), Boolean(opts.json));
|
|
82
|
+
}
|
|
83
|
+
export function runMigrationProductsValidate(opts) {
|
|
84
|
+
const result = validateProductMap(opts.file);
|
|
85
|
+
printJson(result, Boolean(opts.json));
|
|
86
|
+
if (opts.strict && !result.ready)
|
|
87
|
+
throw new Error('Service-to-product mapping is not ready.');
|
|
88
|
+
}
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
type ReviewStatus = 'pending' | 'reviewed' | 'excluded';
|
|
2
|
+
type ParityStatus = 'pending' | 'verified' | 'not_applicable';
|
|
3
|
+
type ReferenceKind = 'import' | 'caller' | 'callee' | 'implements' | 'event' | 'test' | 'config' | 'schema';
|
|
4
|
+
interface ParityEvidence {
|
|
5
|
+
status: ParityStatus;
|
|
6
|
+
evidence: string[];
|
|
7
|
+
reason?: string;
|
|
8
|
+
}
|
|
9
|
+
interface ReviewEntry {
|
|
10
|
+
file: string;
|
|
11
|
+
status: ReviewStatus;
|
|
12
|
+
safety: 'names_only' | 'normal';
|
|
13
|
+
baseline_checksum: string | null;
|
|
14
|
+
reviewed_checksum: string | null;
|
|
15
|
+
purpose: string;
|
|
16
|
+
dependencies: string[];
|
|
17
|
+
follow_up_files: string[];
|
|
18
|
+
references: Array<{
|
|
19
|
+
kind: ReferenceKind;
|
|
20
|
+
target: string;
|
|
21
|
+
external: boolean;
|
|
22
|
+
evidence: string;
|
|
23
|
+
}>;
|
|
24
|
+
interfaces: string[];
|
|
25
|
+
findings: Array<{
|
|
26
|
+
kind: string;
|
|
27
|
+
evidence: string;
|
|
28
|
+
location: {
|
|
29
|
+
start_line: number;
|
|
30
|
+
end_line: number;
|
|
31
|
+
};
|
|
32
|
+
symbol: {
|
|
33
|
+
kind: 'module' | 'class' | 'interface' | 'function' | 'method' | 'field' | 'route' | 'event' | 'schema' | 'other';
|
|
34
|
+
name: string;
|
|
35
|
+
qualified_name?: string;
|
|
36
|
+
};
|
|
37
|
+
classification: 'observed' | 'inferred' | 'proposed' | 'confirmed';
|
|
38
|
+
confidence: 'low' | 'medium' | 'high';
|
|
39
|
+
requires_followup: string[];
|
|
40
|
+
}>;
|
|
41
|
+
exclusion_reason?: string;
|
|
42
|
+
exclusion?: {
|
|
43
|
+
classification: 'generated' | 'vendor' | 'build' | 'snapshot' | 'other';
|
|
44
|
+
reason: string;
|
|
45
|
+
owner?: string;
|
|
46
|
+
generator?: string;
|
|
47
|
+
regeneration_test?: string;
|
|
48
|
+
};
|
|
49
|
+
interface_parity: ParityEvidence;
|
|
50
|
+
functional_parity: ParityEvidence;
|
|
51
|
+
operational_parity: ParityEvidence;
|
|
52
|
+
}
|
|
53
|
+
interface ReviewLedger {
|
|
54
|
+
version: 1;
|
|
55
|
+
artifact_kind: 'ai_migration_review_ledger';
|
|
56
|
+
source: string;
|
|
57
|
+
created_at: string;
|
|
58
|
+
revision: number;
|
|
59
|
+
events: Array<{
|
|
60
|
+
revision: number;
|
|
61
|
+
timestamp: string;
|
|
62
|
+
actor: string;
|
|
63
|
+
tool: '@ductape/cli';
|
|
64
|
+
tool_version: string;
|
|
65
|
+
operation: string;
|
|
66
|
+
state_hash: string;
|
|
67
|
+
previous_hash: string | null;
|
|
68
|
+
hash: string;
|
|
69
|
+
}>;
|
|
70
|
+
entries: ReviewEntry[];
|
|
71
|
+
}
|
|
72
|
+
interface LedgerSignature {
|
|
73
|
+
version: 1;
|
|
74
|
+
artifact_kind: 'ductape_review_ledger_signature';
|
|
75
|
+
ledger: string;
|
|
76
|
+
ledger_checksum: string;
|
|
77
|
+
key_id: string;
|
|
78
|
+
algorithm: 'Ed25519';
|
|
79
|
+
signed_at: string;
|
|
80
|
+
signature: string;
|
|
81
|
+
}
|
|
82
|
+
export declare function initializeReviewLedger(analysisPath: string, ledgerPath?: string): {
|
|
83
|
+
ledger: ReviewLedger;
|
|
84
|
+
path: string;
|
|
85
|
+
};
|
|
86
|
+
export declare function validateReviewLedger(ledgerPath: string): {
|
|
87
|
+
ledger: string;
|
|
88
|
+
source: string;
|
|
89
|
+
ready: boolean;
|
|
90
|
+
coverage: {
|
|
91
|
+
total: number;
|
|
92
|
+
reviewed: number;
|
|
93
|
+
excluded: number;
|
|
94
|
+
pending: number;
|
|
95
|
+
percent: number;
|
|
96
|
+
};
|
|
97
|
+
blockers: {
|
|
98
|
+
pending: string[];
|
|
99
|
+
stale: string[];
|
|
100
|
+
invalid_exclusions: string[];
|
|
101
|
+
invalid_generated_exclusions: string[];
|
|
102
|
+
missing_follow_ups: {
|
|
103
|
+
file: string;
|
|
104
|
+
follow_up: string;
|
|
105
|
+
}[];
|
|
106
|
+
uncovered_references: {
|
|
107
|
+
file: string;
|
|
108
|
+
kind: ReferenceKind;
|
|
109
|
+
target: string;
|
|
110
|
+
}[];
|
|
111
|
+
missing_finding_provenance: {
|
|
112
|
+
file: string;
|
|
113
|
+
finding: number;
|
|
114
|
+
}[];
|
|
115
|
+
missing_symbol_provenance: {
|
|
116
|
+
file: string;
|
|
117
|
+
finding: number;
|
|
118
|
+
}[];
|
|
119
|
+
probable_moves: {
|
|
120
|
+
from: string;
|
|
121
|
+
candidates: string[];
|
|
122
|
+
}[];
|
|
123
|
+
invalidated_dependents: string[];
|
|
124
|
+
audit_errors: string[];
|
|
125
|
+
missing_context: string[];
|
|
126
|
+
incomplete_parity: {
|
|
127
|
+
file: string;
|
|
128
|
+
concern: string;
|
|
129
|
+
}[];
|
|
130
|
+
};
|
|
131
|
+
};
|
|
132
|
+
export declare function snapshotReviewedFile(ledgerPath: string, file: string): ReviewEntry;
|
|
133
|
+
export declare function recordReviewContext(ledgerPath: string, file: string, dataPath: string): ReviewEntry;
|
|
134
|
+
export declare function addFollowUpFile(ledgerPath: string, fromFile: string, followUpFile: string): ReviewEntry;
|
|
135
|
+
export declare function excludeReviewFile(ledgerPath: string, file: string, exclusion: NonNullable<ReviewEntry['exclusion']>): ReviewEntry;
|
|
136
|
+
export declare function setParityEvidence(ledgerPath: string, file: string, concern: 'interface' | 'functional' | 'operational', status: Exclude<ParityStatus, 'pending'>, evidence?: string, reason?: string): ReviewEntry;
|
|
137
|
+
export declare function reopenReviewFile(ledgerPath: string, file: string): ReviewEntry;
|
|
138
|
+
export declare function moveReviewedFile(ledgerPath: string, from: string, to: string): ReviewEntry;
|
|
139
|
+
export declare function signReviewLedger(opts: {
|
|
140
|
+
ledger: string;
|
|
141
|
+
privateKey: string;
|
|
142
|
+
keyId: string;
|
|
143
|
+
output?: string;
|
|
144
|
+
}): {
|
|
145
|
+
path: string;
|
|
146
|
+
signature: LedgerSignature;
|
|
147
|
+
};
|
|
148
|
+
export declare function verifyReviewLedgerSignature(opts: {
|
|
149
|
+
ledger: string;
|
|
150
|
+
signature: string;
|
|
151
|
+
publicKey: string;
|
|
152
|
+
}): {
|
|
153
|
+
ledger: string;
|
|
154
|
+
key_id: string;
|
|
155
|
+
valid: boolean;
|
|
156
|
+
};
|
|
157
|
+
export declare function mergeReviewLedgers(opts: {
|
|
158
|
+
base: string;
|
|
159
|
+
current: string;
|
|
160
|
+
incoming: string;
|
|
161
|
+
output: string;
|
|
162
|
+
}): {
|
|
163
|
+
merged: boolean;
|
|
164
|
+
conflicts: string[];
|
|
165
|
+
output: null;
|
|
166
|
+
} | {
|
|
167
|
+
merged: boolean;
|
|
168
|
+
conflicts: never[];
|
|
169
|
+
output: string;
|
|
170
|
+
};
|
|
171
|
+
export declare function runMigrationReviewInit(opts: {
|
|
172
|
+
analysis: string;
|
|
173
|
+
output?: string;
|
|
174
|
+
json?: boolean;
|
|
175
|
+
}): void;
|
|
176
|
+
export declare function runMigrationReviewValidate(opts: {
|
|
177
|
+
ledger: string;
|
|
178
|
+
strict?: boolean;
|
|
179
|
+
json?: boolean;
|
|
180
|
+
}): void;
|
|
181
|
+
export declare function runMigrationReviewSnapshot(opts: {
|
|
182
|
+
ledger: string;
|
|
183
|
+
file: string;
|
|
184
|
+
json?: boolean;
|
|
185
|
+
}): void;
|
|
186
|
+
export declare function runMigrationReviewRecord(opts: {
|
|
187
|
+
ledger: string;
|
|
188
|
+
file: string;
|
|
189
|
+
data: string;
|
|
190
|
+
json?: boolean;
|
|
191
|
+
}): void;
|
|
192
|
+
export declare function runMigrationReviewFollowUp(opts: {
|
|
193
|
+
ledger: string;
|
|
194
|
+
file: string;
|
|
195
|
+
followUp: string;
|
|
196
|
+
json?: boolean;
|
|
197
|
+
}): void;
|
|
198
|
+
export declare function runMigrationReviewExclude(opts: {
|
|
199
|
+
ledger: string;
|
|
200
|
+
file: string;
|
|
201
|
+
classification: NonNullable<ReviewEntry['exclusion']>['classification'];
|
|
202
|
+
reason: string;
|
|
203
|
+
owner?: string;
|
|
204
|
+
generator?: string;
|
|
205
|
+
regenerationTest?: string;
|
|
206
|
+
json?: boolean;
|
|
207
|
+
}): void;
|
|
208
|
+
export declare function runMigrationReviewParity(opts: {
|
|
209
|
+
ledger: string;
|
|
210
|
+
file: string;
|
|
211
|
+
concern: 'interface' | 'functional' | 'operational';
|
|
212
|
+
status: 'verified' | 'not_applicable';
|
|
213
|
+
evidence?: string;
|
|
214
|
+
reason?: string;
|
|
215
|
+
json?: boolean;
|
|
216
|
+
}): void;
|
|
217
|
+
export declare function runMigrationReviewReopen(opts: {
|
|
218
|
+
ledger: string;
|
|
219
|
+
file: string;
|
|
220
|
+
json?: boolean;
|
|
221
|
+
}): void;
|
|
222
|
+
export declare function runMigrationReviewMove(opts: {
|
|
223
|
+
ledger: string;
|
|
224
|
+
from: string;
|
|
225
|
+
to: string;
|
|
226
|
+
json?: boolean;
|
|
227
|
+
}): void;
|
|
228
|
+
export declare function runMigrationReviewSign(opts: {
|
|
229
|
+
ledger: string;
|
|
230
|
+
privateKey: string;
|
|
231
|
+
keyId: string;
|
|
232
|
+
output?: string;
|
|
233
|
+
json?: boolean;
|
|
234
|
+
}): void;
|
|
235
|
+
export declare function runMigrationReviewVerifySignature(opts: {
|
|
236
|
+
ledger: string;
|
|
237
|
+
signature: string;
|
|
238
|
+
publicKey: string;
|
|
239
|
+
json?: boolean;
|
|
240
|
+
}): void;
|
|
241
|
+
export declare function runMigrationReviewMerge(opts: {
|
|
242
|
+
base: string;
|
|
243
|
+
current: string;
|
|
244
|
+
incoming: string;
|
|
245
|
+
output: string;
|
|
246
|
+
json?: boolean;
|
|
247
|
+
}): void;
|
|
248
|
+
export {};
|