@fishawack/lab-env 4.26.0 → 4.28.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.
Files changed (44) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/_Test/provision.js +39 -16
  3. package/_Test/s3.js +35 -0
  4. package/_Test/utilities.js +35 -1
  5. package/cli.js +0 -2
  6. package/commands/create/cmds/deprovision.js +31 -19
  7. package/commands/create/cmds/provision.js +47 -26
  8. package/commands/create/libs/prompts.js +18 -0
  9. package/commands/create/libs/utilities.js +32 -1
  10. package/commands/create/libs/vars.js +332 -1
  11. package/commands/create/services/aws/cloudfront.js +6 -13
  12. package/commands/create/services/aws/ec2.js +17 -0
  13. package/commands/create/services/aws/elasticbeanstalk.js +96 -0
  14. package/commands/create/services/aws/iam.js +123 -4
  15. package/commands/create/services/aws/index.js +98 -3
  16. package/commands/create/services/aws/misc.js +2 -0
  17. package/commands/create/services/aws/s3.js +48 -5
  18. package/commands/create/services/email.js +20 -23
  19. package/commands/create/templates/elasticbeanstalk/.ebextensions/{apache → httpd}/auto-ssl.config +18 -6
  20. package/commands/create/templates/elasticbeanstalk/.ebextensions/laravel/software.config +1 -12
  21. package/commands/create/templates/elasticbeanstalk/.ebextensions/misc/enable-https-lb.config +9 -0
  22. package/commands/create/templates/elasticbeanstalk/.ebextensions/misc/s3-env.config +41 -0
  23. package/commands/create/templates/elasticbeanstalk/.ebextensions/misc/setvars.config +8 -2
  24. package/commands/create/templates/elasticbeanstalk/.ebextensions/nginx/alb-health.config +28 -0
  25. package/commands/create/templates/elasticbeanstalk/.ebextensions/nginx/auto-ssl.config +19 -7
  26. package/commands/create/templates/elasticbeanstalk/.ebextensions/wordpress/alb-health.config +28 -0
  27. package/commands/create/templates/elasticbeanstalk/.ebextensions/wordpress/cron.config +42 -0
  28. package/commands/create/templates/elasticbeanstalk/.ebextensions/wordpress/environment.config +3 -1
  29. package/commands/create/templates/elasticbeanstalk/.ebextensions/wordpress/post-deploy.config +11 -8
  30. package/commands/create/templates/elasticbeanstalk/.ebextensions/wordpress/software.config +1 -3
  31. package/commands/create/templates/elasticbeanstalk/.elasticbeanstalk/config.yml +7 -0
  32. package/commands/create/templates/elasticbeanstalk/.platform/confighooks/postdeploy/rewrite-flush.sh +4 -0
  33. package/commands/create/templates/elasticbeanstalk/.platform/hooks/postdeploy/rewrite-flush.sh +4 -0
  34. package/commands/create/templates/elasticbeanstalk/.platform/hooks/postdeploy/s3-env.sh +7 -0
  35. package/commands/create/templates/elasticbeanstalk/.platform/hooks/predeploy/deactivate-plugins-single.sh +30 -0
  36. package/commands/create/templates/elasticbeanstalk/.platform/hooks/predeploy/deactivate-plugins.sh +55 -0
  37. package/commands/create/templates/elasticbeanstalk/.platform/httpd/conf.d/elasticbeanstalk/80/forward-https-proto.conf +3 -0
  38. package/commands/create/templates/elasticbeanstalk/.platform/httpd/conf.d/elasticbeanstalk/z-wordpress-htpasswd.conf +13 -0
  39. package/commands/create/templates/elasticbeanstalk/.platform/httpd/conf.d/security_headers-wordpress.conf +11 -0
  40. package/commands/create/templates/elasticbeanstalk/.platform/nginx/conf.d/elasticbeanstalk/health.conf +4 -0
  41. package/commands/create/templates/elasticbeanstalk/.platform/nginx/conf.d/elasticbeanstalk/htpasswd.conf +2 -0
  42. package/globals.js +2 -0
  43. package/package.json +6 -1
  44. /package/commands/create/templates/elasticbeanstalk/.platform/httpd/conf.d/elasticbeanstalk/{443 → 80}/www-to-nonwww-redirection.conf +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  ## Changelog
2
2
 
3
+ ### 4.28.0 (2024-05-17)
4
+ * [Feature] added new clients
5
+ * [Bug] add session driver cookie to laravel high avail option
6
+
7
+ ### 4.27.0 (2024-04-08)
8
+ * [Feature] can now provision fullstack environments
9
+ * [Feature] added new clients
10
+ * [Feature] can override the mail driver
11
+
3
12
  ### 4.26.0 (2024-03-20)
4
13
  * [Feature] can override laravel 8 nginx dockerfile in project root
5
14
 
@@ -1,33 +1,56 @@
1
1
  'use strict';
2
2
 
3
3
  const expect = require('chai').expect;
4
+ const execSync = require('child_process').execSync;
4
5
  const aws = require("../commands/create/services/aws/index.js");
5
6
  var fetch;
6
7
 
7
- describe('provision', async () => {
8
+ describe('provision', () => {
8
9
  let config;
9
10
  let repo = 'lab-env-test-suite';
10
11
  let account = 'fishawack';
11
12
 
12
- before(async () => {
13
- fetch = (await import('node-fetch')).default;
14
-
15
- config = await aws.static(repo, account);
16
-
17
- await aws.s3.addFileToS3Bucket(repo, account, 'index.html', new TextEncoder().encode("test"));
13
+ describe('static', () => {
14
+ before(async () => {
15
+ fetch = (await import('node-fetch')).default;
16
+
17
+ config = await aws.static(repo, account);
18
+
19
+ await aws.s3.addFileToS3Bucket(repo, account, 'index.html', new TextEncoder().encode("test"));
20
+ });
21
+
22
+ it('Should provision s3 bucket', async () => {
23
+ expect((await fetch(config.url)).status).to.be.equal(200);
24
+ });
25
+
26
+ after(async () => {
27
+ await aws.s3.removeFileToS3Bucket(repo, account, 'index.html');
28
+
29
+ await aws.staticTerminate(repo, account, repo, "development", config.cloudfront);
30
+ });
18
31
  });
19
32
 
20
- it('Should provision s3 bucket', async () => {
21
- expect((await fetch(config.url)).status).to.be.equal(200);
22
- });
33
+ describe('fullstack', () => {
34
+ before(async () => {
35
+ fetch = (await import('node-fetch')).default;
36
+
37
+ process.env.REPO = repo;
23
38
 
24
- after(async () => {
25
- await aws.s3.removeFileToS3Bucket(repo, account, 'index.html');
39
+ config = await aws.fullstack(repo, account, [], [], repo, "development", "wordpress");
40
+ });
41
+
42
+ it('Should provision elastic beanstalk environment', async () => {
43
+ expect((await fetch(config.url.replace('https://', 'http://'))).status).to.be.equal(200);
44
+ });
45
+
46
+ after(async () => {
47
+ await aws.fullstackTerminate(repo, account, repo, "development");
26
48
 
27
- await aws.s3.removeS3Bucket(repo, account);
28
-
29
- await aws.cloudfront.removeCloudFrontDistribution(config.cloudfront, account);
49
+ execSync(`rm -rf .elasticbeanstalk`, {encoding: 'utf8', stdio: 'inherit'});
50
+ execSync(`rm -rf .ebextensions`, {encoding: 'utf8', stdio: 'inherit'});
51
+ execSync(`rm -rf .platform`, {encoding: 'utf8', stdio: 'inherit'});
30
52
 
31
- await aws.cloudfront.removeCloudFrontFunction(repo, account);
53
+ delete process.env.REPO;
54
+ });
32
55
  });
33
56
  });
package/_Test/s3.js ADDED
@@ -0,0 +1,35 @@
1
+ 'use strict';
2
+
3
+ const expect = require('chai').expect;
4
+ const s3 = require("../commands/create/services/aws/s3.js");
5
+
6
+ describe('s3', () => {
7
+ let repo = 'lab-env-test-suite';
8
+ let account = 'fishawack';
9
+
10
+ describe('terminate', () => {
11
+ before(async () => {
12
+ await s3.createS3Bucket(repo, account);
13
+
14
+ await s3.addFileToS3Bucket(repo, account, 'index.html', new TextEncoder().encode("test"));
15
+ });
16
+
17
+ it('Should remove all files from bucket during terminate process', async () => {
18
+ let err;
19
+
20
+ await s3.emptyS3Bucket(repo, account);
21
+
22
+ try { await s3.removeS3Bucket(repo, account); } catch (e) { err = e; }
23
+
24
+ expect(err).to.be.undefined;
25
+ });
26
+
27
+ after(async () => {
28
+ try {
29
+ await s3.removeFileToS3Bucket(repo, account, 'index.html');
30
+
31
+ await s3.removeS3Bucket(repo, account);
32
+ } catch (e) {}
33
+ });
34
+ });
35
+ });
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const expect = require('chai').expect;
4
- const { s3Safe } = require("../commands/create/libs/utilities.js");
4
+ const { s3Safe, ebSafe } = require("../commands/create/libs/utilities.js");
5
5
 
6
6
  describe('utilities', async () => {
7
7
  describe('s3Safe', () => {
@@ -36,5 +36,39 @@ describe('utilities', async () => {
36
36
  it('Should remove any special chars', async () => {
37
37
  expect(s3Safe('!@#$%^&*()=_+;,./<>?~`|').match(/[\!\@\#\$\%\^\&\*\(\)\=\_\+\;\,\.\/\<\>\?\~\`\|]/g)).to.be.null;
38
38
  });
39
+ });
40
+
41
+ describe('ebSafe', () => {
42
+ it('Should enforce service name to be at least 4 chars', async () => {
43
+ expect(ebSafe('a').length).to.be.greaterThanOrEqual(4);
44
+ });
45
+
46
+ it('Should limit service name to 40 chars', async () => {
47
+ expect(ebSafe('a'.repeat(100)).length).to.be.lessThanOrEqual(40);
48
+ });
49
+
50
+ it('Should remove any uppercase chars', async () => {
51
+ expect(/^[A-Z]*$/.test(ebSafe('TEST'))).to.be.false;
52
+ });
53
+
54
+ it('Should convert . to -', async () => {
55
+ expect(ebSafe('...').match(/\./g)).to.be.null;
56
+ });
57
+
58
+ it('Should convert _ to -', async () => {
59
+ expect(ebSafe('___').match(/\_/g)).to.be.null;
60
+ });
61
+
62
+ it('Should start with a number or char', async () => {
63
+ expect(/[a-z0-9]/i.test(ebSafe('___').charAt(0))).to.be.true;
64
+ });
65
+
66
+ it('Should end with a number or char', async () => {
67
+ expect(/[a-z0-9]/i.test(ebSafe('___').charAt(ebSafe('___').length - 1))).to.be.true;
68
+ });
69
+
70
+ it('Should remove any special chars', async () => {
71
+ expect(ebSafe('!@#$%^&*()=_+;,./<>?~`|').match(/[\!\@\#\$\%\^\&\*\(\)\=\_\+\;\,\.\/\<\>\?\~\`\|]/g)).to.be.null;
72
+ });
39
73
  })
40
74
  });
package/cli.js CHANGED
@@ -15,8 +15,6 @@ const args = hideBin(process.argv);
15
15
  const updateNotifier = (await import('update-notifier')).default;
16
16
  const pkg = require('./package.json');
17
17
  updateNotifier({pkg, updateCheckInterval: 0}).notify({isGlobal: true});
18
-
19
- process.env.REPO = _.repo;
20
18
 
21
19
  await _.ports.set();
22
20
 
@@ -2,6 +2,7 @@ const _ = require('../../../globals.js');
2
2
  const inquirer = require('inquirer');
3
3
  const aws = require('../services/aws/index.js');
4
4
  const utilities = require('../libs/utilities');
5
+ const { stack, client } = require('../libs/prompts.js');
5
6
 
6
7
  module.exports = [
7
8
  ['deprovision', 'deprov'],
@@ -16,27 +17,42 @@ module.exports = [
16
17
  async argv => {
17
18
  let branch = argv.branch || _.branch;
18
19
 
19
- const answers = await inquirer.prompt([
20
- {
21
- type: 'input',
22
- name: 'id',
23
- message: 'What is the Id of the CloudFront distribution?',
24
- validate: (input) => !!input.length
25
- },
20
+ let answer = await inquirer.prompt([
26
21
  {
27
- type: 'list',
28
- name: 'client',
29
- message: 'Which AWS account is this deployed too?',
30
- choices: aws.clients,
31
- default: 'fishawack'
22
+ type: 'confirm',
23
+ name: 'check',
24
+ message: `Deprovisioning the ${utilities.colorize(branch, 'success')} branch, is this correct?`,
25
+ default: 'Y'
32
26
  }
33
27
  ]);
34
28
 
35
- let answer = await inquirer.prompt([
29
+ if(!answer.check){
30
+ process.exit(1);
31
+ }
32
+
33
+ let answers = await inquirer.prompt([
34
+ stack,
35
+ client
36
+ ]);
37
+
38
+ if(answers.stack === "static"){
39
+ answers = {...answers, ...(await inquirer.prompt([
40
+ {
41
+ type: 'input',
42
+ name: 'id',
43
+ message: 'What is the Id of the CloudFront distribution?',
44
+ validate: (input) => !!input.length
45
+ }
46
+ ]))};
47
+ }
48
+
49
+ const slug = aws.slug(_.repo_safe, answers.client, branch, answers.stack === "static" ? "s3" : "eb");
50
+
51
+ answer = await inquirer.prompt([
36
52
  {
37
53
  type: 'confirm',
38
54
  name: 'check',
39
- message: `Deprovisioning ${utilities.colorize(aws.slug(_.repo_safe, answers.client, branch), 'error')} from ${utilities.colorize(answers.client, 'error')} AWS account, are you sure you want to continue?`,
55
+ message: `Deprovisioning ${utilities.colorize(slug, 'error')} from ${utilities.colorize(answers.client, 'error')} AWS account, are you sure you want to continue?`,
40
56
  default: false
41
57
  }
42
58
  ]);
@@ -45,10 +61,6 @@ module.exports = [
45
61
  process.exit(1);
46
62
  }
47
63
 
48
- try { await aws.s3.removeS3Bucket(aws.slug(_.repo_safe, answers.client, branch), answers.client); } catch(e) {}
49
-
50
- try { await aws.cloudfront.removeCloudFrontDistribution(answers.id, answers.client); } catch(e) {}
51
-
52
- try { await aws.cloudfront.removeCloudFrontFunction(aws.slug(_.repo_safe, answers.client, branch), answers.client); } catch(e) {}
64
+ await aws[`${answers.stack}Terminate`](slug, answers.client, _.repo_safe, branch, answers.id);
53
65
  }
54
66
  ];
@@ -4,6 +4,8 @@ const execSync = require('child_process').execSync;
4
4
  const inquirer = require('inquirer');
5
5
  const aws = require('../services/aws/index.js');
6
6
  const generator = require('generate-password');
7
+ const { stack, client } = require('../libs/prompts.js');
8
+ const { frameworks } = require('../libs/vars');
7
9
 
8
10
  module.exports = [
9
11
  ['provision', 'prov'],
@@ -31,29 +33,42 @@ module.exports = [
31
33
  process.exit(1);
32
34
  }
33
35
 
34
- const answers = await inquirer.prompt([
35
- {
36
- type: 'list',
37
- name: 'stack',
38
- message: 'What type of project are you deploying?',
39
- choices: ['static'],
40
- default: 0
41
- },
42
- {
43
- type: 'list',
44
- name: 'client',
45
- message: 'Which AWS account should this be deployed too?',
46
- choices: aws.clients.sort(),
47
- default: 0,
48
- loop: false
49
- },
36
+ let answers = await inquirer.prompt([
37
+ stack
38
+ ]);
39
+
40
+ if(answers.stack === "fullstack"){
41
+ answers = {...answers, ...(await inquirer.prompt([
42
+ {
43
+ type: 'list',
44
+ name: 'framework',
45
+ message: 'What type of framework are you deploying?',
46
+ choices: frameworks.concat({
47
+ name: "none",
48
+ value: "default"
49
+ })
50
+ },
51
+ {
52
+ type: 'list',
53
+ name: 'availability',
54
+ message: 'What availability is required?',
55
+ choices: [
56
+ 'low',
57
+ 'high'
58
+ ]
59
+ }
60
+ ]))};
61
+ }
62
+
63
+ answers = {...answers, ...(await inquirer.prompt([
64
+ client,
50
65
  {
51
66
  type: 'confirm',
52
67
  name: 'protected',
53
68
  message: 'Should the site be password protected?',
54
69
  default: true
55
70
  }
56
- ]);
71
+ ]))};
57
72
 
58
73
  let credentials = [];
59
74
 
@@ -92,22 +107,28 @@ module.exports = [
92
107
 
93
108
  let config = {};
94
109
  let infastructure;
110
+ const slug = aws.slug(_.repo_safe, answers.client, branch, answers.stack === "static" ? "s3" : "eb");
95
111
 
96
112
  try{
97
- infastructure = await aws.static(aws.slug(_.repo_safe, answers.client, branch), answers.client, [{Key: 'repository', Value: _.repo}, {Key: 'environment', Value: branch}], credentials);
113
+ infastructure = await aws[answers.stack](slug, answers.client, [{Key: 'repository', Value: _.repo}, {Key: 'environment', Value: branch}, {Key: 'automated', Value: true}], credentials, _.repo_safe, branch, answers.framework, answers.availability);
98
114
  } catch(e){
99
115
  console.log(e.message);
100
116
  process.exit(1);
101
117
  }
102
-
103
- config[branch] = {
104
- deploy: {
105
- "url": infastructure.url,
106
- "location": infastructure.bucket.slice(1), // Remove / from start
107
- "aws-s3": answers.client,
108
- "aws-cloudfront": infastructure.cloudfront
109
- }
118
+
119
+ const deploy = answers.stack === "static" ? {
120
+ "url": infastructure.url,
121
+ "location": infastructure.bucket.slice(1), // Remove / from start
122
+ "aws-s3": answers.client,
123
+ "aws-cloudfront": infastructure.cloudfront
124
+ } : {
125
+ "url": infastructure.url,
126
+ "location": "/var/www/html",
127
+ "aws-eb": infastructure.environment,
128
+ "paths": infastructure.paths,
110
129
  };
130
+
131
+ config[branch] = { deploy };
111
132
 
112
133
  if(credentials.length){
113
134
  config[branch].deploy.users = credentials;
@@ -1,6 +1,7 @@
1
1
  const inquirer = require('inquirer');
2
2
 
3
3
  const { templates } = require('./vars');
4
+ const { clients } = require("../services/aws/index.js")
4
5
 
5
6
  const addPrompt = module.exports.addPrompt = (type, prompts, flags) => {
6
7
  switch(type) {
@@ -57,4 +58,21 @@ module.exports.generatePrompts = async flags => {
57
58
  ...flags,
58
59
  ...answers
59
60
  };
61
+ };
62
+
63
+ module.exports.stack = {
64
+ type: 'list',
65
+ name: 'stack',
66
+ message: 'What type of project are you deploying?',
67
+ choices: ['static', 'fullstack'],
68
+ default: 0
69
+ };
70
+
71
+ module.exports.client = {
72
+ type: 'list',
73
+ name: 'client',
74
+ message: 'Which AWS account should this be deployed too?',
75
+ choices: clients.sort(),
76
+ default: 0,
77
+ loop: false
60
78
  };
@@ -100,12 +100,25 @@ module.exports.encode = (username, password) => {
100
100
  }
101
101
 
102
102
  module.exports.s3Safe = (name) => {
103
+ return module.exports.nameSafe(name);
104
+ }
105
+
106
+ module.exports.ebSafe = (name) => {
107
+ return module.exports.nameSafe(name, "eb");
108
+ }
109
+
110
+ module.exports.nameSafe = (name, service = "s3") => {
103
111
  let safe = name;
104
112
  let hash = crypto.createHash('md5').update(name).digest('hex');
105
113
  let suffix = `-${hash.substring(0, 8)}`;
106
114
  let prefix = 'fw-auto-';
115
+ let maxLength = 63; //s3
116
+
117
+ if(service === "eb"){
118
+ maxLength = 40;
119
+ }
107
120
 
108
- safe = safe.substring(0, (63 - suffix.length - prefix.length));
121
+ safe = safe.substring(0, (maxLength - suffix.length - prefix.length));
109
122
  safe = safe.replace(/[^a-zA-Z0-9-_. ]/g, ""); // Remove special chars except . _ - as these are all transformed into -
110
123
  safe = safe.toLowerCase();
111
124
  safe = `${prefix}${safe}`;
@@ -114,4 +127,22 @@ module.exports.s3Safe = (name) => {
114
127
  safe = safe.replace(/\_/g, '-');
115
128
 
116
129
  return safe;
130
+ }
131
+
132
+ module.exports.poll = async (cb, resolve, reject) => {
133
+ let res;
134
+
135
+ do{
136
+ await new Promise((resolve) => setTimeout(() => resolve(), 5000));
137
+
138
+ await module.exports.Spinner.prototype.ping();
139
+
140
+ res = await cb();
141
+
142
+ if(reject && reject(res)){
143
+ throw new Error(`Polling received reject response`);
144
+ }
145
+ } while(resolve(res))
146
+
147
+ return res;
117
148
  }