This repository has been archived by the owner on Nov 19, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
plugin.js
156 lines (133 loc) · 5.58 KB
/
plugin.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
const child_process = require('child_process');
const archiver = require('archiver');
const chokidar = require('chokidar');
const chalk = require('chalk');
class BrefLive {
constructor(serverless, options, utils) {
this.serverless = serverless;
this.options = options;
this.utils = utils;
this.commands = {
'bref:live': {
usage: 'Start Bref Live',
lifecycleEvents: ['start'],
},
'bref:live:install': {
usage: 'Install Bref Live',
lifecycleEvents: ['install'],
},
};
this.hooks = {
initialize: () => this.init(),
'bref:live:start': () => this.start(),
'bref:live:install:install': () => this.install(),
};
}
async init() {
this.awsProvider = this.serverless.getProvider('aws');
this.region = this.awsProvider.getRegion();
const accountId = await this.awsProvider.getAccountId();
this.bucketName = `bref-live-${accountId}`;
this.serverless.service.provider.environment = this.serverless.service.provider.environment ?? {};
// TODO make those configurable in `bref.live` in serverless.yml
this.serverless.service.provider.environment.BREF_LIVE_BUCKET = this.bucketName;
this.serverless.service.provider.environment.BREF_LIVE_BUCKET_REGION = 'eu-west-3';
if (process.env.BREF_LIVE_ENABLE) {
this.serverless.service.provider.environment.BREF_LIVE_ENABLE = process.env.BREF_LIVE_ENABLE;
}
// TODO support include/exclude
this.packagePatterns = this.serverless.service.package.patterns ?? [];
}
async install() {
// TODO create the bucket, maybe with a separate CloudFormation stack?
console.log(`WIP - Create a bucket '${this.bucketName}' and make it accessible by Lambda.`);
console.log('Create it in an AWS region close to your location for faster uploads.');
console.log('In the future the CLI should create the bucket for you, sorry for the trouble :)');
}
async start() {
this.changedFiles = [];
this.spinner = this.utils.progress.create();
// TODO implement a pattern matching that == the one used by Framework
const pathsToWatch = this.packagePatterns.filter((pattern) => !pattern.startsWith('!'));
if (pathsToWatch.length === 0) {
pathsToWatch.push('*');
}
const pathsToIgnore = this.packagePatterns.filter((pattern) => pattern.startsWith('!'))
.map((pattern) => pattern.replace('!', ''));
await this.initialSync();
this.spinner.update('Watching changes');
chokidar.watch(pathsToWatch, {
ignoreInitial: true,
ignored: pathsToIgnore,
}).on('all', async (event, path) => {
await this.sync(path);
});
// TODO catch interrupt to cancel BREF_LIVE_ENABLE
return new Promise(resolve => {});
}
async initialSync() {
this.spinner.update('Deploying all functions');
this.serverless.service.provider.environment.BREF_LIVE_ENABLE = '1';
const functionNames = this.serverless.service.getAllFunctions();
await Promise.all(functionNames.map((functionName) => {
const args = ['deploy', 'function', '--function', functionName];
if (this.options.stage) args.push('--stage', this.options.stage);
if (this.options.region) args.push('--region', this.options.region);
if (this.options.awsProfile) args.push('--aws-profile', this.options.awsProfile);
if (this.options.config) args.push('--config', this.options.config);
return this.spawnAsync('serverless', args, {
BREF_LIVE_ENABLE: '1',
});
}));
}
async sync(path) {
this.changedFiles.push(path);
this.spinner.update('Uploading');
const startTime = process.hrtime();
const startTimeHuman = new Date().toLocaleTimeString();
const functionNames = this.serverless.service.getAllFunctionsNames();
await Promise.all(functionNames.map((functionName) => this.uploadDiff(functionName)));
const elapsedTime = `${this.elapsedTime(startTime)}s`;
this.utils.log.success(`${chalk.gray(startTimeHuman)} ${path} ${chalk.gray(elapsedTime)}`);
this.spinner.update('Watching changes');
}
async uploadDiff(functionName) {
const archive = archiver('zip', {});
for (const file of this.changedFiles) {
archive.file(file, {name: file});
}
await archive.finalize();
await this.awsProvider.request("S3", "upload", {
Bucket: this.bucketName,
Key: `${this.region}/${functionName}.zip`,
Body: archive,
});
}
elapsedTime(startTime){
const hrtime = process.hrtime(startTime);
return (hrtime[0] + (hrtime[1] / 1e9)).toFixed(1);
}
async spawnAsync(command, args, env) {
const child = child_process.spawn(command, args, {
env: {
...process.env,
...env,
},
});
let output = "";
for await (const chunk of child.stdout) {
output += chunk;
}
for await (const chunk of child.stderr) {
output += chunk;
}
const exitCode = await new Promise( (resolve, reject) => {
child.on('close', resolve);
});
if( exitCode) {
throw new Error(`${output}`);
}
return output;
}
}
module.exports = BrefLive;