commit a2404103c2dc2144821c28e24cc2d7be51a79a9d
parent e73083eb1d50a4035bf5d11803a822fbdd41289b
Author: Vladimir Kutepov <frenzzy.man@gmail.com>
Date: Mon, 27 Feb 2017 13:32:03 +0300
Improve deploy script (#1150)
Diffstat:
8 files changed, 78 insertions(+), 77 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -6,5 +6,3 @@ database.sqlite
node_modules
ncp-debug.log
npm-debug.log
-stats.json
-report.html
diff --git a/public/humans.txt b/public/humans.txt
@@ -3,13 +3,13 @@
# TEAM
- <name> -- <role> -- <twitter>
+ <name> -- <role> -- <twitter>
# THANKS
- <name>
+ <name>
# TECHNOLOGY COLOPHON
- CSS3, HTML5, JavaScript
- React, Flux, SuperAgent
+ CSS3, HTML5, JavaScript
+ React Starter Kit -- https://reactstarter.com/
diff --git a/tools/README.md b/tools/README.md
@@ -1,6 +1,6 @@
-## Build Automation Tools
+# Build Automation Tools
-##### `yarn start` (`start.js`)
+### `yarn start` (`start.js`)
* Cleans up the output `/build` directory (`clean.js`)
* Copies static files to the output folder (`copy.js`)
@@ -10,23 +10,24 @@
[Hot Module Replacement](https://webpack.github.io/docs/hot-module-replacement), and
[React Hot Loader](https://github.com/gaearon/react-hot-loader)
-##### `yarn run build` (`build.js`)
+### `yarn run build` (`build.js`)
* Cleans up the output `/build` folder (`clean.js`)
* Copies static files to the output folder (`copy.js`)
* Creates application bundles with Webpack (`bundle.js`, `webpack.config.js`)
-##### `yarn run deploy` (`deploy.js`)
+### `yarn run deploy` (`deploy.js`)
* Builds the project from source files (`build.js`)
* Pushes the contents of the `/build` folder to a remote server with Git
-##### Options
+## Options
Flag | Description
----------- | --------------------------------------------------
`--release` | Minimizes and optimizes the compiled output
`--verbose` | Prints detailed information to the console
+`--analyze` | Launches [Webpack Bundle Analyzer](https://github.com/th0r/webpack-bundle-analyzer)
`--static` | Renders [specified routes](./render.js#L15) as static html files
`--docker` | Build an image from a Dockerfile
@@ -42,7 +43,7 @@ or
$ yarn start -- --release # Launch dev server in production mode
```
-#### Misc
+## Misc
* `webpack.config.js` - Webpack configuration for both client-side and server-side bundles
* `postcss.config.js` - PostCSS configuration for transforming styles with JS plugins
diff --git a/tools/clean.js b/tools/clean.js
@@ -17,13 +17,7 @@ function clean() {
cleanDir('build/*', {
nosort: true,
dot: true,
- ignore: ['build/.git', 'build/public'],
- }),
-
- cleanDir('build/public/*', {
- nosort: true,
- dot: true,
- ignore: ['build/public/.git'],
+ ignore: ['build/.git'],
}),
]);
}
diff --git a/tools/deploy.js b/tools/deploy.js
@@ -10,7 +10,7 @@
import path from 'path';
import fetch from 'node-fetch';
import { spawn } from './lib/cp';
-import { makeDir } from './lib/fs';
+import { makeDir, moveDir, cleanDir } from './lib/fs';
import run from './run';
// GitHub Pages
@@ -22,6 +22,14 @@ const remote = {
static: true,
};
+// Heroku
+// const remote = {
+// name: 'heroku',
+// url: 'https://git.heroku.com/<app>.git',
+// branch: 'master',
+// website: 'https://<app>.herokuapp.com',
+// };
+
// Azure Web Apps
// const remote = {
// name: 'azure',
@@ -31,7 +39,7 @@ const remote = {
// };
const options = {
- cwd: path.resolve(__dirname, '../build', remote.static ? 'public' : ''),
+ cwd: path.resolve(__dirname, '../build'),
stdio: ['ignore', 'inherit', 'inherit'],
};
@@ -40,7 +48,7 @@ const options = {
*/
async function deploy() {
// Initialize a new repository
- await makeDir('build/public');
+ await makeDir('build');
await spawn('git', ['init', '--quiet'], options);
// Changing a remote's URL
@@ -56,10 +64,10 @@ async function deploy() {
// Fetch the remote repository if it exists
let isRefExists = false;
try {
- await spawn('git', ['ls-remote', '--exit-code', remote.url, remote.branch], options);
+ await spawn('git', ['ls-remote', '--quiet', '--exit-code', remote.url, remote.branch], options);
isRefExists = true;
} catch (error) {
- /* skip */
+ await spawn('git', ['update-ref', '-d', 'HEAD'], options);
}
if (isRefExists) {
await spawn('git', ['fetch', remote.name], options);
@@ -72,11 +80,23 @@ async function deploy() {
process.argv.push('--release');
if (remote.static) process.argv.push('--static');
await run(require('./build').default);
+ if (process.argv.includes('--static')) {
+ await cleanDir('build/*', {
+ nosort: true,
+ dot: true,
+ ignore: ['build/.git', 'build/public'],
+ });
+ await moveDir('build/public', 'build');
+ }
// Push the contents of the build folder to the remote server via Git
await spawn('git', ['add', '.', '--all'], options);
- await spawn('git', ['commit', '--message', `Update ${new Date().toISOString()}`], options);
- await spawn('git', ['push', remote.name, `master:${remote.branch}`, '--force', '--set-upstream'], options);
+ try {
+ await spawn('git', ['diff', '--cached', '--exit-code', '--quiet'], options);
+ } catch (error) {
+ await spawn('git', ['commit', '--message', `Update ${new Date().toISOString()}`], options);
+ }
+ await spawn('git', ['push', remote.name, `master:${remote.branch}`, '--set-upstream'], options);
// Check if the site was successfully deployed
const response = await fetch(remote.website);
diff --git a/tools/lib/cp.js b/tools/lib/cp.js
@@ -19,4 +19,15 @@ export const spawn = (command, args, options) => new Promise((resolve, reject) =
});
});
-export default { spawn };
+export const exec = (command, options) => new Promise((resolve, reject) => {
+ cp.exec(command, options, (err, stdout, stderr) => {
+ if (err) {
+ reject(err);
+ return;
+ }
+
+ resolve({ stdout, stderr });
+ });
+});
+
+export default { spawn, exec };
diff --git a/tools/lib/fs.js b/tools/lib/fs.js
@@ -21,6 +21,10 @@ export const writeFile = (file, contents) => new Promise((resolve, reject) => {
fs.writeFile(file, contents, 'utf8', err => (err ? reject(err) : resolve()));
});
+export const renameFile = (source, target) => new Promise((resolve, reject) => {
+ fs.rename(source, target, err => (err ? reject(err) : resolve()));
+});
+
export const copyFile = (source, target) => new Promise((resolve, reject) => {
let cbCalled = false;
function done(err) {
@@ -50,6 +54,20 @@ export const makeDir = name => new Promise((resolve, reject) => {
mkdirp(name, err => (err ? reject(err) : resolve()));
});
+export const moveDir = async (source, target) => {
+ const dirs = await readDir('**/*.*', {
+ cwd: source,
+ nosort: true,
+ dot: true,
+ });
+ await Promise.all(dirs.map(async (dir) => {
+ const from = path.resolve(source, dir);
+ const to = path.resolve(target, dir);
+ await makeDir(path.dirname(to));
+ await renameFile(from, to);
+ }));
+};
+
export const copyDir = async (source, target) => {
const dirs = await readDir('**/*.*', {
cwd: source,
@@ -71,9 +89,11 @@ export const cleanDir = (pattern, options) => new Promise((resolve, reject) =>
export default {
readFile,
writeFile,
+ renameFile,
copyFile,
readDir,
makeDir,
copyDir,
+ moveDir,
cleanDir,
};
diff --git a/tools/webpack.config.js b/tools/webpack.config.js
@@ -15,21 +15,7 @@ import pkg from '../package.json';
const isDebug = !process.argv.includes('--release');
const isVerbose = process.argv.includes('--verbose');
-const isAnalyse = process.argv.includes('--analyse') || process.argv.includes('--analyze');
-const port = parseInt(process.env.PORT || '3000', 10);
-const analyzerPort = port + 3;
-
-// Can be `server`, `static` or `disabled`.
-// In `server` mode analyzer will start HTTP server to show bundle report.
-// In `static` mode single HTML file with bundle report will be generated.
-// In `disabled` mode you can use this plugin to just generate Webpack Stats JSON
-// file by setting `generateStatsFile` to `true`.
-let analyzerMode = 'disabled';
-if (isAnalyse) {
- analyzerMode = 'server';
-} else if (!isDebug) {
- analyzerMode = 'static';
-}
+const isAnalyze = process.argv.includes('--analyze') || process.argv.includes('--analyse');
//
// Common configuration chunk to be used for both
@@ -37,7 +23,7 @@ if (isAnalyse) {
// -----------------------------------------------------------------------------
const config = {
- context: path.resolve(__dirname, '../src'),
+ context: path.resolve(__dirname, '..'),
output: {
path: path.resolve(__dirname, '../build/public/assets'),
@@ -144,10 +130,6 @@ const config = {
],
},
- resolve: {
- modules: [path.resolve(__dirname, '../src'), 'node_modules'],
- },
-
// Don't attempt to continue if there are any errors.
bail: !isDebug,
@@ -177,7 +159,7 @@ const clientConfig = {
target: 'web',
entry: {
- client: ['babel-polyfill', './client.js'],
+ client: ['babel-polyfill', './src/client.js'],
},
output: {
@@ -186,8 +168,6 @@ const clientConfig = {
chunkFilename: isDebug ? '[name].chunk.js' : '[name].[chunkhash:8].chunk.js',
},
- resolve: { ...config.resolve },
-
plugins: [
// Define free variables
// https://webpack.github.io/docs/list-of-plugins.html#defineplugin
@@ -233,30 +213,9 @@ const clientConfig = {
}),
],
- new BundleAnalyzerPlugin({
- // See above
- analyzerMode,
- // Host that will be used in `server` mode to start HTTP server.
- analyzerHost: '127.0.0.1',
- // Port that will be used in `server` mode to start HTTP server.
- analyzerPort,
- // Path to bundle report file that will be generated in `static` mode.
- // Relative to bundles output directory.
- reportFilename: path.resolve(__dirname, '../report.html'),
- // Automatically open report in default browser
- openAnalyzer: true,
- // If `true`, Webpack Stats JSON file will be generated in bundles output directory
- generateStatsFile: !isDebug,
- // Name of Webpack Stats JSON file that will be generated if `generateStatsFile` is `true`.
- // Relative to bundles output directory.
- statsFilename: path.resolve(__dirname, '../stats.json'),
- // Options for `stats.toJson()` method.
- // You can exclude sources of your modules from stats file with `source: false` option.
- // See more options here: https://github.com/webpack/webpack/blob/webpack-1/lib/Stats.js#L21
- statsOptions: null,
- // Log level. Can be 'info', 'warn', 'error' or 'silent'.
- logLevel: 'info',
- }),
+ // Webpack Bundle Analyzer
+ // https://github.com/th0r/webpack-bundle-analyzer
+ ...isAnalyze ? [new BundleAnalyzerPlugin()] : [],
],
// Choose a developer tool to enhance debugging
@@ -285,7 +244,7 @@ const serverConfig = {
target: 'node',
entry: {
- server: ['babel-polyfill', './server.js'],
+ server: ['babel-polyfill', './src/server.js'],
},
output: {
@@ -314,8 +273,6 @@ const serverConfig = {
})),
},
- resolve: { ...config.resolve },
-
externals: [
/^\.\/assets\.json$/,
(context, request, callback) => {