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": {