Skip to content

Commit

Permalink
added watch env var (#91) (#92)
Browse files Browse the repository at this point in the history
* added watch functionality

* added watch env var

* changed test/start.js

* updated help text

* added watch test

* updated help/start.text

* updated README.md

*  gracefully shutdown

* [improve] gracefully shutdown#2

* handle uncaughtException
* child process exit in fastify.close(), don't need wait FULLY_CLOSED message
* remove setTimeout when trigger fastify ready event
* remove setTimeout in watch functionality test
* unref GRACEFUL_SHUT event timer and use process.exit(1) instead of process.send({ type: FULLY_CLOSED, err: null })
* use pass callback to fastify.close() instead of fastify.addHook('onClose', function () {...})
* change process.send({ type: FULLY_CLOSED, err: null }) to process.exit(0) in fastify.close()

* [improve] gracefully shutdown#3

* call unref() the child process.exit(1)'s timeout before calling fastify.close()
* skip watch functionality test on travis
* add lib/watch/constant.js file
  • Loading branch information
gaogao1030 authored and mcollina committed Aug 11, 2018
1 parent cfbd180 commit 02d7d1e
Show file tree
Hide file tree
Showing 10 changed files with 254 additions and 93 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,4 @@ test.sock

# test artifacts
test/workdir
test/fixtures/*.js
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ You can pass the following options via cli arguments, every options has the corr
| Socket to listen on | `-s` | `--socket` | `FASTIFY_SOCKET` |
| Log level (default to fatal) | `-l` | `--log-level` | `FASTIFY_LOG_LEVEL` |
| Prints pretty logs | `-P` | `--pretty-logs` | `FASTIFY_PRETTY_LOGS` |
| Watch process.cwd() directory for changes, recursively; when that happens, the process will auto reload. | `-w` | `--watch` | `FASTIFY_WATCH` |
| Use custom options | `-o` | `--options` | `FASTIFY_OPTIONS` |
| Set the prefix | `-r` | `--prefix` | `FASTIFY_PREFIX` |
| Defines the maximum payload, in bytes,<br>the server is allowed to accept | | `--body-limit` | `FASTIFY_BODY_LIMIT` |
Expand Down
4 changes: 4 additions & 0 deletions help/start.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ Usage: fastify start [opts] <file>
[env: FASTIFY_OPTIONS]
Use custom options

-w, --watch
[env: FASTIFY_WATCH]
Watch process.cwd() directory for changes, recursively; when that happens, the process will auto reload.

-r, --prefix
[env: FASTIFY_PREFIX]
Set the prefix
Expand Down
5 changes: 5 additions & 0 deletions lib/watch/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = {
GRACEFUL_SHUT: 'GRACEFUL SHUTDOWN',
READY: 'ready',
TIMEOUT: 5000
}
41 changes: 41 additions & 0 deletions lib/watch/fork.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
const chalk = require('chalk')
const { stop, runFastify } = require('../../start')

const {
GRACEFUL_SHUT,
READY,
TIMEOUT
} = require('./constants.js')

const fastify = runFastify(process.argv.splice(2))
const type = process.env.childEvent

process.send({ type: type, err: null })

fastify.ready(err => {
process.send({ type: READY, err: err })
if (err) { stop(err) }
})

process.on('message', function (event) {
if (event === GRACEFUL_SHUT) {
const message = chalk.red('[fastify-cli] process forced end')
setTimeout(exit.bind({ message }), TIMEOUT).unref()
fastify.close(() => {
process.exit(0)
})
}
})

process.on('uncaughtException', (err) => {
console.log(err)
const message = chalk.red('[fastify-cli] app crashed - waiting for file changes before starting...')
// fastify.close(exit) // It's looks that only happend fastify normally startup. so we should detect fastify is normally runing, it's normally running that graceful exit happen, other case I think it's better immediately exec process.exit(1)
// setTimeout(exit.bind({ message }), TIMEOUT).unref()
exit.bind({ message })()
})

function exit () {
if (this) { console.log(this.message) }
process.exit(1)
}
93 changes: 93 additions & 0 deletions lib/watch/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
const path = require('path')
const cp = require('child_process')

const {
GRACEFUL_SHUT
} = require('./constants.js')

const EventEmitter = require('events')
const chokidar = require('chokidar')
const forkPath = path.join(__dirname, './fork.js')

const emitter = new EventEmitter()

let allStop = false
let childs = []

const stop = (watcher = null, err = null) => {
if (childs.length > 0) {
childs.forEach(function (child) {
child.kill()
})
}
childs = []
if (err) { console.log(err) }
if (watcher) {
allStop = true
watcher.close()
}
}

const watch = function (args) {
process.on('uncaughtException', () => {
stop()
childs.push(run('restart'))
})

const run = (event) => {
const childEvent = { childEvent: event }
const env = Object.assign({}, process.env, childEvent)
const _child = cp.fork(forkPath, args, {
env: env,
cwd: process.cwd(),
encoding: 'utf8'
})

_child.on('exit', function (code, signal) {
if (!code === 0) { stop() }
if (childs.length === 0 && !allStop) { childs.push(run('restart')) }
return null
})

_child.on('message', (event) => {
const { type, err } = event
if (err) {
emitter.emit('error', err)
return null
}

emitter.emit(type, err)
})

return _child
}

childs.push(run('start'))

const watcher = chokidar.watch(process.cwd(), { ignored: /(node_modules|\.git|bower_components|build|dist)/ })
watcher.on('ready', function () {
watcher.on('all', function () {
try {
const child = childs.shift()
child.send(GRACEFUL_SHUT)
} catch (err) {
if (!childs.length === 0) {
console.log(err)
stop(watcher, err)
}
}
})
})

emitter.on('error', (err) => {
stop(watcher, err)
})

emitter.on('close', () => {
stop(watcher)
})

return emitter
}

module.exports = watch
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@
"pino-colada": "^1.4.4",
"pump": "^3.0.0",
"resolve-from": "^4.0.0",
"update-notifier": "^2.4.0"
"update-notifier": "^2.4.0",
"chokidar": "^2.0.4"
},
"devDependencies": {
"fastify-autoload": "^0.4.0",
Expand Down
50 changes: 32 additions & 18 deletions start.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ const resolveFrom = require('resolve-from')
const fp = require('fastify-plugin')
const isDocker = require('is-docker')
const listenAddressDocker = '0.0.0.0'
const watch = require('./lib/watch')

let Fastify = null
let fastifyPackageJSON = null

Expand All @@ -33,7 +35,8 @@ function showHelp () {
return module.exports.stop()
}

function start (opts) {
function start (args) {
let opts = minimistArgs(args)
if (opts.help) {
return showHelp()
}
Expand All @@ -58,7 +61,11 @@ function start (opts) {
defer: false
})

return runFastify(opts)
if (opts['watch']) {
return watch(args)
}

return runFastify(args)
}

function stop (error) {
Expand All @@ -69,7 +76,27 @@ function stop (error) {
process.exit()
}

function runFastify (opts) {
function minimistArgs (args) {
return minimist(args, {
integer: ['port', 'body-limit'],
boolean: ['pretty-logs', 'options', 'watch'],
string: ['log-level', 'address'],
alias: {
port: 'p',
socket: 's',
help: 'h',
options: 'o',
address: 'a',
watch: 'w',
prefix: 'r',
'log-level': 'l',
'pretty-logs': 'P'
}
})
}

function runFastify (args) {
let opts = minimistArgs(args)
opts = Object.assign(readEnv(), opts)
opts.port = opts.port || 3000
opts['log-level'] = opts['log-level'] || 'fatal'
Expand Down Expand Up @@ -132,21 +159,7 @@ function runFastify (opts) {
}

function cli (args) {
start(minimist(args, {
integer: ['port', 'body-limit'],
boolean: ['pretty-logs', 'options'],
string: ['log-level', 'address'],
alias: {
port: 'p',
socket: 's',
help: 'h',
options: 'o',
address: 'a',
prefix: 'r',
'log-level': 'l',
'pretty-logs': 'P'
}
}))
start(args)
}

function readEnv () {
Expand All @@ -159,6 +172,7 @@ function readEnv () {
if (env.FASTIFY_SOCKET) opts.socket = env.FASTIFY_SOCKET
if (env.FASTIFY_OPTIONS) opts.options = env.FASTIFY_OPTIONS
if (env.FASTIFY_ADDRESS) opts.address = env.FASTIFY_ADDRESS
if (env.FASTIFY_WATCH) opts['watch'] = env.FASTIFY_WATCH
if (env.FASTIFY_PREFIX) opts.prefix = env.FASTIFY_PREFIX
if (env.FASTIFY_LOG_LEVEL) opts['log-level'] = env.FASTIFY_LOG_LEVEL
if (env.FASTIFY_PRETTY_LOGS) opts['pretty-logs'] = env.FASTIFY_PRETTY_LOGS
Expand Down
Empty file added test/fixtures/.keep
Empty file.
Loading

0 comments on commit 02d7d1e

Please sign in to comment.