@fishawack/lab-env 5.7.0-beta.1 → 5.7.0-beta.3

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.
Files changed (37) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/_Test/provision.js +4 -9
  3. package/_Test/prune.js +8 -5
  4. package/bitbucket-pipelines.yml +2 -2
  5. package/cli.js +1 -0
  6. package/commands/create/cmds/dekey.js +13 -9
  7. package/commands/create/cmds/deprovision.js +36 -0
  8. package/commands/create/cmds/key.js +60 -41
  9. package/commands/create/cmds/provision.js +573 -69
  10. package/commands/create/cmds/rekey.js +218 -0
  11. package/commands/create/cmds/sync-secrets.js +2 -1
  12. package/commands/create/libs/output-credentials.js +59 -0
  13. package/commands/create/libs/parallel-runner.js +204 -0
  14. package/commands/create/libs/resolve-operator.js +59 -0
  15. package/commands/create/libs/utilities.js +73 -8
  16. package/commands/create/libs/vars.js +120 -136
  17. package/commands/create/services/aws/acm.js +61 -0
  18. package/commands/create/services/aws/cloudfront.js +51 -45
  19. package/commands/create/services/aws/ec2.js +675 -48
  20. package/commands/create/services/aws/elasticache.js +159 -0
  21. package/commands/create/services/aws/elasticbeanstalk.js +154 -57
  22. package/commands/create/services/aws/iam.js +402 -82
  23. package/commands/create/services/aws/index.js +1398 -260
  24. package/commands/create/services/aws/opensearch.js +155 -0
  25. package/commands/create/services/aws/rds.js +57 -31
  26. package/commands/create/services/aws/s3.js +52 -31
  27. package/commands/create/services/aws/secretsmanager.js +21 -12
  28. package/commands/create/services/aws/ses.js +195 -0
  29. package/commands/create/templates/elasticbeanstalk/.ebextensions/laravel/cron.config +45 -0
  30. package/commands/create/templates/elasticbeanstalk/.ebextensions/laravel/queue-worker.config +29 -0
  31. package/commands/create/templates/elasticbeanstalk/.ebextensions/laravel/software.config +12 -0
  32. package/commands/create/templates/elasticbeanstalk/.ebextensions/misc/setvars.config +2 -16
  33. package/commands/create/templates/elasticbeanstalk/.platform/confighooks/postdeploy/rewrite-flush.sh +1 -1
  34. package/commands/create/templates/elasticbeanstalk/.platform/confighooks/postdeploy/setvars.sh +19 -0
  35. package/commands/create/templates/elasticbeanstalk/.platform/hooks/postdeploy/restart_queue.sh +4 -0
  36. package/commands/create/templates/elasticbeanstalk/.platform/hooks/prebuild/setvars.sh +19 -0
  37. package/package.json +7 -2
@@ -18,6 +18,11 @@ module.exports = [
18
18
  describe: "Branch to configure",
19
19
  type: "string",
20
20
  });
21
+ yargs.option("green-blue", {
22
+ alias: "g",
23
+ describe: "Clone an existing environment (pass source branch name)",
24
+ type: "string",
25
+ });
21
26
  },
22
27
  async (argv) => {
23
28
  let branch = argv.branch || _.branch;
@@ -38,6 +43,190 @@ module.exports = [
38
43
  process.exit(1);
39
44
  }
40
45
 
46
+ // Green/Blue clone flow — entirely separate path
47
+ if (argv.greenBlue) {
48
+ let gbAnswers = await inquirer.prompt([client, region]);
49
+
50
+ setAWSClientDefaults(gbAnswers.client, gbAnswers.region);
51
+
52
+ // List existing EB applications
53
+ const applications = await aws.elasticbeanstalk.listApplications();
54
+
55
+ if (!applications.length) {
56
+ console.log(
57
+ utilities.colorize(
58
+ "\n ✗ No EB applications found in this account/region\n",
59
+ "error",
60
+ ),
61
+ );
62
+ process.exit(1);
63
+ }
64
+
65
+ // List all environments across applications
66
+ let allEnvironments = [];
67
+ for (const app of applications) {
68
+ const envs = await aws.elasticbeanstalk.listEnvironments(
69
+ app.ApplicationName,
70
+ );
71
+ allEnvironments = allEnvironments.concat(
72
+ envs.map((e) => ({
73
+ ...e,
74
+ ApplicationName: app.ApplicationName,
75
+ })),
76
+ );
77
+ }
78
+
79
+ if (!allEnvironments.length) {
80
+ console.log(
81
+ utilities.colorize(
82
+ "\n ✗ No active EB environments found\n",
83
+ "error",
84
+ ),
85
+ );
86
+ process.exit(1);
87
+ }
88
+
89
+ const { sourceEnv } = await inquirer.prompt([
90
+ {
91
+ type: "list",
92
+ name: "sourceEnv",
93
+ message: "Which environment should be cloned?",
94
+ choices: allEnvironments.map((e) => ({
95
+ name: `${e.EnvironmentName} (${e.Status})`,
96
+ value: e,
97
+ })),
98
+ },
99
+ ]);
100
+
101
+ // Validate branch doesn't conflict with source
102
+ const sourceSlug = sourceEnv.EnvironmentName;
103
+ const newSlug = aws.slug(
104
+ _.repoSafe,
105
+ gbAnswers.client,
106
+ branch,
107
+ "eb",
108
+ );
109
+
110
+ if (newSlug === sourceSlug) {
111
+ const { newBranch } = await inquirer.prompt([
112
+ {
113
+ type: "input",
114
+ name: "newBranch",
115
+ message:
116
+ "Branch conflicts with source. Enter a different branch name:",
117
+ default: `${branch}-green`,
118
+ validate: (input) => {
119
+ const slug = aws.slug(
120
+ _.repoSafe,
121
+ gbAnswers.client,
122
+ input,
123
+ "eb",
124
+ );
125
+ if (slug === sourceSlug) {
126
+ return "Branch still conflicts with source environment";
127
+ }
128
+ return true;
129
+ },
130
+ },
131
+ ]);
132
+ branch = newBranch;
133
+ }
134
+
135
+ const slug = aws.slug(_.repoSafe, gbAnswers.client, branch, "eb");
136
+
137
+ // Check the clone doesn't already exist
138
+ const { Environments } =
139
+ await aws.elasticbeanstalk.describeEnvironment(slug);
140
+ const active = (Environments || []).filter(
141
+ (e) => e.Status !== "Terminated",
142
+ );
143
+ if (active.length) {
144
+ console.log(
145
+ utilities.colorize(
146
+ `\n ✗ Environment "${slug}" already exists (${active[0].Status})\n`,
147
+ "error",
148
+ ),
149
+ );
150
+ process.exit(1);
151
+ }
152
+
153
+ const environmentChoices = ["production", "staging", "test"];
154
+ const defaultEnvironment =
155
+ environmentChoices.find((e) =>
156
+ branch.toLowerCase().includes(e),
157
+ ) || "test";
158
+
159
+ // Get environment tag from source
160
+ const sourceTags = await aws.elasticbeanstalk.getEnvironmentTags(
161
+ sourceEnv.EnvironmentArn,
162
+ );
163
+ const sourceEnvTag = sourceTags.find(
164
+ (t) => t.Key === "environment",
165
+ );
166
+ const envTagValue = sourceEnvTag
167
+ ? sourceEnvTag.Value
168
+ : defaultEnvironment;
169
+
170
+ const tags = [
171
+ { Key: "repository", Value: _.repo },
172
+ { Key: "environment", Value: envTagValue },
173
+ { Key: "branch", Value: branch },
174
+ { Key: "automated", Value: "true" },
175
+ { Key: "cloned-from", Value: sourceEnv.EnvironmentName },
176
+ ];
177
+
178
+ try {
179
+ const infastructure = await aws.fullstackClone(
180
+ slug,
181
+ tags,
182
+ _.repoSafe,
183
+ branch,
184
+ sourceEnv.ApplicationName,
185
+ sourceEnv.EnvironmentName,
186
+ sourceEnv.VersionLabel,
187
+ argv.greenBlue,
188
+ );
189
+
190
+ const config = {};
191
+ config[branch] = {
192
+ deploy: {
193
+ url: infastructure.url,
194
+ location: "/var/www/html",
195
+ "aws-eb": infastructure.environment,
196
+ environment: envTagValue,
197
+ paths: infastructure.paths,
198
+ "cloned-from": sourceEnv.EnvironmentName,
199
+ },
200
+ };
201
+
202
+ if (infastructure.copy) {
203
+ config[branch].copy = infastructure.copy;
204
+ }
205
+
206
+ let stringify = JSON.stringify(config, null, 4);
207
+ let output = stringify
208
+ .substring(1, stringify.length - 1)
209
+ .trim();
210
+ utilities.copyToClipboard(output);
211
+ console.log(
212
+ utilities.colorize(
213
+ `\n${output}\n\n(copied to clipboard)`,
214
+ "title",
215
+ ),
216
+ );
217
+ } catch (e) {
218
+ console.log(e.message);
219
+ process.exit(1);
220
+ }
221
+
222
+ return;
223
+ }
224
+
225
+ const environmentChoices = ["production", "staging", "test"];
226
+ const defaultEnvironment =
227
+ environmentChoices.find((e) => branch.toLowerCase().includes(e)) ||
228
+ "test";
229
+
41
230
  let answers = await inquirer.prompt([stack]);
42
231
 
43
232
  // Prompt for redirects if static stack and no existing redirects
@@ -207,12 +396,6 @@ module.exports = [
207
396
  value: "default",
208
397
  }),
209
398
  },
210
- {
211
- type: "list",
212
- name: "availability",
213
- message: "What availability is required?",
214
- choices: ["low", "high"],
215
- },
216
399
  region,
217
400
  {
218
401
  type: "confirm",
@@ -228,19 +411,49 @@ module.exports = [
228
411
  },
229
412
  ])),
230
413
  };
414
+
415
+ if (answers.framework === "laravel") {
416
+ answers = {
417
+ ...answers,
418
+ ...(await inquirer.prompt([
419
+ {
420
+ type: "confirm",
421
+ name: "opensearch",
422
+ message: "Does this site need OpenSearch?",
423
+ default: false,
424
+ },
425
+ {
426
+ type: "confirm",
427
+ name: "cache",
428
+ message: "Does this site need cache (Valkey)?",
429
+ default: false,
430
+ },
431
+ {
432
+ type: "confirm",
433
+ name: "mail",
434
+ message: "Does this site need email (SES)?",
435
+ default: false,
436
+ },
437
+ ])),
438
+ };
439
+ }
231
440
  }
232
441
 
233
442
  answers = {
234
443
  ...answers,
235
- ...(await inquirer.prompt([
236
- client,
237
- {
238
- type: "confirm",
239
- name: "protected",
240
- message: "Should the site be password protected?",
241
- default: true,
242
- },
243
- ])),
444
+ ...(await inquirer.prompt(
445
+ answers.stack === "static"
446
+ ? [
447
+ client,
448
+ {
449
+ type: "confirm",
450
+ name: "protected",
451
+ message: "Should the site be password protected?",
452
+ default: true,
453
+ },
454
+ ]
455
+ : [client],
456
+ )),
244
457
  };
245
458
 
246
459
  let credentials = [];
@@ -296,78 +509,64 @@ module.exports = [
296
509
  // Set AWS client defaults before any AWS operations
297
510
  setAWSClientDefaults(answers.client, answers.region);
298
511
 
299
- // Snapshot restore prompt — only if database requested, before preflight
300
- let snapshot = null;
301
-
302
- if (answers.database) {
303
- const { restore } = await inquirer.prompt([
304
- {
305
- type: "confirm",
306
- name: "restore",
307
- message: "Restore database from an existing snapshot?",
308
- default: false,
309
- },
310
- ]);
311
-
312
- if (restore) {
313
- const snapshots = await aws.rds.listManualSnapshots();
314
-
315
- if (snapshots.length) {
316
- const { selected } = await inquirer.prompt([
317
- {
318
- type: "list",
319
- name: "selected",
320
- message: "Select a snapshot to restore from",
321
- choices: snapshots.map((s) => ({
322
- name: `${s.DBSnapshotIdentifier} (source: ${s.DBInstanceIdentifier}, created: ${new Date(s.SnapshotCreateTime).toLocaleDateString()})`,
323
- value: s.DBSnapshotIdentifier,
324
- })),
325
- },
326
- ]);
327
-
328
- snapshot = selected;
329
- } else {
330
- new utilities.Spinner(
331
- "No manual snapshots found, creating a fresh database",
332
- true,
333
- ).ora.info();
334
- }
335
- }
336
- }
337
-
338
512
  // Pre-flight check for existing resources
339
- let existingSecrets = { app: null, storage: null, database: null };
513
+ let existingSecrets = {
514
+ app: null,
515
+ storage: null,
516
+ database: null,
517
+ opensearch: null,
518
+ cache: null,
519
+ mail: null,
520
+ };
521
+ let existingEnvironment = false;
522
+ let existingResources = {
523
+ storage: false,
524
+ database: false,
525
+ opensearch: false,
526
+ cache: false,
527
+ };
528
+ let snapshot = null;
340
529
 
341
530
  if (answers.stack === "fullstack") {
342
- const { conflicts, existing } = await aws.fullstackPreflight(
531
+ const { existing } = await aws.fullstackPreflight(
343
532
  slug,
344
533
  _.repoSafe,
345
534
  branch,
346
535
  {
347
536
  storage: answers.storage,
348
537
  database: answers.database,
538
+ opensearch: answers.opensearch,
539
+ cache: answers.cache,
349
540
  },
350
541
  );
351
542
 
352
- if (conflicts.length) {
353
- conflicts.forEach((c) =>
354
- console.log(utilities.colorize(`\n ✗ ${c}`, "error")),
355
- );
543
+ if (
544
+ existing.environment &&
545
+ existing.environment.Status !== "Ready"
546
+ ) {
356
547
  console.log(
357
548
  utilities.colorize(
358
- "\nProvisioning aborted due to existing resources.\n",
549
+ `\n ✗ EB environment "${slug}" is currently ${existing.environment.Status}, cannot re-provision\n`,
359
550
  "error",
360
551
  ),
361
552
  );
362
553
  process.exit(1);
363
554
  }
364
555
 
556
+ if (existing.environment) {
557
+ new utilities.Spinner(
558
+ "EB environment already exists, skipping creation",
559
+ true,
560
+ ).ora.info();
561
+ existingEnvironment = existing.environment;
562
+ }
563
+
365
564
  if (existing.storage) {
366
565
  new utilities.Spinner(
367
566
  "S3 bucket already exists, skipping storage creation",
368
567
  true,
369
568
  ).ora.info();
370
- answers.storage = false;
569
+ existingResources.storage = true;
371
570
  }
372
571
 
373
572
  if (existing.database) {
@@ -375,7 +574,60 @@ module.exports = [
375
574
  "RDS instance already exists, skipping database creation",
376
575
  true,
377
576
  ).ora.info();
378
- answers.database = false;
577
+ existingResources.database = true;
578
+ }
579
+
580
+ if (existing.opensearch) {
581
+ new utilities.Spinner(
582
+ "OpenSearch domain already exists, skipping creation",
583
+ true,
584
+ ).ora.info();
585
+ existingResources.opensearch = true;
586
+ }
587
+
588
+ if (existing.cache) {
589
+ new utilities.Spinner(
590
+ "ElastiCache cluster already exists, skipping creation",
591
+ true,
592
+ ).ora.info();
593
+ existingResources.cache = true;
594
+ }
595
+
596
+ // Snapshot restore prompt — only if database is needed and doesn't already exist
597
+ if (answers.database && !existing.database) {
598
+ const { restore } = await inquirer.prompt([
599
+ {
600
+ type: "confirm",
601
+ name: "restore",
602
+ message: "Restore database from an existing snapshot?",
603
+ default: false,
604
+ },
605
+ ]);
606
+
607
+ if (restore) {
608
+ const snapshots = await aws.rds.listManualSnapshots();
609
+
610
+ if (snapshots.length) {
611
+ const { selected } = await inquirer.prompt([
612
+ {
613
+ type: "list",
614
+ name: "selected",
615
+ message: "Select a snapshot to restore from",
616
+ choices: snapshots.map((s) => ({
617
+ name: `${s.DBSnapshotIdentifier} (source: ${s.DBInstanceIdentifier}, created: ${new Date(s.SnapshotCreateTime).toLocaleDateString()})`,
618
+ value: s.DBSnapshotIdentifier,
619
+ })),
620
+ },
621
+ ]);
622
+
623
+ snapshot = selected;
624
+ } else {
625
+ new utilities.Spinner(
626
+ "No manual snapshots found, creating a fresh database",
627
+ true,
628
+ ).ora.info();
629
+ }
630
+ }
379
631
  }
380
632
 
381
633
  existingSecrets = existing.secrets;
@@ -400,6 +652,241 @@ module.exports = [
400
652
  true,
401
653
  ).ora.info();
402
654
  }
655
+
656
+ if (existingSecrets.opensearch) {
657
+ new utilities.Spinner(
658
+ "OpenSearch secret already exists, reusing existing values",
659
+ true,
660
+ ).ora.info();
661
+ }
662
+
663
+ if (existingSecrets.cache) {
664
+ new utilities.Spinner(
665
+ "Cache secret already exists, reusing existing values",
666
+ true,
667
+ ).ora.info();
668
+ }
669
+
670
+ if (existingSecrets.mail) {
671
+ new utilities.Spinner(
672
+ "Mail secret already exists, reusing existing values",
673
+ true,
674
+ ).ora.info();
675
+ }
676
+
677
+ // SES detail prompts — only if mail requested and not already configured
678
+ if (answers.mail && !existingSecrets.mail) {
679
+ const identities = await aws.ses.listVerifiedIdentities();
680
+
681
+ if (!identities.length) {
682
+ new utilities.Spinner(
683
+ "No verified SES identities found, skipping email setup",
684
+ true,
685
+ ).ora.warn();
686
+ answers.mail = false;
687
+ } else {
688
+ const defaultIdentity = answers.appDomain
689
+ ? identities.find(
690
+ (id) =>
691
+ answers.appDomain.endsWith(id) ||
692
+ id === answers.appDomain,
693
+ ) || identities[0]
694
+ : identities[0];
695
+
696
+ const { sesIdentity } = await inquirer.prompt([
697
+ {
698
+ type: "list",
699
+ name: "sesIdentity",
700
+ message:
701
+ "Which verified SES domain should be used?",
702
+ choices: identities,
703
+ default: defaultIdentity,
704
+ },
705
+ ]);
706
+
707
+ const { mailFromAddress } = await inquirer.prompt([
708
+ {
709
+ type: "input",
710
+ name: "mailFromAddress",
711
+ message: "Mail from address:",
712
+ default: `noreply@${sesIdentity}`,
713
+ validate: (input) => {
714
+ if (!input.includes("@")) {
715
+ return "Must be a valid email address";
716
+ }
717
+ const domain = input.split("@")[1];
718
+ if (
719
+ domain === sesIdentity ||
720
+ domain.endsWith(`.${sesIdentity}`)
721
+ ) {
722
+ return true;
723
+ }
724
+ return `Address must be under the verified identity ${sesIdentity}`;
725
+ },
726
+ },
727
+ ]);
728
+
729
+ const { mailFromName } = await inquirer.prompt([
730
+ {
731
+ type: "input",
732
+ name: "mailFromName",
733
+ message: "Mail from name:",
734
+ default:
735
+ answers.appName ||
736
+ _.repoSafe
737
+ .replace(/-/g, " ")
738
+ .replace(/^./, (c) => c.toUpperCase()),
739
+ validate: (input) =>
740
+ !!input.length || "From name is required",
741
+ },
742
+ ]);
743
+
744
+ answers.mailData = {
745
+ fromAddress: mailFromAddress,
746
+ fromName: mailFromName,
747
+ };
748
+ }
749
+ }
750
+
751
+ // Resolve environment tag
752
+ if (existingEnvironment) {
753
+ const envTags = await aws.elasticbeanstalk.getEnvironmentTags(
754
+ existingEnvironment.EnvironmentArn,
755
+ );
756
+ const envTag = envTags.find((t) => t.Key === "environment");
757
+ answers.environment = envTag
758
+ ? envTag.Value
759
+ : defaultEnvironment;
760
+ new utilities.Spinner(
761
+ `Using existing environment tag: ${answers.environment}`,
762
+ true,
763
+ ).ora.info();
764
+ } else {
765
+ answers = {
766
+ ...answers,
767
+ ...(await inquirer.prompt([
768
+ {
769
+ type: "list",
770
+ name: "environment",
771
+ message: "What environment is this?",
772
+ choices: environmentChoices,
773
+ default: defaultEnvironment,
774
+ },
775
+ ])),
776
+ };
777
+ }
778
+
779
+ // EB-specific prompts — only for new environments
780
+ if (!existingEnvironment) {
781
+ answers = {
782
+ ...answers,
783
+ ...(await inquirer.prompt([
784
+ {
785
+ type: "list",
786
+ name: "instanceType",
787
+ message: "What instance size is required?",
788
+ choices: [
789
+ { name: "Small", value: "t3.small" },
790
+ { name: "Medium", value: "t3.medium" },
791
+ { name: "Large", value: "t3.large" },
792
+ { name: "XLarge", value: "t3.xlarge" },
793
+ ],
794
+ default: "t3.small",
795
+ },
796
+ ])),
797
+ };
798
+ }
799
+
800
+ if (!existingEnvironment) {
801
+ answers = {
802
+ ...answers,
803
+ ...(await inquirer.prompt([
804
+ {
805
+ type: "list",
806
+ name: "elbScheme",
807
+ message: "Load balancer visibility?",
808
+ choices: ["public", "internal"],
809
+ },
810
+ ])),
811
+ };
812
+ }
813
+
814
+ // App name and domain prompts — only for new Laravel environments
815
+ if (!existingEnvironment && answers.framework === "laravel") {
816
+ answers = {
817
+ ...answers,
818
+ ...(await inquirer.prompt([
819
+ {
820
+ type: "input",
821
+ name: "appName",
822
+ message: "What is the app name?",
823
+ default: _.repoSafe
824
+ .replace(/-/g, " ")
825
+ .replace(/^./, (c) => c.toUpperCase()),
826
+ validate: (v) =>
827
+ /^[a-zA-Z0-9 ]+$/.test(v) ||
828
+ "Alphanumeric characters and spaces only",
829
+ },
830
+ {
831
+ type: "input",
832
+ name: "appDomain",
833
+ message:
834
+ "What domain will this site be served at? (DNS must be configured manually)",
835
+ filter: (v) =>
836
+ v
837
+ .replace(/^https?:\/\//, "")
838
+ .replace(/\/+$/, ""),
839
+ validate: (v) =>
840
+ v.length > 0 || "Domain is required",
841
+ },
842
+ ])),
843
+ };
844
+
845
+ const matchingCerts = await aws.acm.findCertificatesForDomain(
846
+ answers.appDomain,
847
+ );
848
+
849
+ if (matchingCerts.length) {
850
+ answers = {
851
+ ...answers,
852
+ ...(await inquirer.prompt([
853
+ {
854
+ type: "list",
855
+ name: "certArn",
856
+ message:
857
+ "Which ACM certificate should be used for HTTPS?",
858
+ choices: [
859
+ ...matchingCerts.map((c) => ({
860
+ name: c.DomainName,
861
+ value: c.CertificateArn,
862
+ })),
863
+ { name: "None", value: null },
864
+ ],
865
+ },
866
+ ])),
867
+ };
868
+ } else {
869
+ new utilities.Spinner(
870
+ `No ACM certificates match ${answers.appDomain}, HTTPS listener must be configured manually`,
871
+ true,
872
+ ).ora.info();
873
+ }
874
+ }
875
+ }
876
+
877
+ if (answers.stack === "static") {
878
+ answers = {
879
+ ...answers,
880
+ ...(await inquirer.prompt([
881
+ {
882
+ type: "list",
883
+ name: "environment",
884
+ message: "What environment is this?",
885
+ choices: environmentChoices,
886
+ default: defaultEnvironment,
887
+ },
888
+ ])),
889
+ };
403
890
  }
404
891
 
405
892
  try {
@@ -408,8 +895,9 @@ module.exports = [
408
895
  slug,
409
896
  [
410
897
  { Key: "repository", Value: _.repo },
411
- { Key: "environment", Value: branch },
412
- { Key: "automated", Value: true },
898
+ { Key: "environment", Value: answers.environment },
899
+ { Key: "branch", Value: branch },
900
+ { Key: "automated", Value: "true" },
413
901
  ],
414
902
  credentials,
415
903
  _.repoSafe,
@@ -422,18 +910,28 @@ module.exports = [
422
910
  slug,
423
911
  [
424
912
  { Key: "repository", Value: _.repo },
425
- { Key: "environment", Value: branch },
426
- { Key: "automated", Value: true },
913
+ { Key: "environment", Value: answers.environment },
914
+ { Key: "branch", Value: branch },
915
+ { Key: "automated", Value: "true" },
427
916
  ],
428
- credentials,
429
917
  _.repoSafe,
430
918
  branch,
431
919
  answers.framework,
432
- answers.availability,
433
920
  answers.database,
434
921
  answers.storage,
922
+ answers.opensearch,
923
+ answers.cache,
924
+ answers.mail,
435
925
  existingSecrets,
436
926
  snapshot,
927
+ answers.elbScheme || "public",
928
+ existingEnvironment,
929
+ existingResources,
930
+ answers.appName,
931
+ answers.appDomain,
932
+ answers.certArn,
933
+ answers.instanceType,
934
+ answers.mailData,
437
935
  );
438
936
  }
439
937
  } catch (e) {
@@ -448,16 +946,22 @@ module.exports = [
448
946
  location: infastructure.bucket.slice(1), // Remove / from start
449
947
  "aws-s3": answers.client,
450
948
  "aws-cloudfront": infastructure.cloudfront,
949
+ environment: answers.environment,
451
950
  }
452
951
  : {
453
952
  url: infastructure.url,
454
953
  location: "/var/www/html",
455
954
  "aws-eb": infastructure.environment,
955
+ environment: answers.environment,
456
956
  paths: infastructure.paths,
457
957
  };
458
958
 
459
959
  config[branch] = { deploy };
460
960
 
961
+ if (infastructure.copy) {
962
+ config[branch].copy = infastructure.copy;
963
+ }
964
+
461
965
  if (credentials.length) {
462
966
  config[branch].deploy.users = credentials;
463
967
  }