@fishawack/lab-env 2.1.0 → 2.2.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/_Test/_fixtures/.gitkeep +0 -0
- package/_Test/_helpers/globals.js +10 -0
- package/_Test/provision.js +33 -0
- package/cli.js +6 -6
- package/commands/create/cmds/deprovision.js +53 -0
- package/commands/create/cmds/diagnose.js +5 -3
- package/commands/create/cmds/provision.js +120 -0
- package/commands/create/libs/aws-cloudfront-auth.js +77 -0
- package/commands/create/libs/aws-cloudfront-simple.js +51 -0
- package/commands/create/libs/utilities.js +49 -2
- package/commands/create/services/aws/cloudfront.js +289 -0
- package/commands/create/services/aws/index.js +22 -0
- package/commands/create/services/aws/s3.js +100 -0
- package/commands/create/services/guide.js +13 -0
- package/globals.js +8 -0
- package/package.json +15 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
## Changelog
|
|
2
2
|
|
|
3
|
+
### 2.2.0 (2022-08-10)
|
|
4
|
+
* [Feature] Can now skip diagnose in `fw diagnose`
|
|
5
|
+
* [Feature] Can now provision AWS environments using `fw provision`
|
|
6
|
+
* [Change] Now requires node > 16
|
|
7
|
+
* [Change] Auditted npm dependencies
|
|
8
|
+
* [Misc] Added test coverage for new AWS service
|
|
9
|
+
|
|
3
10
|
### 2.1.0 (2022-05-27)
|
|
4
11
|
* [Change] Bumped core `0.0.21` to `0.1.0`
|
|
5
12
|
|
|
File without changes
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const os = require('os');
|
|
3
|
+
const host = "ftp-fishawack.egnyte.com";
|
|
4
|
+
const creds = JSON.parse(fs.readFileSync(`${os.homedir()}/targets/.ftppass`))[host];
|
|
5
|
+
|
|
6
|
+
module.exports = {
|
|
7
|
+
opts: {encoding: 'utf8', stdio: process.argv.includes('--publish') ? 'pipe' : 'inherit'},
|
|
8
|
+
host,
|
|
9
|
+
creds
|
|
10
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const expect = require('chai').expect;
|
|
4
|
+
const aws = require("../commands/create/services/aws/index.js");
|
|
5
|
+
var fetch;
|
|
6
|
+
|
|
7
|
+
describe('provision', async () => {
|
|
8
|
+
let config;
|
|
9
|
+
let repo = 'lab-env-test-suite';
|
|
10
|
+
let account = 'fishawack';
|
|
11
|
+
|
|
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"));
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('Should provision s3 bucket', async () => {
|
|
21
|
+
expect((await fetch(config.url)).status).to.be.equal(200);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
after(async () => {
|
|
25
|
+
await aws.s3.removeFileToS3Bucket(repo, account, 'index.html');
|
|
26
|
+
|
|
27
|
+
await aws.s3.removeS3Bucket(repo, account);
|
|
28
|
+
|
|
29
|
+
await aws.cloudfront.removeCloudFrontDistribution(config.cloudfront, account);
|
|
30
|
+
|
|
31
|
+
await aws.cloudfront.removeCloudFrontFunction(repo, account);
|
|
32
|
+
});
|
|
33
|
+
});
|
package/cli.js
CHANGED
|
@@ -4,10 +4,6 @@ process.env.CWD = process.cwd();
|
|
|
4
4
|
|
|
5
5
|
const _ = require('./globals.js');
|
|
6
6
|
|
|
7
|
-
const updateNotifier = require('update-notifier');
|
|
8
|
-
const pkg = require('./package.json');
|
|
9
|
-
updateNotifier({pkg, updateCheckInterval: 0}).notify();
|
|
10
|
-
|
|
11
7
|
const execSync = require('child_process').execSync;
|
|
12
8
|
|
|
13
9
|
const yargs = require('yargs/yargs');
|
|
@@ -16,9 +12,13 @@ const { hideBin } = require('yargs/helpers');
|
|
|
16
12
|
const args = hideBin(process.argv);
|
|
17
13
|
|
|
18
14
|
// Stop here if docker process not running and command isn't version or origin which don't require docker
|
|
19
|
-
if(!_.services && !(args[0] === 'origin' || args[0] === '--version'))
|
|
15
|
+
if(!_.services && !(args[0] === 'origin' || args[0] === '--version')) process.exit();
|
|
20
16
|
|
|
21
17
|
(async () => {
|
|
18
|
+
const updateNotifier = (await import('update-notifier')).default;
|
|
19
|
+
const pkg = require('./package.json');
|
|
20
|
+
updateNotifier({pkg, updateCheckInterval: 0}).notify();
|
|
21
|
+
|
|
22
22
|
process.env.REPO = _.repo;
|
|
23
23
|
|
|
24
24
|
await _.ports.set();
|
|
@@ -59,7 +59,7 @@ if(!_.services && !(args[0] === 'origin' || args[0] === '--version')) return;
|
|
|
59
59
|
['build', 'config', 'down', 'mocha', 'rebuild', 'up', 'volumes', 'compose'].forEach(d => cli.command(...require(`./commands/docker/${d}.js`)));
|
|
60
60
|
|
|
61
61
|
// Create commands
|
|
62
|
-
['new', 'diagnose', 'delete'].forEach(d => cli.command(...require(`./commands/create/cmds/${d}.js`)));
|
|
62
|
+
['new', 'provision', 'deprovision', 'diagnose', 'delete'].forEach(d => cli.command(...require(`./commands/create/cmds/${d}.js`)));
|
|
63
63
|
|
|
64
64
|
cli.demandCommand(1, '')
|
|
65
65
|
.wrap(null)
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
const _ = require('../../../globals.js');
|
|
2
|
+
const inquirer = require('inquirer');
|
|
3
|
+
const aws = require('../services/aws/index.js');
|
|
4
|
+
|
|
5
|
+
module.exports = [
|
|
6
|
+
['deprovision', 'deprov'],
|
|
7
|
+
false,
|
|
8
|
+
yargs => {
|
|
9
|
+
yargs.option('branch', {
|
|
10
|
+
alias: 'b',
|
|
11
|
+
describe: 'Branch to configure',
|
|
12
|
+
type: 'string'
|
|
13
|
+
});
|
|
14
|
+
},
|
|
15
|
+
async argv => {
|
|
16
|
+
let branch = argv.branch || _.branch;
|
|
17
|
+
|
|
18
|
+
let answer = await inquirer.prompt([
|
|
19
|
+
{
|
|
20
|
+
type: 'confirm',
|
|
21
|
+
name: 'check',
|
|
22
|
+
message: `Deprovisioning fw-auto-${_.repo}-${branch}, are you sure you want to continue?`,
|
|
23
|
+
default: 'Y'
|
|
24
|
+
}
|
|
25
|
+
]);
|
|
26
|
+
|
|
27
|
+
if(!answer.check){
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const answers = await inquirer.prompt([
|
|
32
|
+
{
|
|
33
|
+
type: 'input',
|
|
34
|
+
name: 'id',
|
|
35
|
+
message: 'What is the Id of the CloudFront distribution?',
|
|
36
|
+
validate: (input) => !!input.length
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
type: 'list',
|
|
40
|
+
name: 'client',
|
|
41
|
+
message: 'Which AWS account is this deployed too?',
|
|
42
|
+
choices: ['fishawack', 'abbvie', 'sanofigenzyme', 'gsk', 'janssen', 'astrazeneca', 'training'],
|
|
43
|
+
default: 'fishawack'
|
|
44
|
+
}
|
|
45
|
+
]);
|
|
46
|
+
|
|
47
|
+
try { await aws.s3.removeS3Bucket(`fw-auto-${_.repo}-${branch}`, answers.client); } catch(e) {}
|
|
48
|
+
|
|
49
|
+
try { await aws.cloudfront.removeCloudFrontDistribution(answers.id, answers.client); } catch(e) {}
|
|
50
|
+
|
|
51
|
+
try { await aws.cloudfront.removeCloudFrontFunction(`fw-auto-${_.repo}-${branch}`, answers.client); } catch(e) {}
|
|
52
|
+
}
|
|
53
|
+
];
|
|
@@ -57,9 +57,11 @@ module.exports = [
|
|
|
57
57
|
while(!await test.bitbucket() || !await bitbucket.check()){
|
|
58
58
|
await guide.bitbucket();
|
|
59
59
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
await
|
|
60
|
+
|
|
61
|
+
if(!await guide.gitlabSkip()) {
|
|
62
|
+
while(!await test.gitlab() || !await gitlab.check()){
|
|
63
|
+
await guide.gitlab();
|
|
64
|
+
}
|
|
63
65
|
}
|
|
64
66
|
|
|
65
67
|
const userRepoName = vars.misc.bitbucket.username.split('@')[0].replace(/\./, '-');
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
const _ = require('../../../globals.js');
|
|
2
|
+
const utilities = require('../libs/utilities');
|
|
3
|
+
const execSync = require('child_process').execSync;
|
|
4
|
+
const inquirer = require('inquirer');
|
|
5
|
+
const aws = require('../services/aws/index.js');
|
|
6
|
+
const generator = require('generate-password');
|
|
7
|
+
|
|
8
|
+
module.exports = [
|
|
9
|
+
['provision', 'prov'],
|
|
10
|
+
'Provisions the deployment target',
|
|
11
|
+
yargs => {
|
|
12
|
+
yargs.option('branch', {
|
|
13
|
+
alias: 'b',
|
|
14
|
+
describe: 'Branch to configure',
|
|
15
|
+
type: 'string'
|
|
16
|
+
});
|
|
17
|
+
},
|
|
18
|
+
async argv => {
|
|
19
|
+
let branch = argv.branch || _.branch;
|
|
20
|
+
|
|
21
|
+
let answer = await inquirer.prompt([
|
|
22
|
+
{
|
|
23
|
+
type: 'confirm',
|
|
24
|
+
name: 'check',
|
|
25
|
+
message: `Provisioning the ${utilities.colorize(branch, 'success')} branch, is this correct?`,
|
|
26
|
+
default: 'Y'
|
|
27
|
+
}
|
|
28
|
+
]);
|
|
29
|
+
|
|
30
|
+
if(!answer.check){
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
|
|
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: 'static'
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
type: 'list',
|
|
44
|
+
name: 'client',
|
|
45
|
+
message: 'Which AWS account should this be deployed too?',
|
|
46
|
+
choices: ['fishawack', 'abbvie', 'sanofigenzyme', 'gsk', 'janssen', 'astrazeneca', 'training'],
|
|
47
|
+
default: 'fishawack'
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
type: 'confirm',
|
|
51
|
+
name: 'protected',
|
|
52
|
+
message: 'Should the site be password protected?',
|
|
53
|
+
default: true
|
|
54
|
+
}
|
|
55
|
+
]);
|
|
56
|
+
|
|
57
|
+
let credentials = [];
|
|
58
|
+
|
|
59
|
+
if(answers.protected){
|
|
60
|
+
let user = {
|
|
61
|
+
another: true
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
while(user.another){
|
|
65
|
+
user = await inquirer.prompt([
|
|
66
|
+
{
|
|
67
|
+
type: 'input',
|
|
68
|
+
name: 'username',
|
|
69
|
+
message: 'Username',
|
|
70
|
+
default: `${_.repo_safe}User${credentials.length ? `-${credentials.length}` : ''}`,
|
|
71
|
+
validate: (input) => !!input.length
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
type: 'input',
|
|
75
|
+
name: 'password',
|
|
76
|
+
message: 'Password (leave empty to generate)',
|
|
77
|
+
default: generator.generate({ length: 10, numbers: true }),
|
|
78
|
+
validate: (input) => !!input.length
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
type: 'confirm',
|
|
82
|
+
name: 'another',
|
|
83
|
+
message: 'Add another user?',
|
|
84
|
+
default: false
|
|
85
|
+
}
|
|
86
|
+
]);
|
|
87
|
+
|
|
88
|
+
credentials.push({username: user.username, password: user.password});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
let config = {};
|
|
93
|
+
let infastructure;
|
|
94
|
+
|
|
95
|
+
try{
|
|
96
|
+
infastructure = await aws.static(`fw-auto-${_.repo}-${branch}`, answers.client, [{Key: 'repository', Value: _.repo}, {Key: 'environment', Value: branch}], credentials);
|
|
97
|
+
} catch(e){
|
|
98
|
+
console.log(e.message);
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
config[branch] = {
|
|
103
|
+
deploy: {
|
|
104
|
+
"url": infastructure.url,
|
|
105
|
+
"location": infastructure.bucket.slice(1), // Remove / from start
|
|
106
|
+
"aws-s3": answers.client,
|
|
107
|
+
"aws-cloudfront": infastructure.cloudfront
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
if(credentials.length){
|
|
112
|
+
config[branch].deploy.users = credentials;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
let stringify = JSON.stringify(config, null, 4);
|
|
116
|
+
let output = stringify.substring(1, stringify.length-1).trim();
|
|
117
|
+
execSync(`printf '${output}' | pbcopy`);
|
|
118
|
+
console.log(utilities.colorize(`\n${output}\n\n(copied to clipboard)`, 'title'));
|
|
119
|
+
}
|
|
120
|
+
];
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
function handler(event) {
|
|
2
|
+
// Redirect if www to non-www
|
|
3
|
+
if (event.request.headers.host.value.includes('www.')) {
|
|
4
|
+
var query = '';
|
|
5
|
+
var index = 0;
|
|
6
|
+
|
|
7
|
+
for(var key in event.request.querystring){
|
|
8
|
+
query += `${index ? '&' : '?'}${key}=${event.request.querystring[key].value}`;
|
|
9
|
+
index++;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
var response = {
|
|
13
|
+
statusCode: 301,
|
|
14
|
+
statusDescription: 'Moved Permanently',
|
|
15
|
+
headers: { "location": { "value": `${event.request.headers.host.value.split('www.')[1]}${event.request.uri}${query}` } }
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
return response;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Redirect if no trailing slash
|
|
22
|
+
if(!event.request.uri.includes('.') && !event.request.uri.endsWith('/')){
|
|
23
|
+
var query = '';
|
|
24
|
+
var index = 0;
|
|
25
|
+
|
|
26
|
+
for(var key in event.request.querystring){
|
|
27
|
+
query += `${index ? '&' : '?'}${key}=${event.request.querystring[key].value}`;
|
|
28
|
+
index++;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
statusCode: 301,
|
|
33
|
+
statusDescription: 'Moved Permanently',
|
|
34
|
+
headers: { "location": { "value": `${event.request.uri}/${query}` } }
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
var authHeaders = event.request.headers.authorization;
|
|
39
|
+
|
|
40
|
+
// The Base64-encoded Auth string that should be present.
|
|
41
|
+
// It is an encoding of `Basic base64([username]:[password])`
|
|
42
|
+
var expected = <%= credentials %>;
|
|
43
|
+
|
|
44
|
+
// If an Authorization header is supplied and it's an exact match, pass the
|
|
45
|
+
// request on through to CF/the origin without any modification.
|
|
46
|
+
if(authHeaders && expected.find(d => d === authHeaders.value)) {
|
|
47
|
+
// Rewrite url to append index.html if not present
|
|
48
|
+
var request = event.request;
|
|
49
|
+
var uri = request.uri;
|
|
50
|
+
|
|
51
|
+
// Check whether the URI is missing a file name.
|
|
52
|
+
if(uri.endsWith('/')) {
|
|
53
|
+
request.uri += 'index.html';
|
|
54
|
+
}
|
|
55
|
+
// Check whether the URI is missing a file extension.
|
|
56
|
+
else if(!uri.includes('.')) {
|
|
57
|
+
request.uri += '/index.html';
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return request;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// But if we get here, we must either be missing the auth header or the
|
|
64
|
+
// credentials failed to match what we expected.
|
|
65
|
+
// Request the browser present the Basic Auth dialog.
|
|
66
|
+
var response = {
|
|
67
|
+
statusCode: 401,
|
|
68
|
+
statusDescription: 'Unauthorized',
|
|
69
|
+
headers: {
|
|
70
|
+
"www-authenticate": {
|
|
71
|
+
value: 'Basic'
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
return response;
|
|
77
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
function handler(event) {
|
|
2
|
+
// Redirect if www to non-www
|
|
3
|
+
if (event.request.headers.host.value.includes('www.')) {
|
|
4
|
+
var query = '';
|
|
5
|
+
var index = 0;
|
|
6
|
+
|
|
7
|
+
for(var key in event.request.querystring){
|
|
8
|
+
query += `${index ? '&' : '?'}${key}=${event.request.querystring[key].value}`;
|
|
9
|
+
index++;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
var response = {
|
|
13
|
+
statusCode: 301,
|
|
14
|
+
statusDescription: 'Moved Permanently',
|
|
15
|
+
headers: { "location": { "value": `${event.request.headers.host.value.split('www.')[1]}${event.request.uri}${query}` } }
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
return response;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Redirect if no trailing slash
|
|
22
|
+
if(!event.request.uri.includes('.') && !event.request.uri.endsWith('/')){
|
|
23
|
+
var query = '';
|
|
24
|
+
var index = 0;
|
|
25
|
+
|
|
26
|
+
for(var key in event.request.querystring){
|
|
27
|
+
query += `${index ? '&' : '?'}${key}=${event.request.querystring[key].value}`;
|
|
28
|
+
index++;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
statusCode: 301,
|
|
33
|
+
statusDescription: 'Moved Permanently',
|
|
34
|
+
headers: { "location": { "value": `${event.request.uri}/${query}` } }
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
var request = event.request;
|
|
39
|
+
var uri = request.uri;
|
|
40
|
+
|
|
41
|
+
// Check whether the URI is missing a file name.
|
|
42
|
+
if(uri.endsWith('/')) {
|
|
43
|
+
request.uri += 'index.html';
|
|
44
|
+
}
|
|
45
|
+
// Check whether the URI is missing a file extension.
|
|
46
|
+
else if(!uri.includes('.')) {
|
|
47
|
+
request.uri += '/index.html';
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return request;
|
|
51
|
+
}
|
|
@@ -23,9 +23,12 @@ const colorize = module.exports.colorize = (str, type) => {
|
|
|
23
23
|
}
|
|
24
24
|
};
|
|
25
25
|
|
|
26
|
+
let latestSpinner = null;
|
|
27
|
+
|
|
26
28
|
module.exports.Spinner = class Spinner {
|
|
27
|
-
constructor(startMessage) {
|
|
28
|
-
this.ora = ora(colorize(startMessage, 'info')).start();
|
|
29
|
+
constructor(startMessage, simple = false) {
|
|
30
|
+
this.ora = ora(simple ? startMessage : colorize(startMessage, 'info')).start();
|
|
31
|
+
latestSpinner = this;
|
|
29
32
|
}
|
|
30
33
|
|
|
31
34
|
update(message, status) {
|
|
@@ -37,6 +40,50 @@ module.exports.Spinner = class Spinner {
|
|
|
37
40
|
this.ora.succeed(colorize(message, status || 'success'));
|
|
38
41
|
}
|
|
39
42
|
}
|
|
43
|
+
|
|
44
|
+
action(message, action, success, failure) {
|
|
45
|
+
return new Promise(async (resolve, reject) => {
|
|
46
|
+
let instance = new this.constructor(message);
|
|
47
|
+
let res;
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
res = await action();
|
|
51
|
+
|
|
52
|
+
instance.update(success);
|
|
53
|
+
} catch (e){
|
|
54
|
+
instance.update(failure, 'fail');
|
|
55
|
+
reject(e);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
resolve(res);
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
simple(message, action) {
|
|
63
|
+
return new Promise(async (resolve, reject) => {
|
|
64
|
+
let instance = new this.constructor(message, true);
|
|
65
|
+
let res;
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
res = await action();
|
|
69
|
+
|
|
70
|
+
instance.ora.succeed();
|
|
71
|
+
} catch (e){
|
|
72
|
+
instance.ora.fail();
|
|
73
|
+
reject(e);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
resolve(res);
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
ping(){
|
|
81
|
+
latestSpinner.ora.color = latestSpinner.ora.color === 'cyan' ? 'magenta' : 'cyan';
|
|
82
|
+
let message = ' - check: ';
|
|
83
|
+
let split = latestSpinner.ora.text.split(message);
|
|
84
|
+
let iteration = split[1] && +split[1].split(':')[0] || 0;
|
|
85
|
+
latestSpinner.ora.text = `${split[0]}${message}${iteration+1}`;
|
|
86
|
+
}
|
|
40
87
|
};
|
|
41
88
|
|
|
42
89
|
module.exports.encode = (username, password) => {
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
const { CloudFrontClient, CreateDistributionWithTagsCommand, CreateCloudFrontOriginAccessIdentityCommand, DeleteDistributionCommand , DeleteCloudFrontOriginAccessIdentityCommand, GetDistributionCommand, UpdateDistributionCommand, GetCloudFrontOriginAccessIdentityCommand, CreateFunctionCommand, GetFunctionCommand, UpdateFunctionCommand, PublishFunctionCommand, DeleteFunctionCommand, DescribeFunctionCommand } = require("@aws-sdk/client-cloudfront");
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const { Spinner } = require('../../libs/utilities');
|
|
4
|
+
|
|
5
|
+
let region = `us-east-1`;
|
|
6
|
+
|
|
7
|
+
const client = new CloudFrontClient({ region });
|
|
8
|
+
|
|
9
|
+
module.exports.createCloudFrontDistribution = async (name, account, tags = []) => {
|
|
10
|
+
process.env.AWS_PROFILE = account;
|
|
11
|
+
|
|
12
|
+
let OAI = await Spinner.prototype.simple(`Creating CloudFront OAI`, () => {
|
|
13
|
+
return client.send(
|
|
14
|
+
new CreateCloudFrontOriginAccessIdentityCommand({
|
|
15
|
+
CloudFrontOriginAccessIdentityConfig: {
|
|
16
|
+
CallerReference: name,
|
|
17
|
+
Comment: `lab-env provisioned CloudFront OAI for s3 bucket ${name}`,
|
|
18
|
+
}
|
|
19
|
+
})
|
|
20
|
+
)
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
let res;
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
res = await Spinner.prototype.simple(`Creating CloudFront distribution with CloudFront OAI and tags`, () => {
|
|
27
|
+
return client.send(
|
|
28
|
+
new CreateDistributionWithTagsCommand({
|
|
29
|
+
DistributionConfigWithTags: {
|
|
30
|
+
DistributionConfig: {
|
|
31
|
+
Enabled: true,
|
|
32
|
+
CallerReference: name,
|
|
33
|
+
Comment: `lab-env provisioned CloudFront distribution for project ${name}`,
|
|
34
|
+
CustomErrorResponses: {
|
|
35
|
+
Items: [
|
|
36
|
+
{
|
|
37
|
+
ErrorCachingMinTTL: 0,
|
|
38
|
+
ErrorCode: 403,
|
|
39
|
+
ResponseCode: 200,
|
|
40
|
+
ResponsePagePath: '/index.html'
|
|
41
|
+
}
|
|
42
|
+
],
|
|
43
|
+
Quantity: 1
|
|
44
|
+
},
|
|
45
|
+
DefaultCacheBehavior: {
|
|
46
|
+
Compress: true,
|
|
47
|
+
TargetOriginId: `${name}.s3.${region}.amazonaws.com`,
|
|
48
|
+
ViewerProtocolPolicy: 'redirect-to-https',
|
|
49
|
+
CachePolicyId: '658327ea-f89d-4fab-a63d-7e88639e58f6' // Built in, Managed AWS Policy - Cache Optimized
|
|
50
|
+
},
|
|
51
|
+
Origins: {
|
|
52
|
+
Items: [
|
|
53
|
+
{
|
|
54
|
+
DomainName: `${name}.s3.${region}.amazonaws.com`,
|
|
55
|
+
Id: `${name}.s3.${region}.amazonaws.com`,
|
|
56
|
+
S3OriginConfig: {
|
|
57
|
+
OriginAccessIdentity: `origin-access-identity/cloudfront/${OAI.CloudFrontOriginAccessIdentity.Id}`
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
],
|
|
61
|
+
Quantity: 1
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
Tags: {
|
|
65
|
+
Items: [{Key: 'client', Value: account}].concat(tags)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
})
|
|
69
|
+
)
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
await Spinner.prototype.simple(`Waiting for CloudFront distribution to deploy`, () => {
|
|
73
|
+
return module.exports.waitForCloudFrontDistribution(res.Distribution.Id, account);
|
|
74
|
+
});
|
|
75
|
+
} catch(e){
|
|
76
|
+
let Id = e.message.split(' ')[e.message.split(' ').length - 1];
|
|
77
|
+
|
|
78
|
+
res = await Spinner.prototype.simple(`Retrieving the already existing CloudFront distribution`, () => {
|
|
79
|
+
return client.send(
|
|
80
|
+
new GetDistributionCommand({ Id })
|
|
81
|
+
);
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return res;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
module.exports.removeCloudFrontDistribution = async (Id, account) => {
|
|
89
|
+
process.env.AWS_PROFILE = account;
|
|
90
|
+
|
|
91
|
+
let res = await Spinner.prototype.simple(`Retrieving the CloudFront distribution ${Id}`, () => {
|
|
92
|
+
return client.send(
|
|
93
|
+
new GetDistributionCommand({ Id })
|
|
94
|
+
);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
let OAI = res.Distribution.DistributionConfig.Origins.Items[0].S3OriginConfig.OriginAccessIdentity.split('origin-access-identity/cloudfront/')[1];
|
|
98
|
+
|
|
99
|
+
res.Distribution.DistributionConfig.Enabled = false;
|
|
100
|
+
|
|
101
|
+
res = await Spinner.prototype.simple(`Disabling the CloudFront distribution`, () => {
|
|
102
|
+
return client.send(
|
|
103
|
+
new UpdateDistributionCommand({ DistributionConfig: res.Distribution.DistributionConfig, Id, IfMatch: res.ETag })
|
|
104
|
+
);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
await Spinner.prototype.simple(`Waiting for CloudFront distribution to deploy`, () => {
|
|
108
|
+
return module.exports.waitForCloudFrontDistribution(res.Distribution.Id, account);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
res = await Spinner.prototype.simple(`Deleting the CloudFront distribution`, () => {
|
|
112
|
+
return client.send(
|
|
113
|
+
new DeleteDistributionCommand({ Id, IfMatch: res.ETag })
|
|
114
|
+
);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
res = await Spinner.prototype.simple(`Retrieving the CloudFront OAI`, () => {
|
|
118
|
+
return client.send(
|
|
119
|
+
new GetCloudFrontOriginAccessIdentityCommand({ Id: OAI })
|
|
120
|
+
);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
res = await Spinner.prototype.simple(`Deleting the CloudFront OAI`, () => {
|
|
124
|
+
return client.send(
|
|
125
|
+
new DeleteCloudFrontOriginAccessIdentityCommand({ Id: OAI, IfMatch: res.ETag })
|
|
126
|
+
);
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
module.exports.waitForCloudFrontDistribution = async (Id, account) => {
|
|
131
|
+
process.env.AWS_PROFILE = account;
|
|
132
|
+
|
|
133
|
+
let status;
|
|
134
|
+
|
|
135
|
+
do{
|
|
136
|
+
await new Promise((resolve) => setTimeout(() => resolve(), 5000));
|
|
137
|
+
|
|
138
|
+
await Spinner.prototype.ping();
|
|
139
|
+
|
|
140
|
+
let check = await client.send(
|
|
141
|
+
new GetDistributionCommand({ Id })
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
status = check.Distribution.Status;
|
|
145
|
+
} while(status !== 'Deployed')
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
module.exports.createCloudFrontFunction = async (name, account, fn, config) => {
|
|
149
|
+
process.env.AWS_PROFILE = account;
|
|
150
|
+
|
|
151
|
+
let FunctionConfig = {
|
|
152
|
+
Comment: `lab-env provisioned cloudfront function for project ${name} using code snippet ${fn}.js`,
|
|
153
|
+
Runtime: `cloudfront-js-1.0`
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
let res;
|
|
157
|
+
|
|
158
|
+
try{
|
|
159
|
+
res = await Spinner.prototype.simple(`Creating CloudFront function`, () => {
|
|
160
|
+
return client.send(
|
|
161
|
+
new CreateFunctionCommand({
|
|
162
|
+
Name: name,
|
|
163
|
+
FunctionCode: new TextEncoder().encode(" "),
|
|
164
|
+
FunctionConfig
|
|
165
|
+
})
|
|
166
|
+
);
|
|
167
|
+
});
|
|
168
|
+
} catch (e){
|
|
169
|
+
res = await Spinner.prototype.simple(`Retrieving the already existing CloudFront function`, () => {
|
|
170
|
+
return client.send(
|
|
171
|
+
new GetFunctionCommand({
|
|
172
|
+
Name: name
|
|
173
|
+
})
|
|
174
|
+
);
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
let processedFn = fs.readFileSync(`${__dirname}/../../libs/${fn}.js`).toString();
|
|
179
|
+
processedFn = processedFn.replace(/<%=.*%>/g, (el) => JSON.stringify(config[el.slice(3, el.length - 2).trim()], null, 4));
|
|
180
|
+
|
|
181
|
+
res = await Spinner.prototype.simple(`Updating CloudFront function with ${fn}.js code`, () => {
|
|
182
|
+
return client.send(
|
|
183
|
+
new UpdateFunctionCommand({
|
|
184
|
+
Name: name,
|
|
185
|
+
FunctionCode: new TextEncoder().encode(processedFn),
|
|
186
|
+
FunctionConfig,
|
|
187
|
+
IfMatch: res.ETag
|
|
188
|
+
})
|
|
189
|
+
);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
res = await Spinner.prototype.simple(`Publishing CloudFront function`, () => {
|
|
193
|
+
return client.send(
|
|
194
|
+
new PublishFunctionCommand({
|
|
195
|
+
Name: name,
|
|
196
|
+
IfMatch: res.ETag
|
|
197
|
+
})
|
|
198
|
+
);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
return res;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
module.exports.removeCloudFrontFunction = async (name, account) => {
|
|
205
|
+
process.env.AWS_PROFILE = account;
|
|
206
|
+
|
|
207
|
+
let res = await Spinner.prototype.simple(`Retrieving CloudFront function`, () => {
|
|
208
|
+
return client.send(
|
|
209
|
+
new GetFunctionCommand({
|
|
210
|
+
Name: name
|
|
211
|
+
})
|
|
212
|
+
);
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
res = await Spinner.prototype.simple(`Deleting CloudFront function`, () => {
|
|
216
|
+
return client.send(
|
|
217
|
+
new DeleteFunctionCommand({
|
|
218
|
+
Name: name,
|
|
219
|
+
IfMatch: res.ETag
|
|
220
|
+
})
|
|
221
|
+
);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
return res;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
module.exports.setCloudFrontFunctionAssociation = async (Id, account) => {
|
|
228
|
+
process.env.AWS_PROFILE = account;
|
|
229
|
+
|
|
230
|
+
let res = await Spinner.prototype.simple(`Retrieving CloudFront distribution`, () => {
|
|
231
|
+
return client.send(
|
|
232
|
+
new GetDistributionCommand({ Id })
|
|
233
|
+
);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
let { FunctionSummary: { FunctionMetadata: { FunctionARN } } } = await Spinner.prototype.simple(`Retrieving CloudFront function`, () => {
|
|
237
|
+
return client.send(
|
|
238
|
+
new DescribeFunctionCommand({
|
|
239
|
+
Name: res.Distribution.DistributionConfig.CallerReference,
|
|
240
|
+
Stage: 'LIVE'
|
|
241
|
+
})
|
|
242
|
+
);
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
res.Distribution.DistributionConfig.DefaultCacheBehavior.FunctionAssociations = {
|
|
246
|
+
Items: [
|
|
247
|
+
{
|
|
248
|
+
EventType: 'viewer-request',
|
|
249
|
+
FunctionARN: FunctionARN
|
|
250
|
+
}
|
|
251
|
+
],
|
|
252
|
+
Quantity: 1
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
res = await Spinner.prototype.simple(`Adding CloudFront function to CloudFront distribution`, () => {
|
|
256
|
+
return client.send(
|
|
257
|
+
new UpdateDistributionCommand({ DistributionConfig: res.Distribution.DistributionConfig, Id, IfMatch: res.ETag })
|
|
258
|
+
);
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
await Spinner.prototype.simple(`Waiting for CloudFront distribution to deploy`, () => {
|
|
262
|
+
return module.exports.waitForCloudFrontDistribution(res.Distribution.Id, account);
|
|
263
|
+
});
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
module.exports.removeCloudFrontFunctionAssociation = async (Id, account) => {
|
|
267
|
+
process.env.AWS_PROFILE = account;
|
|
268
|
+
|
|
269
|
+
let res = await Spinner.prototype.simple(`Retrieving CloudFront distribution`, () => {
|
|
270
|
+
return client.send(
|
|
271
|
+
new GetDistributionCommand({ Id })
|
|
272
|
+
);
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
res.Distribution.DistributionConfig.DefaultCacheBehavior.FunctionAssociations = {
|
|
276
|
+
Items: [],
|
|
277
|
+
Quantity: 0
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
res = await Spinner.prototype.simple(`Removing CloudFront function from CloudFront distribution`, () => {
|
|
281
|
+
return client.send(
|
|
282
|
+
new UpdateDistributionCommand({ DistributionConfig: res.Distribution.DistributionConfig, Id, IfMatch: res.ETag })
|
|
283
|
+
);
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
await Spinner.prototype.simple(`Waiting for CloudFront distribution to deploy`, () => {
|
|
287
|
+
return module.exports.waitForCloudFrontDistribution(res.Distribution.Id, account);
|
|
288
|
+
});
|
|
289
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module.exports.s3 = require("./s3.js");
|
|
2
|
+
module.exports.cloudfront = require("./cloudfront.js");
|
|
3
|
+
|
|
4
|
+
module.exports.static = async (name, account, tags = [], credentials = []) => {
|
|
5
|
+
let s3 = await module.exports.s3.createS3Bucket(name, account, tags);
|
|
6
|
+
|
|
7
|
+
let cloudfront = await module.exports.cloudfront.createCloudFrontDistribution(name, account, tags);
|
|
8
|
+
|
|
9
|
+
await module.exports.s3.setS3BucketPolicy(name, account, cloudfront.Distribution.DistributionConfig.Origins.Items[0].S3OriginConfig.OriginAccessIdentity.split('origin-access-identity/cloudfront/')[1]);
|
|
10
|
+
|
|
11
|
+
await module.exports.cloudfront.createCloudFrontFunction(name, account, credentials.length ? 'aws-cloudfront-auth' : 'aws-cloudfront-simple', {credentials: credentials.map(d => `Basic ${Buffer.from(`${d.username}:${d.password}`).toString('base64')}`)});
|
|
12
|
+
|
|
13
|
+
await module.exports.cloudfront.setCloudFrontFunctionAssociation(cloudfront.Distribution.Id, account);
|
|
14
|
+
|
|
15
|
+
let config = {
|
|
16
|
+
"bucket": s3.Location,
|
|
17
|
+
"url": `https://${cloudfront.Distribution.DomainName}`,
|
|
18
|
+
"cloudfront": cloudfront.Distribution.Id,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
return config;
|
|
22
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
const { S3Client, CreateBucketCommand, DeleteBucketCommand, PutPublicAccessBlockCommand, PutBucketTaggingCommand, PutBucketPolicyCommand, PutObjectCommand, DeleteObjectCommand } = require("@aws-sdk/client-s3");
|
|
2
|
+
const { Spinner } = require('../../libs/utilities');
|
|
3
|
+
|
|
4
|
+
let region = `us-east-1`;
|
|
5
|
+
|
|
6
|
+
const client = new S3Client({ region });
|
|
7
|
+
|
|
8
|
+
module.exports.createS3Bucket = async (bucket, account, tags = []) => {
|
|
9
|
+
process.env.AWS_PROFILE = account;
|
|
10
|
+
|
|
11
|
+
let res = await Spinner.prototype.simple(`Creating s3 bucket ${bucket}`, () => {
|
|
12
|
+
return client.send(
|
|
13
|
+
new CreateBucketCommand({ Bucket: bucket })
|
|
14
|
+
);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
await Spinner.prototype.simple(`Blocking all public access to s3 bucket`, () => {
|
|
18
|
+
return client.send(
|
|
19
|
+
new PutPublicAccessBlockCommand({ Bucket: bucket, PublicAccessBlockConfiguration: { BlockPublicAcls: true, BlockPublicPolicy: true, IgnorePublicAcls: true, RestrictPublicBuckets: true } })
|
|
20
|
+
);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
await Spinner.prototype.simple(`Adding tags to s3 bucket`, () => {
|
|
24
|
+
return client.send(
|
|
25
|
+
new PutBucketTaggingCommand({ Bucket: bucket, Tagging: {TagSet: [{Key: 'client', Value: account}].concat(tags)} })
|
|
26
|
+
);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
return res;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
module.exports.removeS3Bucket = async (bucket, account) => {
|
|
33
|
+
process.env.AWS_PROFILE = account;
|
|
34
|
+
|
|
35
|
+
await Spinner.prototype.simple(`Removing s3 bucket ${bucket}`, () => {
|
|
36
|
+
return client.send(
|
|
37
|
+
new DeleteBucketCommand({ Bucket: bucket })
|
|
38
|
+
);
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
module.exports.setS3BucketPolicy = async (bucket, account, OAI) => {
|
|
43
|
+
process.env.AWS_PROFILE = account;
|
|
44
|
+
|
|
45
|
+
let res = await Spinner.prototype.simple(`Updating s3 bucket policy`, () => {
|
|
46
|
+
return client.send(
|
|
47
|
+
new PutBucketPolicyCommand({
|
|
48
|
+
Bucket: bucket,
|
|
49
|
+
Policy: JSON.stringify({
|
|
50
|
+
"Version": "2008-10-17",
|
|
51
|
+
"Id": "PolicyForCloudFrontPrivateContent",
|
|
52
|
+
"Statement": [
|
|
53
|
+
{
|
|
54
|
+
"Sid": "1",
|
|
55
|
+
"Effect": "Allow",
|
|
56
|
+
"Principal": {
|
|
57
|
+
"AWS": `arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity ${OAI}`
|
|
58
|
+
},
|
|
59
|
+
"Action": "s3:GetObject",
|
|
60
|
+
"Resource": `arn:aws:s3:::${bucket}/*`
|
|
61
|
+
}
|
|
62
|
+
]
|
|
63
|
+
})
|
|
64
|
+
})
|
|
65
|
+
);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
return res;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
module.exports.addFileToS3Bucket = async (bucket, account, filepath, file) => {
|
|
72
|
+
process.env.AWS_PROFILE = account;
|
|
73
|
+
|
|
74
|
+
let res = await Spinner.prototype.simple(`Adding file to s3 bucket`, () => {
|
|
75
|
+
return client.send(
|
|
76
|
+
new PutObjectCommand({
|
|
77
|
+
Bucket: bucket,
|
|
78
|
+
Body: file,
|
|
79
|
+
Key: filepath
|
|
80
|
+
})
|
|
81
|
+
);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
return res;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
module.exports.removeFileToS3Bucket = async (bucket, account, filepath) => {
|
|
88
|
+
process.env.AWS_PROFILE = account;
|
|
89
|
+
|
|
90
|
+
let res = await Spinner.prototype.simple(`Removing file from s3 bucket`, () => {
|
|
91
|
+
return client.send(
|
|
92
|
+
new DeleteObjectCommand({
|
|
93
|
+
Bucket: bucket,
|
|
94
|
+
Key: filepath
|
|
95
|
+
})
|
|
96
|
+
);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
return res;
|
|
100
|
+
}
|
|
@@ -194,6 +194,19 @@ module.exports.preset = async () => {
|
|
|
194
194
|
return inputs.preset;
|
|
195
195
|
}
|
|
196
196
|
|
|
197
|
+
module.exports.gitlabSkip = async () => {
|
|
198
|
+
let inputs = await inquirer.prompt([
|
|
199
|
+
{
|
|
200
|
+
type: 'confirm',
|
|
201
|
+
name: 'confirm',
|
|
202
|
+
message: 'Do you want to test gitlab setup? (VPN required)',
|
|
203
|
+
default: true
|
|
204
|
+
}
|
|
205
|
+
]);
|
|
206
|
+
|
|
207
|
+
return !inputs.confirm;
|
|
208
|
+
}
|
|
209
|
+
|
|
197
210
|
module.exports.config = async () => {
|
|
198
211
|
let inputs = await inquirer.prompt([
|
|
199
212
|
{
|
package/globals.js
CHANGED
|
@@ -20,8 +20,15 @@ var run;
|
|
|
20
20
|
var exec;
|
|
21
21
|
var running;
|
|
22
22
|
var services;
|
|
23
|
+
var branch;
|
|
23
24
|
var opts = {encoding: 'utf8', stdio: 'inherit', shell: '/bin/bash'};
|
|
24
25
|
|
|
26
|
+
try{
|
|
27
|
+
branch = process.env.BRANCH || process.env.CI_COMMIT_REF_NAME || require('git-branch').sync();
|
|
28
|
+
} catch(e){
|
|
29
|
+
branch = 'unknown';
|
|
30
|
+
}
|
|
31
|
+
|
|
25
32
|
try{
|
|
26
33
|
repo = execSync('basename "$(git rev-parse --show-toplevel)"', {encoding: 'utf8', stdio: 'pipe'}).trim() || path.basename(process.cwd());
|
|
27
34
|
} catch(e){
|
|
@@ -113,6 +120,7 @@ module.exports = {
|
|
|
113
120
|
services,
|
|
114
121
|
platform,
|
|
115
122
|
repo,
|
|
123
|
+
branch,
|
|
116
124
|
repo_safe: repo.replace(/\./g, ''),
|
|
117
125
|
pkg,
|
|
118
126
|
docker,
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fishawack/lab-env",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "Docker manager for FW",
|
|
5
5
|
"main": "cli.js",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"test": "rm -rf _Test/_fixtures
|
|
7
|
+
"test": "rm -rf _Test/_fixtures/boilerplate*; mocha _Test/*.js --timeout 1200s --bail",
|
|
8
8
|
"preversion": "npm test",
|
|
9
9
|
"postversion": "git push && git push --tags && npm publish",
|
|
10
10
|
"postpublish": "git checkout development && git merge master && git push"
|
|
@@ -16,23 +16,33 @@
|
|
|
16
16
|
"author": "",
|
|
17
17
|
"license": "ISC",
|
|
18
18
|
"homepage": "https://bitbucket.org/fishawackdigital/lab-env#readme",
|
|
19
|
+
"type": "commonjs",
|
|
19
20
|
"bin": {
|
|
20
21
|
"lab-env": "./cli.js",
|
|
21
22
|
"fw": "./cli.js"
|
|
22
23
|
},
|
|
23
24
|
"dependencies": {
|
|
24
|
-
"
|
|
25
|
+
"@aws-sdk/client-cloudfront": "^3.141.0",
|
|
26
|
+
"@aws-sdk/client-s3": "^3.141.0",
|
|
27
|
+
"axios": "^0.21.4",
|
|
25
28
|
"chalk": "4.1.0",
|
|
29
|
+
"generate-password": "^1.7.0",
|
|
26
30
|
"get-port": "5.1.1",
|
|
31
|
+
"git-branch": "^2.0.1",
|
|
27
32
|
"glob": "7.1.7",
|
|
28
33
|
"inquirer": "8.1.2",
|
|
29
34
|
"ora": "5.4.1",
|
|
30
35
|
"semver": "7.3.4",
|
|
31
|
-
"update-notifier": "
|
|
36
|
+
"update-notifier": "^6.0.2",
|
|
32
37
|
"yargs": "16.2.0"
|
|
33
38
|
},
|
|
34
39
|
"devDependencies": {
|
|
35
40
|
"chai": "4.3.4",
|
|
36
|
-
"mocha": "9.
|
|
41
|
+
"mocha": "^9.2.2",
|
|
42
|
+
"node-fetch": "^3.2.10"
|
|
43
|
+
},
|
|
44
|
+
"engines": {
|
|
45
|
+
"npm": ">=8",
|
|
46
|
+
"node": ">=18"
|
|
37
47
|
}
|
|
38
48
|
}
|