Start a production server from `sourcecred start` (#247)

Summary:
This commit changes `yarn start` to run a production version of the API
server, which serves static files from a pre-built directory and also
handles API requests.

Test Plan:
```shell
$ yarn build
$ yarn backend
$ node ./bin/sourcecred.js start -d /tmp/srccrd &
$ mkdir -p /tmp/srccrd
$ echo hello >/tmp/srccrd/world
$ curl -s localhost:4000/ | file -
/dev/stdin: HTML document, ASCII text, with very long lines, with no line terminators
$ curl -s localhost:4000/api/v1/data/world
hello
```

wchargin-branch: cli-start-prod-server
This commit is contained in:
William Chargin 2018-05-08 22:00:36 -07:00 committed by GitHub
parent ac8d0ff66c
commit cb1339a0a7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 16 additions and 48 deletions

View File

@ -2,8 +2,14 @@
import express from "express"; import express from "express";
export default function apiApp(sourcecredDirectory: string) { export default function apiApp(
sourcecredDirectory: string,
staticFiles?: string
) {
const app = express(); const app = express();
app.use("/api/v1/data", express.static(sourcecredDirectory)); app.use("/api/v1/data", express.static(sourcecredDirectory));
if (staticFiles != null) {
app.use(express.static(staticFiles));
}
return app; return app;
} }

View File

@ -2,11 +2,10 @@
import {Command} from "@oclif/command"; import {Command} from "@oclif/command";
import chalk from "chalk"; import chalk from "chalk";
import child_process from "child_process"; import fs from "fs";
import express from "express";
import {choosePort} from "react-dev-utils/WebpackDevServerUtils"; import {choosePort} from "react-dev-utils/WebpackDevServerUtils";
import tmp from "tmp";
import apiApp from "../../app/apiApp";
import {sourcecredDirectoryFlag} from "../common"; import {sourcecredDirectoryFlag} from "../common";
// Makes the script crash on unhandled rejections instead of silently // Makes the script crash on unhandled rejections instead of silently
@ -35,14 +34,11 @@ export default class StartCommand extends Command {
} }
async function startServer(sourcecredDirectory: string) { async function startServer(sourcecredDirectory: string) {
let server, webpack; let server;
function cleanup() { function cleanup() {
if (server && server.listening) { if (server && server.listening) {
server.close(); server.close();
} }
if (webpack) {
webpack.kill();
}
} }
let shuttingDown = false; let shuttingDown = false;
@ -59,10 +55,13 @@ async function startServer(sourcecredDirectory: string) {
}); });
}); });
const webpackWorkdir = tmp.dirSync({unsafeCleanup: true}).name; const staticFiles = "./build/";
if (!fs.existsSync(staticFiles)) {
console.error("Build output not found. Did you run `yarn build`?");
}
console.log(chalk.bold("Starting Express...")); console.log(chalk.bold("Starting Express..."));
const expressApp = createExpressApp(webpackWorkdir, sourcecredDirectory); const expressApp = apiApp(sourcecredDirectory, staticFiles);
server = await new Promise(async (resolve, _unused_reject) => { server = await new Promise(async (resolve, _unused_reject) => {
const port = await choosePort(HOST, DEFAULT_PORT); const port = await choosePort(HOST, DEFAULT_PORT);
let server = expressApp.listen(port, () => { let server = expressApp.listen(port, () => {
@ -74,44 +73,7 @@ async function startServer(sourcecredDirectory: string) {
cleanup(); cleanup();
}); });
console.log( console.log(
chalk.green(`Server listening on port ${server.address().port}. `) + chalk.green(`Server listening on port ${server.address().port}.`)
`You might want to wait for Webpack to say ${chalk.bold("[built]")}.`
); );
console.log(); console.log();
console.log(chalk.bold("Starting Webpack..."));
webpack = startWebpack(webpackWorkdir);
webpack.on("exit", (code, signal) => {
console.log(
`${chalk.bold("Webpack exited")} with ${code} (signal: ${signal})`
);
cleanup();
});
}
function createExpressApp(webpackWorkdir, sourcecredDirectory) {
const app = express();
app.use(express.static(webpackWorkdir));
app.use("/__data__", express.static(sourcecredDirectory));
return app;
}
function startWebpack(workdir: string) {
const webpack = child_process.spawn(
"yarn",
[
"--silent",
"webpack",
"--config",
"./config/webpack.config.dev.js",
"--output-path",
workdir,
"--watch",
],
{
env: {...process.env, NODE_ENV: "development"},
stdio: "inherit",
}
);
return webpack;
} }