Skip to content
This repository was archived by the owner on Nov 19, 2022. It is now read-only.

Commit 714154b

Browse files
authored
Merge pull request #1 from brefphp/one-less-step
Simplify via a first sync + uploading new changes
2 parents c804ff5 + 4ee4eb7 commit 714154b

File tree

5 files changed

+94
-48
lines changed

5 files changed

+94
-48
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
vendor/
22
composer.lock
33
node_modules
4+
demo/

README.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ serverless plugin install -n @bref.sh/live
2323
Set up Bref Live's S3 bucket by running:
2424

2525
```
26-
serverless bref-live-install
26+
serverless bref:live:install
2727
```
2828

2929
Run Bref Live at the top of your main file (`index.php` or similar):
@@ -42,9 +42,13 @@ require __DIR__.'/../vendor/autoload.php';
4242
Run Bref Live:
4343

4444
```
45-
serverless bref-live
45+
serverless bref:live
4646
```
4747

48-
Only file changes tracked by git will be synchronized to Lambda.
48+
After a first sync, all file changes will be uploaded live to Lambda.
4949

50-
Anything "git ignored" will not be uploaded to Lambda (for example `vendor/` changes, caches, etc.).
50+
After using Bref Live, reset the environment to a clean state via:
51+
52+
```
53+
serverless deploy
54+
```

live.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212

1313
function live(string $entryPoint)
1414
{
15+
if (!getenv('BREF_LIVE_ENABLE')) return;
16+
1517
$baseRoot = getenv('LAMBDA_TASK_ROOT');
1618
$newRoot = '/tmp/.bref-live';
1719

package.json

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@
44
"homepage": "https://github.com/brefphp/live",
55
"repository": "https://github.com/brefphp/live",
66
"license": "MIT",
7+
"main": "plugin.js",
78
"dependencies": {
8-
"anymatch": "^3.1.2",
99
"archiver": "^5.3.0",
1010
"chalk": "^4.1.1",
11-
"chokidar": "^3.5.2",
12-
"ora": "^5.4.1"
11+
"chokidar": "^3.5.2"
1312
},
14-
"main": "plugin.js"
13+
"peerDependencies": {
14+
"serverless": "^3"
15+
}
1516
}

plugin.js

Lines changed: 78 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,26 @@
11
const child_process = require('child_process');
22
const archiver = require('archiver');
3-
const os = require('os');
43
const chokidar = require('chokidar');
5-
const anymatch = require('anymatch');
6-
const ora = require('ora');
74
const chalk = require('chalk');
85

9-
const ignoredPaths = [
10-
'.git/*',
11-
'.serverless',
12-
'.serverless/*',
13-
'serverless.yml',
14-
];
15-
16-
class ServerlessPlugin {
17-
constructor(serverless) {
6+
class BrefLive {
7+
constructor(serverless, options, utils) {
188
this.serverless = serverless;
9+
this.utils = utils;
1910
this.commands = {
20-
'bref-live': {
11+
'bref:live': {
2112
usage: 'Start Bref Live',
2213
lifecycleEvents: ['start'],
2314
},
24-
'bref-live-install': {
15+
'bref:live:install': {
2516
usage: 'Install Bref Live',
2617
lifecycleEvents: ['install'],
2718
},
2819
};
2920
this.hooks = {
3021
initialize: () => this.init(),
31-
'bref-live:start': () => this.start(),
32-
'bref-live-install:install': () => this.install(),
22+
'bref:live:start': () => this.start(),
23+
'bref:live:install:install': () => this.install(),
3324
};
3425
}
3526

@@ -43,48 +34,81 @@ class ServerlessPlugin {
4334
// TODO make those configurable in `bref.live` in serverless.yml
4435
this.serverless.service.provider.environment.BREF_LIVE_BUCKET = this.bucketName;
4536
this.serverless.service.provider.environment.BREF_LIVE_BUCKET_REGION = 'eu-west-3';
37+
if (process.env.BREF_LIVE_ENABLE) {
38+
this.serverless.service.provider.environment.BREF_LIVE_ENABLE = process.env.BREF_LIVE_ENABLE;
39+
}
40+
41+
// TODO support include/exclude
42+
this.packagePatterns = this.serverless.service.package.patterns ?? [];
4643
}
4744

4845
async install() {
4946
// TODO create the bucket, maybe with a separate CloudFormation stack?
5047
console.log(`WIP - Create a bucket '${this.bucketName}' and make it accessible by Lambda.`);
5148
console.log('Create it in an AWS region close to your location for faster uploads.');
49+
console.log('In the future the CLI should create the bucket for you, sorry for the trouble :)');
5250
}
5351

5452
async start() {
55-
console.log(chalk.gray(`Bref Live will upload changes tracked by git: ${chalk.underline('git diff HEAD --name-only')}`));
56-
this.spinner = ora('Watching changes').start();
53+
this.changedFiles = [];
54+
55+
this.spinner = this.utils.progress.create();
56+
57+
// TODO implement a pattern matching that == the one used by Framework
58+
const pathsToWatch = this.packagePatterns.filter((pattern) => !pattern.startsWith('!'));
59+
if (pathsToWatch.length === 0) {
60+
pathsToWatch.push('*');
61+
}
62+
const pathsToIgnore = this.packagePatterns.filter((pattern) => pattern.startsWith('!'))
63+
.map((pattern) => pattern.replace('!', ''));
5764

58-
this.sync('Initial sync');
59-
chokidar.watch('.', {
65+
await this.initialSync();
66+
67+
this.spinner.update('Watching changes');
68+
chokidar.watch(pathsToWatch, {
6069
ignoreInitial: true,
61-
ignored: ignoredPaths,
70+
ignored: pathsToIgnore,
6271
}).on('all', async (event, path) => {
63-
if (this.isGitIgnored(path)) return;
6472
await this.sync(path);
6573
});
74+
75+
// TODO catch interrupt to cancel BREF_LIVE_ENABLE
76+
return new Promise(resolve => {});
77+
}
78+
79+
async initialSync() {
80+
this.spinner.update('Deploying all functions');
81+
82+
this.serverless.service.provider.environment.BREF_LIVE_ENABLE = '1';
83+
const functionNames = this.serverless.service.getAllFunctions();
84+
await Promise.all(functionNames.map((functionName) => {
85+
return this.spawnAsync('serverless', [
86+
'deploy', 'function', '--function', functionName
87+
], {
88+
BREF_LIVE_ENABLE: '1',
89+
});
90+
}));
6691
}
6792

6893
async sync(path) {
69-
this.spinner.text = 'Uploading';
94+
this.changedFiles.push(path);
95+
96+
this.spinner.update('Uploading');
7097

7198
const startTime = process.hrtime();
7299
const startTimeHuman = new Date().toLocaleTimeString();
73100
const functionNames = this.serverless.service.getAllFunctionsNames();
74101
await Promise.all(functionNames.map((functionName) => this.uploadDiff(functionName)));
75-
this.spinner.succeed(`${chalk.gray(startTimeHuman)} - ${this.elapsedTime(startTime)}s - ${path}`);
76102

77-
// New spinner
78-
this.spinner = ora('Watching project').start();
103+
const elapsedTime = `${this.elapsedTime(startTime)}s`;
104+
this.utils.log.success(`${chalk.gray(startTimeHuman)} ${path} ${chalk.gray(elapsedTime)}`);
105+
106+
this.spinner.update('Watching changes');
79107
}
80108

81109
async uploadDiff(functionName) {
82-
const changedFilesOutput = this.spawnSync('git', ['diff', 'HEAD', '--name-only']);
83-
let changedFiles = changedFilesOutput.split(os.EOL);
84-
changedFiles = changedFiles.filter((file) => file !== '' && !anymatch(ignoredPaths, file));
85-
86110
const archive = archiver('zip', {});
87-
for (const file of changedFiles) {
111+
for (const file of this.changedFiles) {
88112
archive.file(file, {name: file});
89113
}
90114
await archive.finalize();
@@ -98,17 +122,31 @@ class ServerlessPlugin {
98122

99123
elapsedTime(startTime){
100124
const hrtime = process.hrtime(startTime);
101-
return (hrtime[0] + (hrtime[1] / 1e9)).toFixed(3);
125+
return (hrtime[0] + (hrtime[1] / 1e9)).toFixed(1);
102126
}
103127

104-
isGitIgnored(path) {
105-
return child_process.spawnSync('git', ['check-ignore', path]).status === 0;
106-
}
107-
108-
spawnSync(cmd, args) {
109-
const p = child_process.spawnSync(cmd, args);
110-
return p.stdout.toString().trim();
128+
async spawnAsync(command, args, env) {
129+
const child = child_process.spawn(command, args, {
130+
env: {
131+
...process.env,
132+
...env,
133+
},
134+
});
135+
let output = "";
136+
for await (const chunk of child.stdout) {
137+
output += chunk;
138+
}
139+
for await (const chunk of child.stderr) {
140+
output += chunk;
141+
}
142+
const exitCode = await new Promise( (resolve, reject) => {
143+
child.on('close', resolve);
144+
});
145+
if( exitCode) {
146+
throw new Error(`${output}`);
147+
}
148+
return output;
111149
}
112150
}
113151

114-
module.exports = ServerlessPlugin;
152+
module.exports = BrefLive;

0 commit comments

Comments
 (0)