@fishawack/lab-env 5.7.0-beta.2 → 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 (36) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/_Test/provision.js +1 -8
  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 +29 -0
  8. package/commands/create/cmds/key.js +60 -41
  9. package/commands/create/cmds/provision.js +425 -55
  10. package/commands/create/cmds/rekey.js +218 -0
  11. package/commands/create/libs/output-credentials.js +59 -0
  12. package/commands/create/libs/parallel-runner.js +204 -0
  13. package/commands/create/libs/resolve-operator.js +59 -0
  14. package/commands/create/libs/utilities.js +71 -8
  15. package/commands/create/libs/vars.js +70 -86
  16. package/commands/create/services/aws/acm.js +4 -2
  17. package/commands/create/services/aws/cloudfront.js +51 -45
  18. package/commands/create/services/aws/ec2.js +132 -53
  19. package/commands/create/services/aws/elasticache.js +159 -0
  20. package/commands/create/services/aws/elasticbeanstalk.js +141 -60
  21. package/commands/create/services/aws/iam.js +159 -93
  22. package/commands/create/services/aws/index.js +1209 -466
  23. package/commands/create/services/aws/opensearch.js +25 -17
  24. package/commands/create/services/aws/rds.js +22 -34
  25. package/commands/create/services/aws/s3.js +14 -27
  26. package/commands/create/services/aws/secretsmanager.js +8 -15
  27. package/commands/create/services/aws/ses.js +195 -0
  28. package/commands/create/templates/elasticbeanstalk/.ebextensions/laravel/cron.config +45 -0
  29. package/commands/create/templates/elasticbeanstalk/.ebextensions/laravel/queue-worker.config +29 -0
  30. package/commands/create/templates/elasticbeanstalk/.ebextensions/laravel/software.config +12 -0
  31. package/commands/create/templates/elasticbeanstalk/.ebextensions/misc/setvars.config +2 -16
  32. package/commands/create/templates/elasticbeanstalk/.platform/confighooks/postdeploy/rewrite-flush.sh +1 -1
  33. package/commands/create/templates/elasticbeanstalk/.platform/confighooks/postdeploy/setvars.sh +19 -0
  34. package/commands/create/templates/elasticbeanstalk/.platform/hooks/postdeploy/restart_queue.sh +4 -0
  35. package/commands/create/templates/elasticbeanstalk/.platform/hooks/prebuild/setvars.sh +19 -0
  36. package/package.json +5 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,47 @@
1
1
  ## Changelog
2
2
 
3
+ ### 5.7.0-beta.3 (2026-07-01)
4
+
5
+ #### Features
6
+
7
+ * added a rekey that keeps an operator from accidentally removing their own keys ([a5887c8](https://bitbucket.org/fishawackdigital/lab-env/commits/a5887c809c76df27d7a3cd42ab309b4a3ffd852a))
8
+ * added cron and queue setup as default laravel files ([21a74c5](https://bitbucket.org/fishawackdigital/lab-env/commits/21a74c54e492e498985934ce2f5b507b42f79dd7))
9
+ * app now prompts for smtp creds and creates them for operators ([6fb1916](https://bitbucket.org/fishawackdigital/lab-env/commits/6fb1916b2685bfc0248ef5b27da86d7ad0ab9b69))
10
+ * environment prompt and setting in outputted json ([77eac4d](https://bitbucket.org/fishawackdigital/lab-env/commits/77eac4d392441c18c90bb22f9054d69f8e9393b9))
11
+ * export base64 encoded creds for runner users ([9b2d05e](https://bitbucket.org/fishawackdigital/lab-env/commits/9b2d05e7ebc1120c39ae8d1752e99fc23a2bd99f))
12
+ * ignore 400s and health checks on load balancer now baked in rather than configs ([8e56878](https://bitbucket.org/fishawackdigital/lab-env/commits/8e568788804508220093a3a6803d1185fda72a4f))
13
+ * populate session domain when app domain defined ([6df2fef](https://bitbucket.org/fishawackdigital/lab-env/commits/6df2fefe9ab94487864e16b00ce7d4a2ac98230e))
14
+ * prompt for instance size ([be6d133](https://bitbucket.org/fishawackdigital/lab-env/commits/be6d133e792fa1cd5b8c7f16225f3aa7ec0d1ca1))
15
+ * prompt users for eb files that have changed ([0572244](https://bitbucket.org/fishawackdigital/lab-env/commits/057224429aeeb267fd8207cb19f25f152294ba4d))
16
+ * run key commands in parallel with parallel logging ([17cba11](https://bitbucket.org/fishawackdigital/lab-env/commits/17cba11118ddabd9ee49a187923ce28044ce5654))
17
+ * support adding email creds with dedicated iam user ([451672a](https://bitbucket.org/fishawackdigital/lab-env/commits/451672a852b40b9d7ddb5b1ee8daaee5951f71e2))
18
+ * support cache option and parallelize prov and deprov ([7081109](https://bitbucket.org/fishawackdigital/lab-env/commits/7081109f4413d4b78a83d4531c96f28181151746))
19
+ * support creating elasticcache option ([71ea8c3](https://bitbucket.org/fishawackdigital/lab-env/commits/71ea8c3bead34f27d960b29115684bdf0ad4e717))
20
+ * support green blue flag to mirror env ([fdfb4ea](https://bitbucket.org/fishawackdigital/lab-env/commits/fdfb4ea0a68829950f2b7aee7429d0c7b026bfe8))
21
+
22
+ #### Bug Fixes
23
+
24
+ * added missing opensearch secret store into setvars ([d764a2a](https://bitbucket.org/fishawackdigital/lab-env/commits/d764a2abb8e02c31acec5230a0d6a74b5a5ed47f))
25
+ * added tags to resources that were missing them ([8347271](https://bitbucket.org/fishawackdigital/lab-env/commits/8347271cefa467ae70da62d71782509cf44c7f54))
26
+ * app name now defaults to sentence case from app title ([a799af1](https://bitbucket.org/fishawackdigital/lab-env/commits/a799af1bae31fc28ab18747415c7a74f96871507))
27
+ * aws region now part of app secrets ([6446778](https://bitbucket.org/fishawackdigital/lab-env/commits/6446778a9335a52d13b059590c1921f018d6d516))
28
+ * coerce tags to strings and keep checksums of files to detect local changes ([c44ed04](https://bitbucket.org/fishawackdigital/lab-env/commits/c44ed04905d1f3ef9077409d8da71be897dd980b))
29
+ * delete now runs in parallel ([52a9997](https://bitbucket.org/fishawackdigital/lab-env/commits/52a99974b5bd09427e7430d83321be0a1c28ebde))
30
+ * dont copy nginx healthcheck anymore now laravel provides ([eed3730](https://bitbucket.org/fishawackdigital/lab-env/commits/eed37305a93ce3cc5c2b91fbb8b94ce52e99a597))
31
+ * duplicate hooks to avoid permission issues and move setvars to prebuild: ([861e6e5](https://bitbucket.org/fishawackdigital/lab-env/commits/861e6e5579524ec757288781f99a30fe86b9127d))
32
+ * go back to config for healtcheck as needs to be done via resources ([f8c50fa](https://bitbucket.org/fishawackdigital/lab-env/commits/f8c50fa4c5ac110c846aa39af7f5fa785d631b30))
33
+ * iac code now placed in dedicated IaC dir and htpasswd on fullstack deprecated ([d41c17c](https://bitbucket.org/fishawackdigital/lab-env/commits/d41c17c026e525ef9f97a033d783ea1ccf8c1018))
34
+ * instance size now abstracted ([57c5996](https://bitbucket.org/fishawackdigital/lab-env/commits/57c59962b101ebb6d43debc639ae3deb04e5e660))
35
+ * instance size qusetions now comes after healhchecks ([fd22642](https://bitbucket.org/fishawackdigital/lab-env/commits/fd22642b8e23a6e0bb6f7d5f661d9647be98ce2f))
36
+ * moved setvars to hooks so it processes on config updates aswell as deploys ([1b88516](https://bitbucket.org/fishawackdigital/lab-env/commits/1b88516852ba56000bffce833a094437fa5ef865))
37
+ * remove laravel-vue as separate option ([cbfa13c](https://bitbucket.org/fishawackdigital/lab-env/commits/cbfa13c19019627b0aedc18781209f3dcf01c63f))
38
+ * renamed setvars to setvars-secure to be backwards compatible ([c0edccc](https://bitbucket.org/fishawackdigital/lab-env/commits/c0edcccea465f61b0273c5315c3b19d429555af8))
39
+ * wait for cloudfront dns before testing ([6f0c182](https://bitbucket.org/fishawackdigital/lab-env/commits/6f0c1825ebc6725fc1d92711e35b5e92939abb68))
40
+
41
+ #### Build Updates
42
+
43
+ * bumped to latest pipelines ([cff06a5](https://bitbucket.org/fishawackdigital/lab-env/commits/cff06a53321382533a9f30cecedb6afbb2e2832e))
44
+
3
45
  ### 5.7.0-beta.2 (2026-06-11)
4
46
 
5
47
  #### Features
@@ -49,14 +49,7 @@ describe("provision", () => {
49
49
 
50
50
  process.env.REPO = repo;
51
51
 
52
- config = await aws.fullstack(
53
- repo,
54
- [],
55
- [],
56
- repo,
57
- branch,
58
- "wordpress",
59
- );
52
+ config = await aws.fullstack(repo, [], repo, branch, "wordpress");
60
53
  });
61
54
 
62
55
  it("Should provision elastic beanstalk environment", async () => {
package/_Test/prune.js CHANGED
@@ -264,11 +264,14 @@ describe("prune", () => {
264
264
  cfDistributionId,
265
265
  );
266
266
 
267
- // Allow propagation time
268
- await new Promise((r) => setTimeout(r, 5000));
269
-
270
- const distributions = await aws.cloudfront.listDistributions();
271
- const ids = distributions.map((d) => d.Id);
267
+ // Poll until the distribution is no longer in the list (eventual consistency)
268
+ let ids;
269
+ const deadline = Date.now() + 120000;
270
+ do {
271
+ await new Promise((r) => setTimeout(r, 5000));
272
+ const distributions = await aws.cloudfront.listDistributions();
273
+ ids = distributions.map((d) => d.Id);
274
+ } while (ids.includes(cfDistributionId) && Date.now() < deadline);
272
275
 
273
276
  expect(ids).to.not.include(cfDistributionId);
274
277
  });
@@ -46,6 +46,6 @@ pipelines:
46
46
  - docker
47
47
  branches:
48
48
  master:
49
- import: bitbucket-ci:v2.2.0:master
49
+ import: bitbucket-ci:v2.3.7:master
50
50
  beta:
51
- import: bitbucket-ci:v2.2.0:beta
51
+ import: bitbucket-ci:v2.3.7:beta
package/cli.js CHANGED
@@ -170,6 +170,7 @@ const args = hideBin(process.argv);
170
170
  "delete",
171
171
  "key",
172
172
  "dekey",
173
+ "rekey",
173
174
  "prune",
174
175
  "sync-secrets",
175
176
  ].forEach((d) =>
@@ -4,6 +4,7 @@ const aws = require("../services/aws/index.js");
4
4
  const {
5
5
  setAWSClientDefaults,
6
6
  } = require("../../../commands/create/services/aws/misc.js");
7
+ const parallelRunner = require("../libs/parallel-runner.js");
7
8
 
8
9
  module.exports = [
9
10
  "dekey",
@@ -63,16 +64,19 @@ module.exports = [
63
64
  clients = answer.clients;
64
65
  }
65
66
 
66
- for (let i = 0; i < clients.length; i++) {
67
- let client = clients[i];
67
+ const batches = clients.map((client) => ({
68
+ users,
69
+ client,
70
+ fn: async (user) => {
71
+ setAWSClientDefaults(client);
68
72
 
69
- setAWSClientDefaults(client);
70
-
71
- for (let j = 0; j < users.length; j++) {
72
- let user = users[j];
73
+ await aws.iam.removeIAMUser(`fw-automation-${user}`);
74
+ },
75
+ }));
73
76
 
74
- await aws.iam.removeIAMUser(`fw-automation-${user}`, client);
75
- }
76
- }
77
+ await parallelRunner(batches, {
78
+ concurrency: 10,
79
+ verb: "Remove credentials",
80
+ });
77
81
  },
78
82
  ];
@@ -16,6 +16,11 @@ module.exports = [
16
16
  describe: "Branch to configure",
17
17
  type: "string",
18
18
  });
19
+ yargs.option("green-blue", {
20
+ alias: "g",
21
+ describe: "Teardown a cloned environment (pass source branch name)",
22
+ type: "string",
23
+ });
19
24
  },
20
25
  async (argv) => {
21
26
  let branch = argv.branch || _.branch;
@@ -78,6 +83,16 @@ module.exports = [
78
83
  // Set AWS client defaults before any AWS operations
79
84
  setAWSClientDefaults(answers.client, answers.region);
80
85
 
86
+ if (argv.greenBlue) {
87
+ await aws.fullstackCloneTerminate(
88
+ slug,
89
+ _.repoSafe,
90
+ branch,
91
+ argv.greenBlue,
92
+ );
93
+ return;
94
+ }
95
+
81
96
  if (answers.stack === "fullstack") {
82
97
  const resourceAnswers = await inquirer.prompt([
83
98
  {
@@ -98,6 +113,18 @@ module.exports = [
98
113
  message: "Delete the OpenSearch domain?",
99
114
  default: false,
100
115
  },
116
+ {
117
+ type: "confirm",
118
+ name: "deleteCache",
119
+ message: "Delete the ElastiCache cluster?",
120
+ default: false,
121
+ },
122
+ {
123
+ type: "confirm",
124
+ name: "deleteMail",
125
+ message: "Delete the SES mail credentials?",
126
+ default: false,
127
+ },
101
128
  {
102
129
  type: "confirm",
103
130
  name: "deleteAppSecret",
@@ -110,6 +137,8 @@ module.exports = [
110
137
  deleteStorage: resourceAnswers.deleteStorage,
111
138
  deleteDatabase: resourceAnswers.deleteDatabase,
112
139
  deleteOpenSearch: resourceAnswers.deleteOpenSearch,
140
+ deleteCache: resourceAnswers.deleteCache,
141
+ deleteMail: resourceAnswers.deleteMail,
113
142
  deleteAppSecret: resourceAnswers.deleteAppSecret,
114
143
  });
115
144
  } else {
@@ -1,15 +1,13 @@
1
1
  const _ = require("../../../globals.js");
2
- const utilities = require("../libs/utilities");
3
2
  const inquirer = require("inquirer");
4
3
  const aws = require("../services/aws/index.js");
5
- const email = require("../services/email.js");
6
- const htmlEmail = require("fs").readFileSync(
7
- `${__dirname}/../templates/credentials.html`,
8
- { encoding: "utf8" },
9
- );
4
+ const outputCredentials = require("../libs/output-credentials.js");
5
+ const { misc } = require("../libs/vars.js");
10
6
  const {
11
7
  setAWSClientDefaults,
12
8
  } = require("../../../commands/create/services/aws/misc.js");
9
+ const parallelRunner = require("../libs/parallel-runner.js");
10
+ const resolveOperator = require("../libs/resolve-operator.js");
13
11
 
14
12
  module.exports = [
15
13
  "key",
@@ -18,6 +16,8 @@ module.exports = [
18
16
  : false,
19
17
  () => {},
20
18
  async () => {
19
+ let operator = await resolveOperator();
20
+
21
21
  let users = [];
22
22
  let clients = [];
23
23
  let sendEmail = false;
@@ -74,32 +74,73 @@ module.exports = [
74
74
  {
75
75
  type: "confirm",
76
76
  name: "check",
77
- message: `Would you like to email out the credentials after creation (requires office365 credentials in ~/targets/misc.json)?`,
77
+ message: `Would you like to email out the credentials after creation (requires mail credentials in ~/targets/misc.json)?`,
78
78
  default: "Y",
79
79
  },
80
80
  ]);
81
81
 
82
82
  sendEmail = answer.check;
83
83
 
84
- let credentials = {};
85
-
86
- for (let i = 0; i < clients.length; i++) {
87
- let client = clients[i];
84
+ // Check for SES email credentials if user wants to send emails
85
+ if (sendEmail && !misc.nodemailer?.AWSSES?.username) {
86
+ answer = await inquirer.prompt([
87
+ {
88
+ type: "confirm",
89
+ name: "check",
90
+ message:
91
+ "No email credentials found, create them via AWS SES?",
92
+ default: "Y",
93
+ },
94
+ ]);
88
95
 
89
- setAWSClientDefaults(client);
96
+ if (answer.check) {
97
+ try {
98
+ const creds = await aws.ses.ensureOperatorSESCredentials(
99
+ operator.username,
100
+ );
101
+
102
+ if (creds) {
103
+ aws.ses.updateMiscNodemailer(creds);
104
+ console.log(
105
+ "\n ✓ SES email credentials created and saved to misc.json\n",
106
+ );
107
+ } else {
108
+ console.log(
109
+ "\n ⚠ SES IAM user exists but keys were already created. Rotate with `fw rekey` to generate new credentials.\n",
110
+ );
111
+ }
112
+ } catch (err) {
113
+ console.log(
114
+ `\n ⚠ Failed to create SES credentials: ${err.message}\n`,
115
+ );
116
+ }
117
+ }
118
+ }
90
119
 
91
- for (let j = 0; j < users.length; j++) {
92
- let user = users[j];
120
+ let credentials = {};
93
121
 
94
- if (!credentials[user]) {
95
- credentials[user] = {};
96
- }
122
+ const batches = clients.map((client) => ({
123
+ users,
124
+ client,
125
+ fn: async (user) => {
126
+ setAWSClientDefaults(client);
97
127
 
98
- let res = await aws.iam.createFWIAMUser(
128
+ return aws.iam.createFWIAMUser(
99
129
  `fw-automation-${user}`,
100
130
  _.config.users.find((d) => d.username === user).permissions,
101
131
  );
132
+ },
133
+ }));
134
+
135
+ const resultsMap = await parallelRunner(batches, {
136
+ concurrency: 10,
137
+ verb: "Create credentials",
138
+ });
139
+
140
+ for (const [user, entries] of resultsMap) {
141
+ credentials[user] = {};
102
142
 
143
+ for (const { client, res } of entries) {
103
144
  credentials[user][client] = {
104
145
  key:
105
146
  (res.AccessKey && res.AccessKey.AccessKeyId) ||
@@ -111,28 +152,6 @@ module.exports = [
111
152
  }
112
153
  }
113
154
 
114
- let output = "";
115
-
116
- for (var user in credentials) {
117
- output += utilities.colorize(`\n${user}\n`, "title");
118
-
119
- let outputUser = "";
120
- for (var client in credentials[user]) {
121
- outputUser += `\n[${client}]\n`;
122
- outputUser += `aws_access_key_id = ${credentials[user][client].key}\n`;
123
- outputUser += `aws_secret_access_key = ${credentials[user][client].secret}\n`;
124
- }
125
- output += outputUser;
126
-
127
- if (sendEmail) {
128
- await email.send(
129
- _.config.users.find((d) => d.username === user).email,
130
- "New AWS Keys",
131
- `${htmlEmail}<pre><code>${outputUser.replace(/\n/g, "<br>")}</code></pre>`,
132
- );
133
- }
134
- }
135
-
136
- console.log(output);
155
+ await outputCredentials(credentials, sendEmail);
137
156
  },
138
157
  ];