diff --git a/.gitignore b/.gitignore
index 766b018a5..3d06bab7e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -56,3 +56,4 @@ vendor/github.com/ugorji/go/codec/codecgen/bin/*
client/build-external/*
prog/staticui/*
prog/externalui/*
+client/pkg
diff --git a/Makefile b/Makefile
index c6928a28c..e30aaedcf 100644
--- a/Makefile
+++ b/Makefile
@@ -167,6 +167,9 @@ ui-upload: client/build-external/index.html
AWS_SECRET_ACCESS_KEY=$$UI_BUCKET_KEY_SECRET \
aws s3 cp client/build-external/ s3://static.weave.works/scope-ui/ --recursive --exclude '*.html'
+ui-build-pkg:
+ cd client && npm run build-pkg && npm run s3-publish
+
clean:
$(GO) clean ./...
# Don't actually rmi the build images - rm'ing the .uptodate files is enough to ensure
diff --git a/circle.yml b/circle.yml
index 47ed292d7..5555fa4b0 100644
--- a/circle.yml
+++ b/circle.yml
@@ -75,7 +75,8 @@ deployment:
)) &&
docker push ${DOCKER_ORGANIZATION:-$DOCKER_USER}/scope &&
docker push ${DOCKER_ORGANIZATION:-$DOCKER_USER}/scope:$(./tools/image-tag) &&
- (test -z "${UI_BUCKET_KEY_ID}" || make ui-upload)
+ (test -z "${UI_BUCKET_KEY_ID}" || make ui-upload) &&
+ make ui-build-pkg
)
- |
test -z "${QUAY_USER}" || (
diff --git a/client/.eslintrc b/client/.eslintrc
index 456aca91d..c6ed99ee7 100644
--- a/client/.eslintrc
+++ b/client/.eslintrc
@@ -1,5 +1,6 @@
{
"extends": "airbnb",
+ "parser": "babel-eslint",
"env": {
"browser": true,
"jest": true,
diff --git a/client/.gitignore b/client/.gitignore
index 1026d1331..ccd631864 100644
--- a/client/.gitignore
+++ b/client/.gitignore
@@ -2,3 +2,4 @@ node_modules
build/
coverage/
test/*png
+weave-scope.tgz
diff --git a/client/app/scripts/components/app.js b/client/app/scripts/components/app.js
index 3361c7285..05ac9bf59 100644
--- a/client/app/scripts/components/app.js
+++ b/client/app/scripts/components/app.js
@@ -104,7 +104,7 @@ class App extends React.Component {
const isIframe = window !== window.top;
return (
-
+
{showingDebugToolbar() && }
{showingHelp && }
diff --git a/client/app/scripts/components/terminal.js b/client/app/scripts/components/terminal.js
index ed9133c36..55b2219aa 100644
--- a/client/app/scripts/components/terminal.js
+++ b/client/app/scripts/components/terminal.js
@@ -9,10 +9,8 @@ import Term from 'xterm';
import { clickCloseTerminal } from '../actions/app-actions';
import { getNeutralColor } from '../utils/color-utils';
import { setDocumentTitle } from '../utils/title-utils';
-import { getPipeStatus, basePath, doResizeTty } from '../utils/web-api-utils';
+import { getPipeStatus, doResizeTty, wsUrl } from '../utils/web-api-utils';
-const wsProto = location.protocol === 'https:' ? 'wss' : 'ws';
-const wsUrl = `${wsProto}://${location.host}${basePath(location.pathname)}`;
const log = debug('scope:terminal');
const DEFAULT_COLS = 80;
diff --git a/client/app/scripts/utils/title-utils.js b/client/app/scripts/utils/title-utils.js
index fed4b86a1..47f035471 100644
--- a/client/app/scripts/utils/title-utils.js
+++ b/client/app/scripts/utils/title-utils.js
@@ -1,5 +1,5 @@
-const PREFIX = 'Weave Scope';
+const PREFIX = document.title || 'Weave Scope';
const SEPARATOR = ' - ';
export function setDocumentTitle(title) {
diff --git a/client/app/scripts/utils/web-api-utils.js b/client/app/scripts/utils/web-api-utils.js
index 15c063139..1a858d2c3 100644
--- a/client/app/scripts/utils/web-api-utils.js
+++ b/client/app/scripts/utils/web-api-utils.js
@@ -58,8 +58,14 @@ export function basePathSlash(urlPath) {
return `${basePath(urlPath)}/`;
}
+const API_PATH = basePathSlash(window.location.pathname) === '/'
+ ? 'api'
+ : `/api${window.location.pathname}/api`;
+
const wsProto = location.protocol === 'https:' ? 'wss' : 'ws';
-const wsUrl = `${wsProto}://${location.host}${basePath(location.pathname)}`;
+export const wsUrl = basePathSlash(window.location.pathname) === '/'
+ ? `${wsProto}://${location.host}${basePath(window.location.pathname)}`
+ : `${wsProto}://${location.host}/api${basePath(window.location.pathname)}`;
function createWebsocket(topologyUrl, optionsQuery, dispatch) {
if (socket) {
@@ -136,7 +142,7 @@ export function getAllNodes(getState, dispatch) {
export function getTopologies(options, dispatch) {
clearTimeout(topologyTimer);
const optionsQuery = buildOptionsQuery(options);
- const url = `api/topology?${optionsQuery}`;
+ const url = `${API_PATH}/topology?${optionsQuery}`;
reqwest({
url,
success: (res) => {
@@ -171,13 +177,13 @@ export function getNodeDetails(topologyUrlsById, currentTopologyId, options, nod
const obj = nodeMap.last();
if (obj && topologyUrlsById.has(obj.topologyId)) {
const topologyUrl = topologyUrlsById.get(obj.topologyId);
- let urlComponents = [topologyUrl, '/', encodeURIComponent(obj.id)];
+ let urlComponents = [API_PATH, '/', trimStart(topologyUrl, '/api'), '/', encodeURIComponent(obj.id)];
if (currentTopologyId === obj.topologyId) {
// Only forward filters for nodes in the current topology
const optionsQuery = buildOptionsQuery(options);
urlComponents = urlComponents.concat(['?', optionsQuery]);
}
- const url = urlComponents.join('').substr(1);
+ const url = urlComponents.join('');
reqwest({
url,
@@ -204,7 +210,7 @@ export function getNodeDetails(topologyUrlsById, currentTopologyId, options, nod
export function getApiDetails(dispatch) {
clearTimeout(apiDetailsTimer);
- const url = 'api';
+ const url = API_PATH;
reqwest({
url,
success: (res) => {
@@ -225,7 +231,7 @@ export function getApiDetails(dispatch) {
export function doControlRequest(nodeId, control, dispatch) {
clearTimeout(controlErrorTimer);
- const url = `api/control/${encodeURIComponent(control.probeId)}/`
+ const url = `${API_PATH}/control/${encodeURIComponent(control.probeId)}/`
+ `${encodeURIComponent(control.nodeId)}/${control.id}`;
reqwest({
method: 'POST',
@@ -261,7 +267,7 @@ export function doControlRequest(nodeId, control, dispatch) {
export function doResizeTty(pipeId, control, cols, rows) {
- const url = `api/control/${encodeURIComponent(control.probeId)}/`
+ const url = `${API_PATH}/control/${encodeURIComponent(control.probeId)}/`
+ `${encodeURIComponent(control.nodeId)}/${control.id}`;
return reqwest({
@@ -276,7 +282,7 @@ export function doResizeTty(pipeId, control, cols, rows) {
export function deletePipe(pipeId, dispatch) {
- const url = `api/pipe/${encodeURIComponent(pipeId)}`;
+ const url = `${API_PATH}/pipe/${encodeURIComponent(pipeId)}`;
reqwest({
method: 'DELETE',
url,
@@ -292,7 +298,7 @@ export function deletePipe(pipeId, dispatch) {
export function getPipeStatus(pipeId, dispatch) {
- const url = `api/pipe/${encodeURIComponent(pipeId)}/check`;
+ const url = `${API_PATH}/pipe/${encodeURIComponent(pipeId)}/check`;
reqwest({
method: 'GET',
url,
diff --git a/client/app/styles/main.scss b/client/app/styles/main.scss
index 3b23b92e6..62be5fa87 100644
--- a/client/app/styles/main.scss
+++ b/client/app/styles/main.scss
@@ -115,63 +115,52 @@ $label-background-color: fade-out($background-average-color, .3);
}
}
-* {
- box-sizing: border-box;
- -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
-}
-*:before,
-*:after {
- box-sizing: border-box;
-}
-
-html {
- -webkit-font-smoothing: antialiased;
-}
-
-html,
-body {
- height: 100%;
- width: 100%;
-}
-
-/* Space out content a bit */
-body {
- background: $body-background-color;
- color: $text-color;
- line-height: 150%;
- font-family: $base-font;
- font-size: 13px;
- margin: 0;
-}
-
-p {
- line-height: 20px;
- padding-top: 6px;
- margin-bottom: 14px;
- letter-spacing: 0;
- font-weight: 400;
- color: $text-color;
-}
-
-h2 {
- font-size: 34px;
- line-height: 40px;
- padding-top: 8px;
- margin-bottom: 12px;
- font-weight: 400;
-}
-
.hide {
opacity: 0;
}
-.app {
- position: fixed;
- top: 0;
- left: 0;
+.scope-app {
+ -webkit-font-smoothing: antialiased;
+ background: $body-background-color;
bottom: 0;
- right: 0;
+ color: $text-color;
+ font-family: $base-font;
+ font-size: 13px;
+ height: 100%;
+ left: 0;
+ line-height: 150%;
+ margin: 0;
overflow: auto;
+ position: fixed;
+ right: 0;
+ top: 0;
+ width: 100%;
+
+ * {
+ box-sizing: border-box;
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+ }
+ *:before,
+ *:after {
+ box-sizing: border-box;
+ }
+
+ p {
+ line-height: 20px;
+ padding-top: 6px;
+ margin-bottom: 14px;
+ letter-spacing: 0;
+ font-weight: 400;
+ color: $text-color;
+ }
+
+ h2 {
+ font-size: 34px;
+ line-height: 40px;
+ padding-top: 8px;
+ margin-bottom: 12px;
+ font-weight: 400;
+ }
}
.header {
diff --git a/client/index.js b/client/index.js
new file mode 100644
index 000000000..5335356d1
--- /dev/null
+++ b/client/index.js
@@ -0,0 +1,3 @@
+exports.reducer = require('./dist/reducers/root').default;
+exports.Scope = require('./dist/components/app').default;
+exports.ActionTypes = require('./dist/constants/action-types').default;
diff --git a/client/package.json b/client/package.json
index 6f6d797b3..4e0129a40 100644
--- a/client/package.json
+++ b/client/package.json
@@ -5,7 +5,9 @@
"repository": "weaveworks/scope",
"license": "Apache-2.0",
"private": true,
+ "main": "index.js",
"dependencies": {
+ "babel-plugin-lodash": "^3.2.10",
"babel-polyfill": "6.16.0",
"classnames": "2.2.5",
"d3-array": "1.0.2",
@@ -39,15 +41,16 @@
"reselect": "2.5.4",
"timely": "0.1.0",
"whatwg-fetch": "2.0.1",
+ "react-addons-perf": "15.4.1",
"xterm": "2.1.0"
},
"devDependencies": {
"autoprefixer": "6.5.3",
+ "babel-cli": "^6.18.0",
"babel-core": "6.18.2",
"babel-eslint": "7.1.1",
"babel-jest": "17.0.2",
"babel-loader": "6.2.8",
- "babel-plugin-lodash": "3.2.10",
"babel-preset-es2015": "6.18.0",
"babel-preset-react": "6.16.0",
"clean-webpack-plugin": "0.1.14",
@@ -67,7 +70,6 @@
"json-loader": "0.5.4",
"node-sass": "3.13.1",
"postcss-loader": "1.2.0",
- "react-addons-perf": "15.4.1",
"redux-devtools": "3.3.1",
"redux-devtools-dock-monitor": "1.1.1",
"redux-devtools-log-monitor": "1.1.1",
@@ -88,6 +90,8 @@
"scripts": {
"build": "webpack --config webpack.production.config.js",
"build-external": "EXTERNAL=true webpack --config webpack.production.config.js",
+ "build-pkg": "babel app/scripts --ignore __tests__ --out-dir pkg && cp package.json pkg/ && cp -R app/styles pkg/",
+ "bundle": "npm pack pkg && mv weave-scope-$npm_package_version.tgz weave-scope.tgz",
"start": "node server.js",
"start-production": "NODE_ENV=production node server.js",
"test": "jest",
@@ -95,7 +99,8 @@
"lint": "eslint app",
"clean": "rm build/app.js",
"noprobe": "../scope stop && ../scope launch --no-probe --app.window 8760h",
- "loadreport": "npm run noprobe && sleep 1 && curl -X POST -H \"Content-Type: application/json\" http://$BACKEND_HOST/api/report -d"
+ "loadreport": "npm run noprobe && sleep 1 && curl -X POST -H \"Content-Type: application/json\" http://$BACKEND_HOST/api/report -d",
+ "s3-publish": "aws s3 cp weave-scope.tgz s3://weaveworks-npm-modules/weave-scope/ --acl public-read"
},
"jest": {
"transform": {