Travis & Development's CI with surge

Adding Travis CI with custom deploy script for deploying in surge static files.

Major changes in code:
* Adding dynamic-import-node for fixing Travis tests
* Add babel-polyfill
* Adding a comment in PR using GitHub REST API with deployed link
This commit is contained in:
Adolfo Panizo 2018-03-12 09:30:03 +01:00 committed by GitHub
parent 01e8e08617
commit 888d6a7614
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 172 additions and 73 deletions

View File

@ -14,5 +14,12 @@
"@babel/plugin-syntax-dynamic-import",
"transform-es3-member-expression-literals",
"transform-es3-property-literals"
]
],
"env": {
"test": {
"plugins": [
"dynamic-import-node"
]
}
}
}

21
.travis.yml Normal file
View File

@ -0,0 +1,21 @@
language: node_js
node_js:
- "8"
os:
- linux
before_script:
- npm install -g truffle
- npm install -g surge
- git clone https://github.com/gnosis/gnosis-safe-contracts.git
- cd gnosis-safe-contracts
- truffle compile && cd ..
after_success:
- npm run build
- |
if [ ${TRAVIS_BRANCH} = "master" ]; then
export NODE_ENV=production;
else
export NODE_ENV=development;
fi
- chmod ugo+x ./config/deploy/deploy.sh
- ./config/deploy/deploy.sh

74
config/deploy/deploy.sh Normal file
View File

@ -0,0 +1,74 @@
#!/bin/bash
echo "Deployment script for gnosis-safe-team"
# Split on "/", ref: http://stackoverflow.com/a/5257398/689223
REPO_SLUG_ARRAY=(${TRAVIS_REPO_SLUG//\// })
REPO_OWNER=${REPO_SLUG_ARRAY[0]}
REPO_NAME=${REPO_SLUG_ARRAY[1]}
DEPLOY_PATH=./build_webpack
DEPLOY_SUBDOMAIN_UNFORMATTED_LIST=()
if [ "$TRAVIS_PULL_REQUEST" != "false" ]
then
if [ "$NODE_ENV" == "production" ]
then
DEPLOY_SUBDOMAIN_UNFORMATTED_LIST+=(release-${TRAVIS_PULL_REQUEST}-pr)
else
DEPLOY_SUBDOMAIN_UNFORMATTED_LIST+=(staging-${TRAVIS_PULL_REQUEST}-pr)
fi
elif [ -n "${TRAVIS_TAG// }" ] #TAG is not empty
then
if [ "$NODE_ENV" == "production" ]
then
#sorts the tags and picks the latest
#sort -V does not work on the travis machine
#sort -V ref: http://stackoverflow.com/a/14273595/689223
#sort -t ... ref: http://stackoverflow.com/a/4495368/689223
#reverse with sed ref: http://stackoverflow.com/a/744093/689223
#git tags | ignore release candidates | sort versions | reverse | pick first line
LATEST_TAG=`git tag | grep -v rc | sort -t. -k 1,1n -k 2,2n -k 3,3n -k 4,4n | sed '1!G;h;$!d' | sed -n 1p`
echo $LATEST_TAG
if [ "$TRAVIS_TAG" == "$LATEST_TAG" ]
then
DEPLOY_SUBDOMAIN_UNFORMATTED_LIST+=(latest)
fi
DEPLOY_SUBDOMAIN_UNFORMATTED_LIST+=(${TRAVIS_TAG}-tag)
fi
else
DEPLOY_SUBDOMAIN_UNFORMATTED_LIST+=(${TRAVIS_BRANCH}-branch)
fi
for DEPLOY_SUBDOMAIN_UNFORMATTED in "${DEPLOY_SUBDOMAIN_UNFORMATTED_LIST[@]}"
do
echo $DEPLOY_SUBDOMAIN_UNFORMATTED
# replaces non alphanumeric symbols with "-"
# sed -r is only supported in linux, ref http://stackoverflow.com/a/2871217/689223
# Domain names follow the RFC1123 spec [a-Z] [0-9] [-]
# The length is limited to 253 characters
# https://en.wikipedia.org/wiki/Domain_Name_System#Domain_name_syntax
DEPLOY_SUBDOMAIN=`echo "$DEPLOY_SUBDOMAIN_UNFORMATTED" | sed -r 's/[^A-Za-z0-9]+/\-/g'`
echo $DEPLOY_SUBDOMAIN
DEPLOY_DOMAIN=https://${DEPLOY_SUBDOMAIN}-${REPO_NAME}-${REPO_OWNER}.surge.sh
surge --project ${DEPLOY_PATH} --domain $DEPLOY_DOMAIN;
if [ "$TRAVIS_PULL_REQUEST" != "false" ]
then
# Using the Issues api instead of the PR api
# Done so because every PR is an issue, and the issues api allows to post general comments,
# while the PR api requires that comments are made to specific files and specific commits
GITHUB_PR_COMMENTS=https://api.github.com/repos/${TRAVIS_REPO_SLUG}/issues/${TRAVIS_PULL_REQUEST}/comments
curl -H "Authorization: token ${GITHUB_API_TOKEN}" --request POST ${GITHUB_PR_COMMENTS} --data '{"body":"Travis automatic deployment: '${DEPLOY_DOMAIN}'"}'
fi
done
echo "Deploy domain: ${DEPLOY_DOMAIN}"

View File

@ -1,18 +1,18 @@
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const autoprefixer = require('autoprefixer');
const cssvars = require('postcss-simple-vars');
const webpack = require('webpack');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
const autoprefixer = require('autoprefixer')
const cssvars = require('postcss-simple-vars')
const webpack = require('webpack')
const UglifyJSPlugin = require('uglifyjs-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const ManifestPlugin = require('webpack-manifest-plugin');
const url = require('url');
const HtmlWebpackPlugin = require('html-webpack-plugin')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const ManifestPlugin = require('webpack-manifest-plugin')
const url = require('url')
var paths = require('./paths');
var getClientEnvironment = require('./env');
const paths = require('./paths')
const getClientEnvironment = require('./env')
var cssvariables = require(paths.appSrc + '/theme/variables');
const cssvariables = require(`${paths.appSrc}/theme/variables`)
const postcssPlugins = [
autoprefixer({
@ -21,25 +21,24 @@ const postcssPlugins = [
'last 4 versions',
'Firefox ESR',
'not ie < 9', // React doesn't support IE8 anyway
]
],
}),
cssvars({
variables: function () {
return Object.assign({}, cssvariables);
variables() {
return Object.assign({}, cssvariables)
},
silent: true
silent: true,
}),
];
]
function ensureSlash(path, needsSlash) {
var hasSlash = path.endsWith('/');
const hasSlash = path.endsWith('/')
if (hasSlash && !needsSlash) {
return path.substr(path, path.length - 1);
return path.substr(path, path.length - 1)
} else if (!hasSlash && needsSlash) {
return path + '/';
} else {
return path;
return `${path}/`
}
return path
}
// We use "homepage" field to infer "public path" at which the app is served.
@ -47,22 +46,23 @@ function ensureSlash(path, needsSlash) {
// single-page apps that may serve index.html for nested URLs like /todos/42.
// We can't use a relative path in HTML because we don't want to load something
// like /todos/42/static/js/bundle.7289d.js. We have to know the root.
var homepagePath = require(paths.appPackageJson).homepage;
var homepagePathname = homepagePath ? url.parse(homepagePath).pathname : '/';
const homepagePath = require(paths.appPackageJson).homepage
// var homepagePathname = homepagePath ? url.parse(homepagePath).pathname : '/';
const homepagePathname = '/'
// Webpack uses `publicPath` to determine where the app is being served from.
// It requires a trailing slash, or the file assets will get an incorrect path.
var publicPath = ensureSlash(homepagePathname, true);
const publicPath = ensureSlash(homepagePathname, true)
// `publicUrl` is just like `publicPath`, but we will provide it to our app
// as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript.
// Omit trailing slash as %PUBLIC_PATH%/xyz looks better than %PUBLIC_PATH%xyz.
var publicUrl = ensureSlash(homepagePathname, false);
const publicUrl = ensureSlash(homepagePathname, false)
// Get environment variables to inject into our app.
var env = getClientEnvironment(publicUrl);
const env = getClientEnvironment(publicUrl)
// Assert this just to be safe.
// Development builds of React are slow and not intended for production.
if (env['process.env'].NODE_ENV !== '"production"') {
throw new Error('Production builds must have NODE_ENV=production.');
throw new Error('Production builds must have NODE_ENV=production.')
}
// This is the production configuration.
@ -77,17 +77,17 @@ module.exports = {
cacheGroups: {
vendor: {
test: /node_modules/,
name: "vendor",
chunks: "all",
name: 'vendor',
chunks: 'all',
enforce: true,
minSize: 1
}
}
}
minSize: 1,
},
},
},
},
entry: [
require.resolve('./polyfills'),
paths.appIndexJs
paths.appIndexJs,
],
output: {
// The build folder.
@ -98,14 +98,14 @@ module.exports = {
filename: 'static/js/[name].[chunkhash:8].js',
chunkFilename: 'static/js/[name].[chunkhash:8].chunk.js',
// We inferred the "public path" (such as / or /my-project) from homepage.
publicPath: publicPath
publicPath,
},
resolve: {
modules: [
paths.appSrc,
'node_modules',
paths.appContracts,
],
],
// These are the reasonable defaults supported by the Node ecosystem.
// We also include JSX as a common component filename extension to support
// some tools, although we do not recommend using it, see:
@ -114,7 +114,7 @@ module.exports = {
alias: {
'~': paths.appSrc,
'#': paths.appContracts,
}
},
},
module: {
@ -123,33 +123,33 @@ module.exports = {
test: /\.(js|jsx)$/,
include: paths.appSrc,
use: {
loader: "babel-loader"
}
loader: 'babel-loader',
},
},
{
test: /\.(scss|css)$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
{
{
loader: 'css-loader',
options: {
importLoaders: 1,
modules: true,
minimize: true
}
minimize: true,
},
},
{
loader: 'postcss-loader',
options: {
sourceMap: true,
plugins: postcssPlugins,
}
},
},
],
})
}),
},
]
],
},
plugins: [
// Generates an `index.html` file with the <script> injected.
@ -166,8 +166,8 @@ module.exports = {
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true
}
minifyURLs: true,
},
}),
// Makes some environment variables available to the JS code, for example:
// if (process.env.NODE_ENV === 'production') { ... }. See `./env.js`.
@ -180,7 +180,7 @@ module.exports = {
// to their corresponding output file so that tools can pick it up without
// having to parse `index.html`.
new ManifestPlugin({
fileName: 'asset-manifest.json'
fileName: 'asset-manifest.json',
}),
// new BundleAnalyzerPlugin()
],
@ -189,6 +189,6 @@ module.exports = {
node: {
fs: 'empty',
net: 'empty',
tls: 'empty'
}
};
tls: 'empty',
},
}

19
package-lock.json generated
View File

@ -2310,6 +2310,15 @@
"babel-runtime": "6.26.0"
}
},
"babel-plugin-dynamic-import-node": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-1.2.0.tgz",
"integrity": "sha512-yeDwKaLgGdTpXL7RgGt5r6T4LmnTza/hUn5Ul8uZSGGMtEjYo13Nxai7SQaGCTEzUtg9Zq9qJn0EjEr7SeSlTQ==",
"dev": true,
"requires": {
"babel-plugin-syntax-dynamic-import": "6.18.0"
}
},
"babel-plugin-istanbul": {
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-4.1.5.tgz",
@ -20792,16 +20801,6 @@
"requires": {
"has-flag": "3.0.0"
}
},
"ws": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-4.1.0.tgz",
"integrity": "sha512-ZGh/8kF9rrRNffkLFV4AzhvooEclrOH0xaugmqGsIfFgOE/pIz4fMc4Ef+5HSQqTEug2S9JZIWDR47duDSLfaA==",
"dev": true,
"requires": {
"async-limiter": "1.0.0",
"safe-buffer": "5.1.1"
}
}
}
},

View File

@ -38,6 +38,7 @@
"babel-core": "^7.0.0-bridge.0",
"babel-eslint": "^7.2.3",
"babel-loader": "^8.0.0-beta.0",
"babel-plugin-dynamic-import-node": "^1.2.0",
"babel-plugin-transform-es3-member-expression-literals": "^6.22.0",
"babel-plugin-transform-es3-property-literals": "^6.22.0",
"css-loader": "^0.28.10",

View File

@ -1,3 +1,4 @@
/*eslint-disable*/
// Do this as the first thing so that any code reading it knows the right env.
process.env.NODE_ENV = 'production';
@ -124,19 +125,14 @@ function build(previousSizeMap) {
console.log('Creating an optimized production build...');
webpack(config).run((err, stats) => {
if (err) {
printErrors('Failed to compile.', [err]);
printErrors('Failed to compile A.', [err]);
process.exit(1);
}
if (stats.compilation.errors.length) {
printErrors('Failed to compile.', stats.compilation.errors);
printErrors('Failed to compile B.', stats.compilation.errors);
process.exit(1);
}
if (process.env.CI && stats.compilation.warnings.length) {
printErrors('Failed to compile.', stats.compilation.warnings);
process.exit(1);
}
}
console.log(chalk.green('Compiled successfully.'));
console.log();

View File

@ -1,3 +1,4 @@
import 'babel-polyfill'
import { MuiThemeProvider } from 'material-ui/styles'
import React from 'react'
import ReactDOM from 'react-dom'
@ -12,5 +13,5 @@ const Root = () => (
ReactDOM.render(
<Root />,
document.getElementById('root')
);
document.getElementById('root'),
)