@payfit/iac 1.0.0-ephemeral-migration-scripts.7 → 1.0.0-ephemeral-migration-scripts.8
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/README.md +97 -4
- package/dist/executors/stack-migration/README.md +111 -11
- package/dist/executors/stack-migration/lib/constants.d.ts +0 -11
- package/dist/executors/stack-migration/lib/constants.js +1 -12
- package/dist/executors/stack-migration/lib/constants.js.map +1 -1
- package/dist/executors/stack-migration/lib/file-operations.d.ts +0 -43
- package/dist/executors/stack-migration/lib/file-operations.js +0 -88
- package/dist/executors/stack-migration/lib/file-operations.js.map +1 -1
- package/dist/executors/stack-migration/lib/git-operations.d.ts +0 -27
- package/dist/executors/stack-migration/lib/git-operations.js +2 -35
- package/dist/executors/stack-migration/lib/git-operations.js.map +1 -1
- package/dist/executors/stack-migration/lib/logger.d.ts +0 -3
- package/dist/executors/stack-migration/lib/logger.js +3 -6
- package/dist/executors/stack-migration/lib/logger.js.map +1 -1
- package/dist/executors/stack-migration/lib/migration-helper.d.ts +8 -44
- package/dist/executors/stack-migration/lib/migration-helper.js +23 -67
- package/dist/executors/stack-migration/lib/migration-helper.js.map +1 -1
- package/dist/executors/stack-migration/lib/migration-tool.d.ts +4 -15
- package/dist/executors/stack-migration/lib/migration-tool.js +77 -212
- package/dist/executors/stack-migration/lib/migration-tool.js.map +1 -1
- package/dist/executors/stack-migration/lib/path-builder.d.ts +0 -8
- package/dist/executors/stack-migration/lib/path-builder.js +2 -28
- package/dist/executors/stack-migration/lib/path-builder.js.map +1 -1
- package/dist/executors/stack-migration/lib/templates/index.d.ts +1 -0
- package/dist/executors/stack-migration/lib/templates/index.js +1 -0
- package/dist/executors/stack-migration/lib/templates/index.js.map +1 -1
- package/dist/executors/stack-migration/lib/templates/message-templates.d.ts +25 -0
- package/dist/executors/stack-migration/lib/templates/message-templates.js +107 -0
- package/dist/executors/stack-migration/lib/templates/message-templates.js.map +1 -0
- package/dist/executors/stack-migration/lib/templates/pr-templates.d.ts +0 -4
- package/dist/executors/stack-migration/lib/templates/pr-templates.js +3 -24
- package/dist/executors/stack-migration/lib/templates/pr-templates.js.map +1 -1
- package/dist/executors/stack-migration/lib/templates/procedure-template.js +5 -40
- package/dist/executors/stack-migration/lib/templates/procedure-template.js.map +1 -1
- package/dist/executors/stack-migration/lib/types.d.ts +0 -9
- package/dist/executors/stack-migration/lib/types.js +0 -3
- package/dist/executors/stack-migration/lib/types.js.map +1 -1
- package/dist/executors/stack-migration/schema.d.ts +2 -1
- package/dist/executors/stack-migration/schema.json +4 -3
- package/dist/executors/stack-migration/stack-migration.js +30 -60
- package/dist/executors/stack-migration/stack-migration.js.map +1 -1
- package/package.json +3 -2
|
@@ -1,7 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Main migration tool orchestrating all migration steps
|
|
4
|
-
*/
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
exports.MigrationTool = void 0;
|
|
7
4
|
const tslib_1 = require("tslib");
|
|
@@ -11,46 +8,29 @@ const migration_helper_1 = require("./migration-helper");
|
|
|
11
8
|
const spacelift_client_1 = require("./spacelift-client");
|
|
12
9
|
const templates_1 = require("./templates");
|
|
13
10
|
class MigrationTool extends migration_helper_1.MigrationHelper {
|
|
14
|
-
/**
|
|
15
|
-
* Step 1: Setup Repositories
|
|
16
|
-
* Clones required repos, validates spacectl auth
|
|
17
|
-
*/
|
|
18
11
|
async setupRepositories(options) {
|
|
19
12
|
this.logger.step(1, 9, 'Repository Setup and Validation');
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
this.config.serviceRepository.
|
|
13
|
+
if (this.config.resuming &&
|
|
14
|
+
this.config.serviceRepository.name &&
|
|
15
|
+
this.config.serviceRepository.domain &&
|
|
16
|
+
this.config.serviceRepository.team) {
|
|
17
|
+
this.logger.success('Using existing service repository configuration');
|
|
18
|
+
this.logger.info(` Service: ${this.config.serviceRepository.name}`);
|
|
19
|
+
this.logger.info(` Domain: ${this.config.serviceRepository.domain}`);
|
|
20
|
+
this.logger.info(` Team: ${this.config.serviceRepository.team}`);
|
|
23
21
|
}
|
|
24
22
|
else {
|
|
25
23
|
const inferredName = path.basename(process.cwd());
|
|
26
|
-
|
|
27
|
-
this.config.serviceRepository.name
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
if (options?.domain) {
|
|
32
|
-
this.config.serviceRepository.domain = options.domain;
|
|
33
|
-
}
|
|
34
|
-
else {
|
|
35
|
-
this.config.serviceRepository.domain = await this.ask('Domain name (e.g., core-platform, ide, data-platform)');
|
|
36
|
-
}
|
|
37
|
-
if (options?.team) {
|
|
38
|
-
this.config.serviceRepository.team = options.team;
|
|
39
|
-
}
|
|
40
|
-
else {
|
|
41
|
-
this.config.serviceRepository.team = await this.ask('Team name (e.g., infrastructure-developer-experience)');
|
|
42
|
-
}
|
|
43
|
-
if (options?.oktaGroups && options.oktaGroups.length > 0) {
|
|
44
|
-
this.config.serviceRepository.oktaGroups = options.oktaGroups;
|
|
45
|
-
}
|
|
46
|
-
else {
|
|
47
|
-
const oktaGroupsInput = await this.ask('Okta groups (comma-separated, e.g., "Squad - Infrastructure & Developer Experience")');
|
|
24
|
+
this.config.serviceRepository.name = await this.getOrAskConfig(options?.name, 'Service repository name', inferredName);
|
|
25
|
+
this.logger.info(`Using service repository: ${this.config.serviceRepository.name}`);
|
|
26
|
+
this.config.serviceRepository.domain = await this.getOrAskConfig(options?.domain, 'Domain name (e.g., core-platform, ide, data-platform)');
|
|
27
|
+
this.config.serviceRepository.team = await this.getOrAskConfig(options?.team, 'Team name (e.g., infrastructure-developer-experience)');
|
|
28
|
+
const oktaGroupsInput = await this.getOrAskConfig(options?.oktaGroups, 'Okta groups (comma-separated, e.g., "Squad - Infrastructure & Developer Experience")');
|
|
48
29
|
this.config.serviceRepository.oktaGroups = oktaGroupsInput
|
|
49
30
|
.split(',')
|
|
50
31
|
.map(g => g.trim())
|
|
51
32
|
.filter(g => g.length > 0);
|
|
52
33
|
}
|
|
53
|
-
// Clone required repositories to temp directory
|
|
54
34
|
this.logger.info('\nCloning required repositories...');
|
|
55
35
|
if (!this.files.exists(this.paths.tmpDir)) {
|
|
56
36
|
this.files.mkdir(this.paths.tmpDir, { recursive: true });
|
|
@@ -93,16 +73,24 @@ class MigrationTool extends migration_helper_1.MigrationHelper {
|
|
|
93
73
|
else {
|
|
94
74
|
this.logger.info('.gitignore already contains migration entries');
|
|
95
75
|
}
|
|
96
|
-
// Save initial state
|
|
97
|
-
this.config.migrationStartedAt
|
|
76
|
+
// Save initial state (only set timestamp if not resuming)
|
|
77
|
+
if (!this.config.migrationStartedAt) {
|
|
78
|
+
this.config.migrationStartedAt = new Date().toISOString();
|
|
79
|
+
}
|
|
98
80
|
this.saveMigrationState();
|
|
99
81
|
}
|
|
100
82
|
/**
|
|
101
83
|
* Step 2: Load Configuration
|
|
102
|
-
* Load stacks
|
|
84
|
+
* Load stacks from iac-deploy
|
|
103
85
|
*/
|
|
104
86
|
async loadConfiguration() {
|
|
105
|
-
this.logger.step(2, 9, 'Loading Configuration from iac-deploy
|
|
87
|
+
this.logger.step(2, 9, 'Loading Configuration from iac-deploy');
|
|
88
|
+
// Check if configuration is already loaded from state
|
|
89
|
+
if (this.config.resuming && this.config.stacks.length > 0) {
|
|
90
|
+
this.logger.success(`Configuration already loaded from state (${this.config.stacks.length} stack(s))`);
|
|
91
|
+
this.logger.info('Skipping file reads as configuration is already available');
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
106
94
|
// Load stacks from iac-deploy
|
|
107
95
|
const iacConfigPath = this.paths.iacConfigFile(this.config.serviceRepository.name);
|
|
108
96
|
if (!this.files.exists(iacConfigPath)) {
|
|
@@ -115,29 +103,6 @@ class MigrationTool extends migration_helper_1.MigrationHelper {
|
|
|
115
103
|
this.config.stacks = iacConfig.stacks;
|
|
116
104
|
this.logger.success(`Found ${this.config.stacks.length} stack(s) in configuration`);
|
|
117
105
|
}
|
|
118
|
-
// Load ECR repositories from ecr-deploy
|
|
119
|
-
const ecrConfigPath = this.paths.ecrConfigFile(constants_1.ECR_DEPLOY_PATHS.ecrRepos);
|
|
120
|
-
if (this.files.exists(ecrConfigPath)) {
|
|
121
|
-
this.logger.info(`Reading ECR configuration from ecr-deploy...`);
|
|
122
|
-
const ecrConfig = this.files.readYaml(ecrConfigPath);
|
|
123
|
-
const repoSource = `https://github.com/PayFit/${this.config.serviceRepository.name}`;
|
|
124
|
-
// Filter ECR repos by source
|
|
125
|
-
for (const [key, value] of Object.entries(ecrConfig)) {
|
|
126
|
-
if (key === 'payfit_team' || key === 'anchors' || key.startsWith('.'))
|
|
127
|
-
continue;
|
|
128
|
-
const repo = value;
|
|
129
|
-
if (repo?.source === repoSource) {
|
|
130
|
-
this.config.ecrRepositories.push({
|
|
131
|
-
name: key,
|
|
132
|
-
team: repo.owner || this.config.serviceRepository.team,
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
this.logger.success(`Found ${this.config.ecrRepositories.length} ECR repository(ies)`);
|
|
137
|
-
}
|
|
138
|
-
else {
|
|
139
|
-
this.logger.info('No ECR configuration found (this is fine)');
|
|
140
|
-
}
|
|
141
106
|
// Save state
|
|
142
107
|
this.saveMigrationState();
|
|
143
108
|
}
|
|
@@ -149,15 +114,9 @@ class MigrationTool extends migration_helper_1.MigrationHelper {
|
|
|
149
114
|
// Skip if already created
|
|
150
115
|
if (this.config.generatedFiles.safetyNetPR) {
|
|
151
116
|
this.logger.success(`Safety net PR already exists: ${this.config.generatedFiles.safetyNetPR.url}`);
|
|
152
|
-
const shouldContinue = await this.confirm('\nHas this PR been merged?', false);
|
|
153
|
-
if (!shouldContinue) {
|
|
154
|
-
this.logger.warn('Please merge the safety net PR before continuing');
|
|
155
|
-
this.logger.info('Run this script again when ready');
|
|
156
|
-
process.exit(0);
|
|
157
|
-
}
|
|
158
117
|
return;
|
|
159
118
|
}
|
|
160
|
-
this.logger.info(
|
|
119
|
+
this.logger.info(templates_1.MessageTemplates.SAFETY_NET_DESCRIPTION);
|
|
161
120
|
const iacConfigPath = this.paths.iacConfigFile(this.config.serviceRepository.name);
|
|
162
121
|
// Read the current config
|
|
163
122
|
const iacConfig = this.files.readYaml(iacConfigPath);
|
|
@@ -181,7 +140,7 @@ class MigrationTool extends migration_helper_1.MigrationHelper {
|
|
|
181
140
|
return;
|
|
182
141
|
}
|
|
183
142
|
// Create branch
|
|
184
|
-
const branchName = `safety-net-${this.config.serviceRepository.name}
|
|
143
|
+
const branchName = this.generateBranchName(`safety-net-${this.config.serviceRepository.name}`);
|
|
185
144
|
await this.git.createBranch(branchName, this.config.repositories.iacDeploy);
|
|
186
145
|
// Update all stacks to have autodeploy: false
|
|
187
146
|
this.files.updateAutodeployInYaml(iacConfigPath, false);
|
|
@@ -196,17 +155,9 @@ class MigrationTool extends migration_helper_1.MigrationHelper {
|
|
|
196
155
|
branch: branchName,
|
|
197
156
|
url: prUrl,
|
|
198
157
|
};
|
|
199
|
-
// Save state after creating PR
|
|
200
158
|
this.saveMigrationState();
|
|
201
159
|
this.logger.success(`Safety net PR created: ${prUrl}`);
|
|
202
|
-
this.logger.warn(
|
|
203
|
-
const shouldContinue = await this.confirm('\nHave you merged the safety net PR?', false);
|
|
204
|
-
if (!shouldContinue) {
|
|
205
|
-
this.logger.warn('Please merge the safety net PR before continuing');
|
|
206
|
-
this.logger.info('Run this script again when ready');
|
|
207
|
-
process.exit(0);
|
|
208
|
-
}
|
|
209
|
-
this.logger.success('Safety net PR merged - stacks are now safe from changes');
|
|
160
|
+
this.logger.warn(templates_1.MessageTemplates.SAFETY_NET_IMPORTANT);
|
|
210
161
|
}
|
|
211
162
|
/**
|
|
212
163
|
* Step 4: Create Domain Enrollment PR
|
|
@@ -218,9 +169,7 @@ class MigrationTool extends migration_helper_1.MigrationHelper {
|
|
|
218
169
|
this.logger.success(`Domain enrollment PR already exists: ${this.config.generatedFiles.domainEnrollmentPR.url}`);
|
|
219
170
|
const shouldContinue = await this.confirm('\nHas this PR been merged?', false);
|
|
220
171
|
if (!shouldContinue) {
|
|
221
|
-
this.
|
|
222
|
-
this.logger.info('Run this script again when ready');
|
|
223
|
-
process.exit(0);
|
|
172
|
+
this.exitWithMessage('Please merge the PR before continuing');
|
|
224
173
|
}
|
|
225
174
|
return;
|
|
226
175
|
}
|
|
@@ -299,7 +248,7 @@ class MigrationTool extends migration_helper_1.MigrationHelper {
|
|
|
299
248
|
}
|
|
300
249
|
// Only create branch and commit if there are changes
|
|
301
250
|
if (needsEnrollment) {
|
|
302
|
-
const branchName = `enroll-${this.config.serviceRepository.name}
|
|
251
|
+
const branchName = this.generateBranchName(`enroll-${this.config.serviceRepository.name}`);
|
|
303
252
|
await this.git.createBranch(branchName, this.config.repositories.domainEnrollment);
|
|
304
253
|
await this.git.add(['.'], this.config.repositories.domainEnrollment);
|
|
305
254
|
await this.git.commit(`feat: enroll ${this.config.serviceRepository.name} in ${this.config.serviceRepository.domain} domain`, this.config.repositories.domainEnrollment);
|
|
@@ -312,7 +261,6 @@ class MigrationTool extends migration_helper_1.MigrationHelper {
|
|
|
312
261
|
branch: branchName,
|
|
313
262
|
url: prUrl,
|
|
314
263
|
};
|
|
315
|
-
// Save state after creating PR
|
|
316
264
|
this.saveMigrationState();
|
|
317
265
|
this.logger.info('\nNext steps for domain enrollment:');
|
|
318
266
|
this.logger.info(`1. Review PR: ${prUrl}`);
|
|
@@ -320,11 +268,9 @@ class MigrationTool extends migration_helper_1.MigrationHelper {
|
|
|
320
268
|
this.logger.info('3. Merge PR to deploy repository metastack');
|
|
321
269
|
const shouldContinue = await this.confirm('\nHave you merged the domain enrollment PR?', false);
|
|
322
270
|
if (!shouldContinue) {
|
|
323
|
-
this.
|
|
324
|
-
this.logger.info('Run this script again when ready');
|
|
325
|
-
process.exit(0);
|
|
271
|
+
this.exitWithMessage('Please merge the PR before continuing');
|
|
326
272
|
}
|
|
327
|
-
this.logger.success(
|
|
273
|
+
this.logger.success(templates_1.MessageTemplates.DOMAIN_ENROLLMENT_SUCCESS);
|
|
328
274
|
}
|
|
329
275
|
else {
|
|
330
276
|
this.logger.success('Repository already enrolled - skipping domain enrollment PR');
|
|
@@ -333,8 +279,8 @@ class MigrationTool extends migration_helper_1.MigrationHelper {
|
|
|
333
279
|
// Save state after marking enrollment as done
|
|
334
280
|
this.saveMigrationState();
|
|
335
281
|
// Check if metastack exists
|
|
336
|
-
this.logger.info(
|
|
337
|
-
const shouldContinue = await this.confirm(
|
|
282
|
+
this.logger.info(templates_1.MessageTemplates.DOMAIN_ENROLLMENT_ALREADY_EXISTS);
|
|
283
|
+
const shouldContinue = await this.confirm(templates_1.MessageTemplates.DOMAIN_ENROLLMENT_CONFIRM, true);
|
|
338
284
|
if (!shouldContinue) {
|
|
339
285
|
this.logger.info('Exiting migration');
|
|
340
286
|
process.exit(0);
|
|
@@ -342,10 +288,10 @@ class MigrationTool extends migration_helper_1.MigrationHelper {
|
|
|
342
288
|
}
|
|
343
289
|
}
|
|
344
290
|
/**
|
|
345
|
-
* Step 5: Create Service Repository PR (spacelift.yaml,
|
|
291
|
+
* Step 5: Create Service Repository PR (spacelift.yaml, import.tf)
|
|
346
292
|
*/
|
|
347
293
|
async createServiceRepositoryPR() {
|
|
348
|
-
this.logger.step(5, 9, 'Create Service Repository PR (spacelift.yaml,
|
|
294
|
+
this.logger.step(5, 9, 'Create Service Repository PR (spacelift.yaml, .metastack/import.tf)');
|
|
349
295
|
// Create safety net PR first if not already created
|
|
350
296
|
if (!this.config.generatedFiles.safetyNetPR) {
|
|
351
297
|
this.logger.info('\nSafety net PR not yet created, creating it first...');
|
|
@@ -354,7 +300,7 @@ class MigrationTool extends migration_helper_1.MigrationHelper {
|
|
|
354
300
|
// Skip if already created
|
|
355
301
|
if (this.config.generatedFiles.serviceRepoPR) {
|
|
356
302
|
this.logger.success(`Service repository PR already exists: ${this.config.generatedFiles.serviceRepoPR.url}`);
|
|
357
|
-
this.logger.warn(
|
|
303
|
+
this.logger.warn(templates_1.MessageTemplates.SERVICE_REPO_NOT_MERGE_WARNING);
|
|
358
304
|
return;
|
|
359
305
|
}
|
|
360
306
|
// Ask for confirmation before creating PRs
|
|
@@ -370,18 +316,6 @@ class MigrationTool extends migration_helper_1.MigrationHelper {
|
|
|
370
316
|
if (!this.dryRun) {
|
|
371
317
|
this.logger.success('Created spacelift.yaml');
|
|
372
318
|
}
|
|
373
|
-
// Create ecr.yaml
|
|
374
|
-
if (this.config.ecrRepositories.length > 0) {
|
|
375
|
-
const ecrData = {
|
|
376
|
-
payfit_team: this.config.serviceRepository.team,
|
|
377
|
-
repositories: this.config.ecrRepositories.map(r => ({ name: r.name })),
|
|
378
|
-
};
|
|
379
|
-
const ecrPath = this.paths.serviceRepoEcrYaml();
|
|
380
|
-
this.files.writeYaml(ecrPath, ecrData);
|
|
381
|
-
if (!this.dryRun) {
|
|
382
|
-
this.logger.success('Created ecr.yaml');
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
319
|
// Create .metastack/import.tf
|
|
386
320
|
await this.generateImportTf();
|
|
387
321
|
// Update .gitignore to exclude migration temporary files
|
|
@@ -399,10 +333,10 @@ class MigrationTool extends migration_helper_1.MigrationHelper {
|
|
|
399
333
|
}
|
|
400
334
|
}
|
|
401
335
|
// Create branch and commit
|
|
402
|
-
const branchName =
|
|
336
|
+
const branchName = this.generateBranchName('metastack-migration');
|
|
403
337
|
await this.git.createBranch(branchName, this.config.serviceRepository.path);
|
|
404
|
-
await this.git.add(['spacelift.yaml', '
|
|
405
|
-
await this.git.commit(`feat: migrate to metastack\n\nAdd spacelift.yaml
|
|
338
|
+
await this.git.add(['spacelift.yaml', '.metastack/', '.gitignore'], this.config.serviceRepository.path);
|
|
339
|
+
await this.git.commit(`feat: migrate to metastack\n\nAdd spacelift.yaml and import.tf for metastack migration`, this.config.serviceRepository.path);
|
|
406
340
|
if (!this.dryRun) {
|
|
407
341
|
this.logger.success(`Created branch: ${branchName}`);
|
|
408
342
|
}
|
|
@@ -412,18 +346,19 @@ class MigrationTool extends migration_helper_1.MigrationHelper {
|
|
|
412
346
|
branch: branchName,
|
|
413
347
|
url: prUrl,
|
|
414
348
|
};
|
|
415
|
-
// Save state after creating PR
|
|
416
349
|
this.saveMigrationState();
|
|
417
|
-
this.logger.warn(
|
|
418
|
-
this.logger.info(
|
|
350
|
+
this.logger.warn(templates_1.MessageTemplates.CLEANUP_PR_NOT_MERGE_WARNING);
|
|
351
|
+
this.logger.info(templates_1.MessageTemplates.CLEANUP_PR_MERGE_TIMING);
|
|
419
352
|
this.logger.info(`PR URL: ${prUrl}`);
|
|
420
353
|
}
|
|
421
354
|
/**
|
|
422
|
-
* Generate import.tf file with import blocks for stacks
|
|
355
|
+
* Generate import.tf file with import blocks for stacks
|
|
423
356
|
*/
|
|
424
357
|
async generateImportTf() {
|
|
425
358
|
const metastackDir = this.paths.metastackDir;
|
|
426
359
|
this.files.mkdir(metastackDir, { recursive: true });
|
|
360
|
+
// Ensure spacectl is authenticated before fetching resources
|
|
361
|
+
await this.ensureSpacectlAuthentication();
|
|
427
362
|
this.logger.info('Fetching stack resources from projects-manager...');
|
|
428
363
|
// Fetch all resources from projects-manager
|
|
429
364
|
let projectManagerResources = '';
|
|
@@ -438,28 +373,12 @@ class MigrationTool extends migration_helper_1.MigrationHelper {
|
|
|
438
373
|
let importTf = `# Import existing resources to metastack
|
|
439
374
|
# Generated by migrate-to-metastack.ts
|
|
440
375
|
# This file contains import blocks for:
|
|
441
|
-
# - ECR repositories
|
|
442
376
|
# - Spacelift stacks
|
|
443
377
|
# - AWS integration attachments
|
|
444
378
|
# - Context attachments
|
|
445
379
|
# - Stack dependencies
|
|
446
380
|
|
|
447
381
|
`;
|
|
448
|
-
// Import ECR repositories
|
|
449
|
-
if (this.config.ecrRepositories.length > 0) {
|
|
450
|
-
importTf += `# ==============================================================================
|
|
451
|
-
# ECR Repositories
|
|
452
|
-
# ==============================================================================
|
|
453
|
-
`;
|
|
454
|
-
for (const repo of this.config.ecrRepositories) {
|
|
455
|
-
importTf += `import {
|
|
456
|
-
to = module.ecr_private["${repo.name}"].aws_ecr_repository.this
|
|
457
|
-
id = "${repo.name}"
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
`;
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
382
|
// Import Stacks and their attachments
|
|
464
383
|
if (this.config.stacks.length > 0) {
|
|
465
384
|
importTf += `# ==============================================================================
|
|
@@ -475,8 +394,8 @@ class MigrationTool extends migration_helper_1.MigrationHelper {
|
|
|
475
394
|
# 4. Stack dependencies (if any)
|
|
476
395
|
\n`;
|
|
477
396
|
for (const stack of this.config.stacks) {
|
|
478
|
-
const stackId =
|
|
479
|
-
const moduleKey =
|
|
397
|
+
const stackId = this.getStackModuleKey(stack);
|
|
398
|
+
const moduleKey = this.getStackModuleKey(stack);
|
|
480
399
|
this.logger.info(` Processing stack: ${stackId}`);
|
|
481
400
|
// Parse resources to extract attachments for this specific stack module
|
|
482
401
|
const { contexts, dependencies } = this.parseStackModuleResources(projectManagerResources, moduleKey);
|
|
@@ -554,9 +473,6 @@ import {
|
|
|
554
473
|
if (!this.dryRun) {
|
|
555
474
|
this.logger.success('Created .metastack/import.tf with import blocks');
|
|
556
475
|
this.logger.info(` Total stacks: ${this.config.stacks.length}`);
|
|
557
|
-
if (this.config.ecrRepositories.length > 0) {
|
|
558
|
-
this.logger.info(` Total ECR repositories: ${this.config.ecrRepositories.length}`);
|
|
559
|
-
}
|
|
560
476
|
}
|
|
561
477
|
}
|
|
562
478
|
/**
|
|
@@ -628,24 +544,20 @@ import {
|
|
|
628
544
|
return { contexts, dependencies };
|
|
629
545
|
}
|
|
630
546
|
/**
|
|
631
|
-
* Step 6: Create Cleanup
|
|
547
|
+
* Step 6: Create Cleanup PR for iac-deploy
|
|
632
548
|
*/
|
|
633
549
|
async createCleanupPRs() {
|
|
634
|
-
this.logger.step(6, 9, 'Create Cleanup
|
|
635
|
-
// Ask for confirmation before creating cleanup
|
|
636
|
-
const shouldCreate = await this.confirm('\nCreate and push cleanup
|
|
550
|
+
this.logger.step(6, 9, 'Create Cleanup PR for iac-deploy (remove old resources)');
|
|
551
|
+
// Ask for confirmation before creating cleanup PR
|
|
552
|
+
const shouldCreate = await this.confirm('\nCreate and push cleanup PR (iac-deploy)?', true);
|
|
637
553
|
if (!shouldCreate) {
|
|
638
554
|
this.logger.warn('Skipping cleanup PR creation');
|
|
639
555
|
return;
|
|
640
556
|
}
|
|
641
|
-
// iac-deploy cleanup
|
|
642
557
|
await this.createIacDeployCleanupPR();
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
}
|
|
647
|
-
this.logger.warn('\nDO NOT MERGE THESE PRs YET');
|
|
648
|
-
this.logger.info('State removal commands must be run first');
|
|
558
|
+
await this.generateMigrationProcedure();
|
|
559
|
+
this.logger.warn(templates_1.MessageTemplates.CLEANUP_PR_NOT_MERGE_WARNING);
|
|
560
|
+
this.logger.info(templates_1.MessageTemplates.CLEANUP_PR_STATE_REMOVAL_WARNING);
|
|
649
561
|
}
|
|
650
562
|
/**
|
|
651
563
|
* Create cleanup PR for iac-deploy
|
|
@@ -656,7 +568,7 @@ import {
|
|
|
656
568
|
// Remove the config file
|
|
657
569
|
const shouldDelete = await this.confirm(`Delete ${this.config.serviceRepository.name}.yaml from iac-deploy?`, true);
|
|
658
570
|
if (shouldDelete) {
|
|
659
|
-
const branchName = `cleanup-${this.config.serviceRepository.name}
|
|
571
|
+
const branchName = this.generateBranchName(`cleanup-${this.config.serviceRepository.name}`);
|
|
660
572
|
await this.git.createBranch(branchName, this.config.repositories.iacDeploy);
|
|
661
573
|
this.files.deleteFile(configPath);
|
|
662
574
|
await this.git.add(['.'], this.config.repositories.iacDeploy);
|
|
@@ -670,42 +582,6 @@ import {
|
|
|
670
582
|
branch: branchName,
|
|
671
583
|
url: prUrl,
|
|
672
584
|
};
|
|
673
|
-
// Save state after creating PR
|
|
674
|
-
this.saveMigrationState();
|
|
675
|
-
this.logger.info(`PR URL: ${prUrl}`);
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
|
-
/**
|
|
679
|
-
* Create cleanup PR for ecr-deploy
|
|
680
|
-
*/
|
|
681
|
-
async createEcrDeployCleanupPR() {
|
|
682
|
-
this.logger.info('\nCreating ecr-deploy cleanup PR...');
|
|
683
|
-
const ecrConfigPath = this.paths.ecrConfigFile(constants_1.ECR_DEPLOY_PATHS.ecrRepos);
|
|
684
|
-
// Check which repositories need to be removed
|
|
685
|
-
const ecrConfig = this.files.readYaml(ecrConfigPath);
|
|
686
|
-
const reposToRemove = this.config.ecrRepositories
|
|
687
|
-
.map(r => r.name)
|
|
688
|
-
.filter(name => ecrConfig[name] !== undefined);
|
|
689
|
-
if (reposToRemove.length > 0) {
|
|
690
|
-
reposToRemove.forEach(name => {
|
|
691
|
-
this.logger.info(` Will remove ${name}`);
|
|
692
|
-
});
|
|
693
|
-
const branchName = `cleanup-${this.config.serviceRepository.name}-ecr-${Date.now()}`;
|
|
694
|
-
await this.git.createBranch(branchName, this.config.repositories.ecrDeploy);
|
|
695
|
-
// Use removeYamlKeys to preserve file formatting
|
|
696
|
-
this.files.removeYamlKeys(ecrConfigPath, reposToRemove);
|
|
697
|
-
await this.git.add(['.'], this.config.repositories.ecrDeploy);
|
|
698
|
-
await this.git.commit(`chore: remove ECR repos for ${this.config.serviceRepository.name} (migrated to metastack)`, this.config.repositories.ecrDeploy);
|
|
699
|
-
if (!this.dryRun) {
|
|
700
|
-
this.logger.success(`Created ecr-deploy cleanup branch: ${branchName}`);
|
|
701
|
-
}
|
|
702
|
-
// Push and create draft PR
|
|
703
|
-
const prUrl = await this.git.createPR(branchName, `chore: remove ECR repos for ${this.config.serviceRepository.name} (migrated to metastack)`, templates_1.PRTemplates.ecrDeployCleanupPRBody(this.config.serviceRepository.name, this.config.ecrRepositories.length), this.config.repositories.ecrDeploy);
|
|
704
|
-
this.config.generatedFiles.ecrDeployPR = {
|
|
705
|
-
branch: branchName,
|
|
706
|
-
url: prUrl,
|
|
707
|
-
};
|
|
708
|
-
// Save state after creating PR
|
|
709
585
|
this.saveMigrationState();
|
|
710
586
|
this.logger.info(`PR URL: ${prUrl}`);
|
|
711
587
|
}
|
|
@@ -720,18 +596,6 @@ import {
|
|
|
720
596
|
# They use spacectl to safely remove resources from Terraform state
|
|
721
597
|
|
|
722
598
|
`;
|
|
723
|
-
// ECR state removal from ecr-deploy-shared-eu-west-3
|
|
724
|
-
if (this.config.ecrRepositories.length > 0) {
|
|
725
|
-
commands += `# Remove ECR repositories from ecr-deploy-shared-eu-west-3 stack
|
|
726
|
-
# These repositories will be managed by the metastack after migration
|
|
727
|
-
# The underlying ECR repositories remain untouched
|
|
728
|
-
|
|
729
|
-
`;
|
|
730
|
-
for (const repo of this.config.ecrRepositories) {
|
|
731
|
-
commands += `spacectl stack task --id="${constants_1.STACK_MANAGERS.ecrDeployShared}" --command="tofu state rm 'module.ecr_private[\\"${repo.name}\\"]'"\n`;
|
|
732
|
-
}
|
|
733
|
-
commands += `\n`;
|
|
734
|
-
}
|
|
735
599
|
// Stack state removal from projects-manager
|
|
736
600
|
if (this.config.stacks.length > 0) {
|
|
737
601
|
commands += `# Remove stack definitions from projects-manager
|
|
@@ -741,7 +605,7 @@ import {
|
|
|
741
605
|
|
|
742
606
|
`;
|
|
743
607
|
for (const stack of this.config.stacks) {
|
|
744
|
-
const moduleKey =
|
|
608
|
+
const moduleKey = this.getStackModuleKey(stack);
|
|
745
609
|
commands += `spacectl stack task --id="${constants_1.STACK_MANAGERS.projectsManager}" --command="tofu state rm 'module.stacks_generator[\\"${moduleKey}\\"]'"\n`;
|
|
746
610
|
}
|
|
747
611
|
commands += `\n`;
|
|
@@ -753,7 +617,7 @@ import {
|
|
|
753
617
|
this.config.generatedFiles.stateRemovalCommands = commandsPath;
|
|
754
618
|
// Save state after generating commands
|
|
755
619
|
this.saveMigrationState();
|
|
756
|
-
this.logger.info(
|
|
620
|
+
this.logger.info(templates_1.MessageTemplates.STATE_REMOVAL_REVIEW_WARNING);
|
|
757
621
|
}
|
|
758
622
|
}
|
|
759
623
|
/**
|
|
@@ -761,17 +625,19 @@ import {
|
|
|
761
625
|
*/
|
|
762
626
|
async updateStackSpaces() {
|
|
763
627
|
this.logger.step(8, 9, 'Update Stack Spaces (Move stacks to domain space)');
|
|
764
|
-
this.logger.info(
|
|
765
|
-
this.logger.info(
|
|
628
|
+
this.logger.info(templates_1.MessageTemplates.STACK_SPACES_DESCRIPTION);
|
|
629
|
+
this.logger.info(templates_1.MessageTemplates.STACK_SPACES_REASON);
|
|
766
630
|
this.logger.info('');
|
|
767
|
-
this.logger.warn(
|
|
631
|
+
this.logger.warn(templates_1.MessageTemplates.STACK_SPACES_IMPORTANT_TIMING);
|
|
768
632
|
this.logger.info('');
|
|
769
633
|
const shouldUpdate = await this.confirm('Update stack spaces now?', false);
|
|
770
634
|
if (!shouldUpdate) {
|
|
771
635
|
this.logger.warn('Skipping stack space update');
|
|
772
|
-
this.logger.info(
|
|
636
|
+
this.logger.info(templates_1.MessageTemplates.STACK_SPACES_SKIP_LATER);
|
|
773
637
|
return;
|
|
774
638
|
}
|
|
639
|
+
// Ensure spacectl is authenticated before making API calls
|
|
640
|
+
await this.ensureSpacectlAuthentication();
|
|
775
641
|
// Determine the target space name
|
|
776
642
|
const targetSpaceName = `domain-${this.config.serviceRepository.domain}`;
|
|
777
643
|
this.logger.info(`Target space: ${targetSpaceName}`);
|
|
@@ -780,7 +646,7 @@ import {
|
|
|
780
646
|
this.logger.info(` Target space: ${targetSpaceName}`);
|
|
781
647
|
this.logger.info(` Stacks to update: ${this.config.stacks.length}`);
|
|
782
648
|
for (const stack of this.config.stacks) {
|
|
783
|
-
const stackId =
|
|
649
|
+
const stackId = this.getStackModuleKey(stack);
|
|
784
650
|
this.logger.info(` - ${stackId}`);
|
|
785
651
|
}
|
|
786
652
|
return;
|
|
@@ -798,7 +664,7 @@ import {
|
|
|
798
664
|
this.logger.info(`\nUpdating ${this.config.stacks.length} stacks...`);
|
|
799
665
|
const results = [];
|
|
800
666
|
for (const stack of this.config.stacks) {
|
|
801
|
-
const stackId =
|
|
667
|
+
const stackId = this.getStackModuleKey(stack);
|
|
802
668
|
try {
|
|
803
669
|
// Check current space
|
|
804
670
|
const currentStack = await client.getStack(stackId);
|
|
@@ -895,10 +761,6 @@ import {
|
|
|
895
761
|
name: 'iac-deploy Cleanup',
|
|
896
762
|
pr: this.config.generatedFiles.iacDeployPR,
|
|
897
763
|
},
|
|
898
|
-
{
|
|
899
|
-
name: 'ecr-deploy Cleanup',
|
|
900
|
-
pr: this.config.generatedFiles.ecrDeployPR,
|
|
901
|
-
},
|
|
902
764
|
];
|
|
903
765
|
for (const { name, pr } of prs) {
|
|
904
766
|
if (pr) {
|
|
@@ -979,7 +841,13 @@ import {
|
|
|
979
841
|
this.logger.info('This tool will guide you through the metastack migration');
|
|
980
842
|
this.logger.info('');
|
|
981
843
|
if (this.dryRun) {
|
|
982
|
-
this.logger.warn(
|
|
844
|
+
this.logger.warn(templates_1.MessageTemplates.DRY_RUN_MODE_WARNING);
|
|
845
|
+
this.logger.info('');
|
|
846
|
+
}
|
|
847
|
+
// Load existing migration state if available
|
|
848
|
+
const hasExistingState = this.loadMigrationState();
|
|
849
|
+
if (hasExistingState) {
|
|
850
|
+
this.logger.info('Resuming from existing migration state');
|
|
983
851
|
this.logger.info('');
|
|
984
852
|
}
|
|
985
853
|
await this.setupRepositories();
|
|
@@ -989,8 +857,6 @@ import {
|
|
|
989
857
|
await this.createServiceRepositoryPR();
|
|
990
858
|
await this.createCleanupPRs();
|
|
991
859
|
await this.generateStateRemovalCommands();
|
|
992
|
-
// Optional: Update stack spaces
|
|
993
|
-
await this.updateStackSpaces();
|
|
994
860
|
await this.generateMigrationProcedure();
|
|
995
861
|
this.logger.section('Migration Setup Complete!');
|
|
996
862
|
this.logger.success('All preparation complete');
|
|
@@ -1009,20 +875,19 @@ import {
|
|
|
1009
875
|
if (this.config.generatedFiles.iacDeployPR) {
|
|
1010
876
|
this.logger.info(` iac-deploy Cleanup: ${this.config.generatedFiles.iacDeployPR.url}`);
|
|
1011
877
|
}
|
|
1012
|
-
if (this.config.generatedFiles.ecrDeployPR) {
|
|
1013
|
-
this.logger.info(` ecr-deploy Cleanup: ${this.config.generatedFiles.ecrDeployPR.url}`);
|
|
1014
|
-
}
|
|
1015
878
|
this.logger.info('');
|
|
1016
879
|
this.logger.info('Generated files:');
|
|
1017
880
|
this.logger.info(` - ${this.config.generatedFiles.migrationProcedure}`);
|
|
1018
881
|
this.logger.info(` - ${this.config.generatedFiles.stateRemovalCommands}`);
|
|
1019
882
|
this.logger.info('');
|
|
1020
|
-
this.logger.warn(
|
|
883
|
+
this.logger.warn(templates_1.MessageTemplates.FINAL_WORKFLOW_NEXT_STEPS);
|
|
1021
884
|
this.logger.info('1. Review the PRs above');
|
|
1022
885
|
this.logger.info('2. Review METASTACK_MIGRATION_PROCEDURE.md');
|
|
1023
|
-
this.logger.info('3.
|
|
886
|
+
this.logger.info('3. Execute state removal commands from METASTACK_STATE_REMOVAL_COMMANDS.sh');
|
|
887
|
+
this.logger.info('4. Run migrate-spaces command manually: nx run <project>:stack-migration --command=migrate-spaces');
|
|
888
|
+
this.logger.info('5. Follow the migration procedure to merge PRs in correct order');
|
|
1024
889
|
this.logger.info('');
|
|
1025
|
-
this.logger.warn(
|
|
890
|
+
this.logger.warn(templates_1.MessageTemplates.FINAL_WORKFLOW_EXECUTE_ORDER_WARNING);
|
|
1026
891
|
this.logger.info('');
|
|
1027
892
|
this.logger.info('Note: Migration state saved to .migration-state.json for resumability');
|
|
1028
893
|
}
|