move to browserify

This commit is contained in:
Vladimir Polyakov 2015-12-11 09:53:57 +03:00
parent a042876009
commit 5879a7caf3
59 changed files with 37251 additions and 467 deletions

5
.jshintignore Normal file
View File

@ -0,0 +1,5 @@
static/scripts/**
static/js/**
node_modules/**
lib/node/jake/**
docs/build_docs/**

33
.jshintrc Normal file
View File

@ -0,0 +1,33 @@
{
"node": true,
"newcap": false,
"supernew": false,
"maxlen": 80,
"smarttabs": true,
"indent": 1,
"globalstrict": true,
"strict": true,
// use es3 to get 'extra comma' at object literal error
"es3": true,
// suppress warnings about switches with just one case
"onecase": true,
"nonew": false,
"trailing": true,
"sub": false,
"loopfunc": true,
"boss": false,
"lastsemic": false,
"quotmark": "single",
"undef": true,
"immed": true,
// allows ECMAScript 6 syntax (generators, etc)
"esnext": true,
// allow generators without a yield
"noyield": true,
"globals": {
"describe": false,
"it": false,
"before": false,
"after": false
}
}

10
app/actions/build.js Normal file
View File

@ -0,0 +1,10 @@
'use strict';
var Reflux = require('reflux');
module.exports = Reflux.createActions([
'cancel',
'readTerminalOutput',
'readAll',
'read'
]);

9
app/actions/buildLog.js Normal file
View File

@ -0,0 +1,9 @@
'use strict';
var Reflux = require('reflux');
module.exports = Reflux.createActions([
'getTail',
'getLines'
]);

9
app/actions/project.js Normal file
View File

@ -0,0 +1,9 @@
'use strict';
var Reflux = require('reflux');
module.exports = Reflux.createActions([
'run',
'readAll',
'read'
]);

44
app/app.js Normal file
View File

@ -0,0 +1,44 @@
'use strict';
var React = require('react'),
ReactDOM = require('react-dom'),
App = require('./components/app'),
Dashboard = require('./components/dashboard'),
connect = require('./connect'),
resources = require('./resources'),
Router = require('react-router');
var Route = React.createFactory(Router.Route),
DefaultRoute = React.createFactory(Router.DefaultRoute);
var routes = (
Route({handler: App},
Route({
name: 'dashboard',
path: '/',
handler: Dashboard
})
//Route({
//name: 'project',
//path: 'projects/:name',
//handler: Components.Project.View
//}),
//Route({name: 'build', path: 'builds/:id', handler: Components.Build.View}),
//Route({
//name: 'buildLog',
//path: 'builds/:buildId/log',
//handler: Components.BuildLog
//})
)
);
connect.io.on('connect', function() {
console.log('on connect');
Router.run(routes, Router.HistoryLocation, function(Handler) {
ReactDOM.render(
React.createElement(Handler),
document.getElementById('content')
);
});
});

View File

@ -0,0 +1,5 @@
div
Header()
.container-fluid
.page-wrapper
RouteHandler()

View File

@ -0,0 +1,23 @@
'use strict';
var React = require('react'),
Router = require('react-router'),
Header = require('../header'),
ProjectActions = require('../../actions/project'),
template = require('./index.jade');
var Component = React.createClass({
componentDidMount: function() {
console.log('read all projects in component');
ProjectActions.readAll();
},
render: function() {
return template({
Link: Router.Link,
Header: Header,
RouteHandler: Router.RouteHandler
});
}
});
module.exports = Component;

View File

@ -0,0 +1,94 @@
mixin statusText(build)
if build.status === 'in-progress'
span in progress
if build.status === 'queued'
span queued
if build.status === 'done'
span done
if build.status === 'error'
span error
- var build = this.props.build;
.build(class="")
.build_content
.build_status
.status(class="status__#{build.status}")
div.build_header
if build.project
span
Scm(scm=build.project.scm.type)
|
Link(to="project", params={name: build.project.name})
span= build.project.name
|
if build.number
span(style={fontSize: '15px', color: '#a6a6a6'}) build
|
if build.status !== 'queued'
Link(to="build", params={id: build.id})
span #
span= build.number
else
span #
span= build.number
if build.waitReason
span (
span= build.waitReason
span , waiting)
if build.status === 'in-progress' && build.currentStep
span (
span= build.currentStep
span )
div
if build.endDate
span.build_info
i.fa.fa-fw.fa-clock-o
| finished
DateTime(value=build.endDate)
|
Duration(value=(build.endDate - build.startDate), withSuffix=true)
else
if build.startDate
span.build_info
i.fa.fa-fw.fa-clock-o
| started
DateTime(value=build.startDate)
else
span.build_info
i.fa.fa-fw.fa-clock-o
| queued
DateTime(value=build.createDate)
|
if build.scm
span.build_info
i.fa.fa-fw.fa-comment-o
|
span= utils.prune(build.scm.rev.comment, 40)
|
.build_controls
if build.completed
.build_controls_buttons
a.btn.btn-sm.btn-default(href="javascript:void(0);", onClick=this.onRebuildProject(build.project.name))
i.fa.fa-fw.fa-repeat(title="Rebuild")
|
| Build again
if build.status === 'in-progress'
.build_controls_progress
if build.project.avgBuildDuration
Progress(build=build)
if build.status === 'queued'
.build_controls_buttons
a.btn.btn-sm.btn-default(href="javascript:void(0);", onClick=this.onCancelBuild(build.id))
i.fa.fa-fw.fa-times(title="Cancel build")
|
| Cancel build

View File

@ -0,0 +1,25 @@
'use strict';
var _ = require('underscore'),
React = require('react'),
Router = require('react-router'),
ProjectActions = require('../../../actions/project'),
BuildActions = require('../../../actions/build'),
CommonComponents = require('../../common'),
utils = require('../../../utils'),
template = require('./index.jade');
var Component = React.createClass({
onRebuildProject: function(projectName) {
ProjectActions.run(projectName);
},
onCancelBuild: function(buildId) {
BuildActions.cancel(buildId);
},
render: template.locals(_({
Link: Router.Link,
utils: utils
}).extend(CommonComponents))
});
module.exports = Component;

View File

@ -0,0 +1,7 @@
.builds
if !this.state.items.length
p Build history is empty
- console.log('>>>> builds = ', JSON.stringify(this.state.items).length, this.state.items)
each build, index in this.state.items
Item(build=build, key=build.id)

View File

@ -0,0 +1,28 @@
'use strict';
var _ = require('underscore'),
React = require('react'),
Reflux = require('reflux'),
Item = require('../item'),
buildsStore = require('../../../stores/builds'),
template = require('./index.jade');
var Component = React.createClass({
mixins: [
Reflux.connectFilter(buildsStore, 'items', function(items) {
var projectName = this.props.projectName;
if (projectName) {
return _(items).filter(function(item) {
return item.project && item.project.name === projectName;
});
} else {
return items;
}
})
],
render: template.locals({
Item: Item
})
});
module.exports = Component;

View File

@ -0,0 +1,13 @@
'use strict';
var React = require('react'),
template = require('./template.jade'),
moment = require('moment');
var Component = React.createClass({
render: template.locals({
moment: moment
})
});
module.exports = Component;

View File

@ -0,0 +1,13 @@
'use strict';
var React = require('react'),
template = require('./index.jade'),
moment = require('moment');
var Component = React.createClass({
render: template.locals({
moment: moment
})
});
module.exports = Component;

View File

@ -0,0 +1,13 @@
'use strict';
var DateTime = require('./dateTime'),
Duration = require('./duration'),
Progress = require('./progress'),
Scm = require('./scm');
module.exports = {
DateTime: DateTime,
Scm: Scm,
Duration: Duration,
Progress: Progress
};

View File

@ -0,0 +1,32 @@
'use strict';
var React = require('react'),
_ = require('underscore'),
template = require('./index.jade');
module.exports = React.createClass({
render: template,
_computePercent: function() {
var build = this.props.build;
return Math.round((Date.now() - build.startDate) /
build.project.avgBuildDuration * 100);
},
componentDidMount: function() {
var self = this;
var updateCallback = function() {
if (self.props.build.status === 'in-progress') {
if (self.isMounted()) {
self.setState({percent: self._computePercent()});
_.delay(updateCallback, 100);
}
}
};
updateCallback();
},
getInitialState: function() {
return {
percent: this._computePercent()
};
}
});

View File

@ -0,0 +1,9 @@
'use strict';
var React = require('react'),
template = require('./index.jade');
module.exports = React.createClass({
render: template
});

View File

@ -0,0 +1,5 @@
.main-row
.row
.col-md-8.col-sm-12
h2 Builds history
BuildsList()

View File

@ -0,0 +1,19 @@
'use strict';
var React = require('react'),
Router = require('react-router'),
ProjectActions = require('../../actions/project'),
BuildActions = require('../../actions/build'),
BuildsList = require('../builds/list'),
template = require('./index.jade');
module.exports = React.createClass({
componentWillMount: function() {
ProjectActions.readAll();
BuildActions.readAll();
},
render: template.locals({
Link: Router.Link,
BuildsList: BuildsList
})
});

View File

@ -0,0 +1,15 @@
'use strict';
var React = require('react'),
Router = require('react-router'),
ProjectsSelector = require('../projects-selector'),
template = require('./index.jade');
var Component = React.createClass({
render: template.locals({
Link: Router.Link,
ProjectsSelector: ProjectsSelector
})
});
module.exports = Component;

View File

@ -0,0 +1,24 @@
.projects-selector(href="javascript:void(0);")
if !this.state.showSearch
span.projects-selector_preview(onClick=this.onSearchProject)
i.fa.fa-fw.fa-bars
span.projects-selector_preview_text Select a project...
else
input.projects-selector_input(
type="text",
value=this.state.searchQuery,
onChange=this.onSearchChange,
ref=this.onInputMount,
onBlur=this.onBlurSearch
)
ul.projects-selector_items
each project in this.state.projects
li.projects-selector_item(key=project.name)
Link.projects-selector_item_link(to="project", params={name: project.name}, onMouseDown=this.onSelectProject(project.name))
Scm(scm=project.scm.type)
span
span= project.name
a.projects-selector_item_run(href="javascript:void(0);", onMouseDown=this.onRunProject(project.name))
i.fa.fa-fw.fa-play

View File

@ -0,0 +1,54 @@
'use strict';
var React = require('react'),
ReactDOM = require('react-dom'),
Router = require('react-router'),
Reflux = require('reflux'),
ProjectActions = require('../../actions/project'),
projectsStore = require('../../stores/project'),
template = require('./index.jade'),
Scm = require('../common/scm');
module.exports = React.createClass({
mixins: [Reflux.ListenerMixin, Router.Navigation],
componentDidMount: function() {
this.listenTo(projectsStore, this.updateItems);
},
getInitialState: function() {
return {
showSearch: false,
projects: []
};
},
onRunProject: function(projectName) {
ProjectActions.run(projectName);
this.setState({showSearch: false});
},
onSelectProject: function(name) {
this.transitionTo('project', {name: name});
},
updateItems: function(projects) {
this.setState({projects: projects});
},
onSearchProject: function() {
this.setState({showSearch: true});
},
onInputMount: function(component) {
var node = ReactDOM.findDOMNode(component);
if (node) {
node.focus();
}
},
onBlurSearch: function() {
this.setState({showSearch: false});
},
onSearchChange: function(event) {
var query = event.target.value;
this.setState({searchQuery: query});
ProjectActions.readAll({nameQuery: query});
},
render: template.locals({
Link: Router.Link,
Scm: Scm
})
});

9
app/connect.js Normal file
View File

@ -0,0 +1,9 @@
'use strict';
var socketio = require('socket.io-client'),
dataio = require('data.io/data.io'),
io = socketio(),
data = dataio(io);
module.exports.io = io;
module.exports.data = data;

10
app/resources.js Normal file
View File

@ -0,0 +1,10 @@
'use strict';
var connect = require('./connect'),
projects = connect.data.resource('projects'),
builds = connect.data.resource('builds');
module.exports = {
projects: projects,
builds: builds
};

34
app/stores/build.js Normal file
View File

@ -0,0 +1,34 @@
'use strict';
var _ = require('underscore'),
Reflux = require('reflux'),
BuildActions = require('../actions/build'),
resources = require('../resources'),
resource = resources.builds;
var Store = Reflux.createStore({
listenables: BuildActions,
build: null,
onChange: function(data, action) {
if (this.build && (data.buildId === this.build.id)) {
_(this.build).extend(data.changes);
this.trigger(this.build);
}
},
init: function() {
resource.subscribe('change', this.onChange);
},
onRead: function(id) {
var self = this;
resource.sync('read', {id: id}, function(err, build) {
if (err) throw err;
self.build = build;
self.trigger(self.build);
});
}
});
module.exports = Store;

43
app/stores/buildLog.js Normal file
View File

@ -0,0 +1,43 @@
'use strict';
var Reflux = require('reflux'),
BuildLogActions = require('../actions/buildLog'),
resources = require('../resources'),
resource = resources.builds;
var Store = Reflux.createStore({
listenables: BuildLogActions,
data: {
lines: [],
total: 0
},
getInitialState: function() {
return this.data;
},
onGetTail: function(params) {
var self = this;
console.time('>>> getBuildLogTail');
resource.sync('getBuildLogTail', params, function(err, data) {
if (err) throw err;
console.timeEnd('>>> getBuildLogTail');
self.data = data;
self.trigger(self.data);
});
},
onGetLines: function(params) {
var self = this;
console.time('>>> getBuildLogLines');
resource.sync('getBuildLogLines', params, function(err, data) {
if (err) throw err;
console.timeEnd('>>> getBuildLogLines');
console.log('>>> isLast log lines = ', data.isLast);
self.data.lines = data.lines;
self.trigger(self.data);
});
}
});
module.exports = Store;

66
app/stores/builds.js Normal file
View File

@ -0,0 +1,66 @@
'use strict';
var _ = require('underscore'),
Reflux = require('reflux'),
BuildActions = require('../actions/build'),
resources = require('../resources'),
resource = resources.builds;
var Store = Reflux.createStore({
listenables: BuildActions,
builds: [],
getInitialState: function() {
return this.builds;
},
onChanged: function(data) {
var oldBuild = _(this.builds).findWhere({id: data.buildId});
if (oldBuild) {
_(oldBuild).extend(data.changes);
} else {
this.builds.unshift(
_({id: data.buildId}).extend(data.changes)
);
}
this.trigger(this.builds);
},
onCancelled: function(data) {
// WORKAROUND: client that trigger `onCancel` gets one `onCancelled`
// call other clients get 2 calls (second with empty data)
if (!data) {
return;
}
var index = _(this.builds).findIndex({id: data.buildId});
if (index !== -1) {
this.builds.splice(index, 1);
}
this.trigger(this.builds);
},
init: function() {
resource.subscribe('change', this.onChanged);
resource.subscribe('cancel', this.onCancelled);
},
onReadAll: function(params) {
var self = this;
resource.sync('readAll', params, function(err, builds) {
if (err) throw err;
self.builds = builds;
self.trigger(self.builds);
});
},
onCancel: function(buildId) {
resource.sync('cancel', {buildId: buildId}, function(err) {
if (err) throw err;
});
}
});
module.exports = Store;

35
app/stores/project.js Normal file
View File

@ -0,0 +1,35 @@
'use strict';
var _ = require('underscore'),
Reflux = require('reflux'),
ProjectActions = require('../actions/project'),
resources = require('../resources'),
resource = resources.projects;
var Store = Reflux.createStore({
listenables: ProjectActions,
project: {},
getInitialState: function() {
return this.project;
},
onChange: function(data, action) {
this.trigger(data.project);
},
init: function() {
resource.subscribe('change', this.onChange);
},
onRead: function(params) {
var self = this;
resource.sync('read', params, function(err, project) {
if (err) throw err;
self.project = project;
self.trigger(self.project);
});
}
});
module.exports = Store;

23
app/stores/projects.js Normal file
View File

@ -0,0 +1,23 @@
'use strict';
var Reflux = require('reflux'),
ProjectActions = require('../actions/project'),
resource = require('../resources').projects;
var Store = Reflux.createStore({
listenables: ProjectActions,
onRun: function(projectName) {
resource.sync('run', {projectName: projectName}, function(err) {
if (err) throw err;
});
},
onReadAll: function(params) {
var self = this;
resource.sync('readAll', params, function(err, projects) {
if (err) throw err;
self.trigger(projects);
});
}
});
module.exports = Store;

64
app/stores/terminal.js Normal file
View File

@ -0,0 +1,64 @@
'use strict';
var _ = require('underscore'),
Reflux = require('reflux'),
BuildActions = require('../actions/build'),
connect = require('app/connect');
var Store = Reflux.createStore({
listenables: BuildActions,
init: function() {
// the only purpose of this hash to reconnect all the time
// except first, see notes at using
this.connectedResourcesHash = {};
},
onReadTerminalOutput: function(build) {
var self = this,
output = [],
resourceName = 'build' + build.id;
var connectToBuildDataResource = function() {
// reconnect for get data below (at subscribe), coz
// data emitted only once during connect
if (self.connectedResourcesHash[resourceName]) {
connect.resource(resourceName).reconnect();
} else {
self.connectedResourcesHash[resourceName] = 1;
}
connect.resource(resourceName).subscribe('data', function(data) {
var lastLine = _(self.lines).last();
if (lastLine && (_(data.lines).first().number === lastLine.number)) {
self.lines = _(self.lines).initial();
}
self.lines = self.lines.concat(data.lines);
self.trigger({
buildId: build.id,
name: 'Console for build #' + build.id,
data: _(self.lines).pluck('text')
});
});
};
this.lines = [];
this.currentLine = '';
// create data resource for completed build
if (build.completed) {
connect.resource('projects').sync(
'createBuildDataResource',
{buildId: build.id},
function(err) {
if (err) throw err;
connectToBuildDataResource();
}
);
} else {
connectToBuildDataResource();
}
}
});
module.exports = Store;

16
app/utils.js Normal file
View File

@ -0,0 +1,16 @@
'use strict';
var utils = {};
utils.prune = function(str, length) {
var result = '',
words = str.split(' ');
do {
result += words.shift() + ' ';
} while (words.length && result.length < length);
return result.replace(/ $/, words.length ? '...' : '');
};
module.exports = utils;

View File

@ -8,7 +8,9 @@
"scripts": {
"makeTestRepos": "rm -rf test/repos/{mercurial,git}; cd test/repos/ && tar -xf mercurial.tar.gz && tar -xf git.tar.gz",
"test": "npm run makeTestRepos && mocha --bail --reporter=spec --timeout 4000",
"dev": "gulp",
"watchLess": "catw -c 'lessc static/css/index.less' 'static/css/**/*.less' > static/css/index.css",
"watchJs": "watchify app/app.js -t react-jade -o static/js/app.build.js -dv",
"dev": "npm run watchJs & nodemon app.js",
"sync": "npm install && npm prune && bower install && bower prune",
"buildJs": "r.js -o static/js/requirejs/buid.js",
"buildClean": "rm static/index.html",
@ -46,20 +48,22 @@
"colors": "1.1.2",
"cron": "1.0.9",
"data.io": "0.3.0",
"moment": "^2.10.6",
"nlevel": "1.0.3",
"node-static": "0.7.6",
"socket.io": "1.3.5",
"socket.io-client": "^1.3.7",
"through": "2.3.6",
"twostep": "0.4.1",
"underscore": "1.8.3",
"through": "2.3.6"
"underscore": "1.8.3"
},
"devDependencies": {
"bower": "1.4.1",
"catw": "^1.0.1",
"expect.js": "0.3.1",
"gulp": "3.8.11",
"gulp-less": "3.0.3",
"gulp-nodemon": "2.0.3",
"gulp-react-jade-amd": "git://github.com/vladimir-polyakov/gulp-react-jade-amd",
"jade": "1.11.0",
"main-bower-files": "2.7.0",
"memdown": "1.1.0",

View File

@ -1,4 +1,4 @@
@import url(http://fonts.googleapis.com/css?family=Open+Sans:400,700&subset=latin,cyrillic-ext);
/*@import url(http://fonts.googleapis.com/css?family=Open+Sans:400,700&subset=latin,cyrillic-ext);*/
@bowerPath: "../js/libs";
@ -24,3 +24,4 @@
@import "./sources/components/builds.less";
@import "./sources/components/projects.less";
@import "./sources/components/terminal.less";

File diff suppressed because one or more lines are too long

View File

@ -1,12 +0,0 @@
'use strict';
define(['reflux'], function(Reflux) {
var Actions = Reflux.createActions([
'cancel',
'readTerminalOutput',
'readAll',
'read'
]);
return Actions;
});

View File

@ -1,10 +0,0 @@
'use strict';
define(['reflux'], function(Reflux) {
var Actions = Reflux.createActions([
'getTail',
'getLines'
]);
return Actions;
});

View File

@ -1,11 +0,0 @@
'use strict';
define(['reflux'], function(Reflux) {
var Actions = Reflux.createActions([
'run',
'readAll',
'read'
]);
return Actions;
});

View File

@ -1,17 +0,0 @@
'use strict';
define([
'react',
'templates/app/components/common/dateTime/template',
'moment'
], function(React, template, moment) {
template = template.locals({
moment: moment
});
var Component = React.createClass({
render: template
});
return Component;
});

View File

@ -1,15 +0,0 @@
'use strict';
define([
'react', 'templates/app/components/common/duration/index', 'moment'
], function(React, template, moment) {
template = template.locals({
moment: moment
});
var Component = React.createClass({
render: template
});
return Component;
});

View File

@ -1,15 +0,0 @@
'use strict';
define([
'./dateTime/index',
'./scm/index',
'./duration/index',
'./progress/index'
], function(DateTime, Scm, Duration, Progress) {
return {
DateTime: DateTime,
Scm: Scm,
Duration: Duration,
Progress: Progress
};
});

View File

@ -1,34 +0,0 @@
'use strict';
define([
'underscore',
'react',
'templates/app/components/common/progress/index'
], function(_, React, template) {
return React.createClass({
render: template,
_computePercent: function() {
var build = this.props.build;
return Math.round((Date.now() - build.startDate) /
build.project.avgBuildDuration * 100);
},
componentDidMount: function() {
var self = this;
var updateCallback = function() {
if (self.props.build.status === 'in-progress') {
if (self.isMounted()) {
self.setState({percent: self._computePercent()});
_.delay(updateCallback, 100);
}
}
};
updateCallback();
},
getInitialState: function() {
return {
percent: this._computePercent()
}
}
});
});

View File

@ -1,10 +0,0 @@
'use strict';
define([
'react', 'templates/app/components/common/scm/index'
], function(React, template) {
return React.createClass({
render: template
});
});

View File

@ -1,20 +0,0 @@
'use strict';
define([
'react',
'react-router',
'app/components/projects/selector/index',
'templates/app/components/header/index',
'bootstrap/collapse'
], function(React, Router, ProjectsSelector, template) {
template = template.locals({
Link: Router.Link,
ProjectsSelector: ProjectsSelector
});
var Component = React.createClass({
render: template
});
return Component;
});

View File

@ -1,8 +0,0 @@
'use strict';
define([
'socketio', 'dataio'
], function(socketio, dataio) {
// Do it because we use connect in console store
return dataio(socketio.connect());
});

View File

@ -1,11 +0,0 @@
'use strict';
define(['app/connect'], function(connect) {
var projects = connect.resource('projects');
var builds = connect.resource('builds');
return {
projects: projects,
builds: builds
}
});

View File

@ -1,35 +0,0 @@
'use strict';
define([
'underscore',
'reflux', 'app/actions/build', 'app/resources'
], function(_, Reflux, BuildActions, resources) {
var resource = resources.builds;
var Store = Reflux.createStore({
listenables: BuildActions,
build: null,
onChange: function(data, action) {
if (this.build && (data.buildId === this.build.id)) {
_(this.build).extend(data.changes);
this.trigger(this.build);
}
},
init: function() {
resource.subscribe('change', this.onChange);
},
onRead: function(id) {
var self = this;
resource.sync('read', {id: id}, function(err, build) {
if (err) throw err;
self.build = build;
self.trigger(self.build);
});
}
});
return Store;
});

View File

@ -1,46 +0,0 @@
'use strict';
define([
'reflux', 'app/actions/buildLog', 'app/resources'
], function(
Reflux, BuildLogActions, resources
) {
var resource = resources.builds;
var Store = Reflux.createStore({
listenables: BuildLogActions,
data: {
lines: [],
total: 0
},
getInitialState: function() {
return this.data;
},
onGetTail: function(params) {
var self = this;
console.time('>>> getBuildLogTail');
resource.sync('getBuildLogTail', params, function(err, data) {
if (err) throw err;
console.timeEnd('>>> getBuildLogTail');
self.data = data;
self.trigger(self.data);
});
},
onGetLines: function(params) {
var self = this;
console.time('>>> getBuildLogLines');
resource.sync('getBuildLogLines', params, function(err, data) {
if (err) throw err;
console.timeEnd('>>> getBuildLogLines');
console.log('>>> isLast log lines = ', data.isLast);
self.data.lines = data.lines;
self.trigger(self.data);
});
}
});
return Store;
});

View File

@ -1,66 +0,0 @@
'use strict';
define([
'underscore',
'reflux', 'app/actions/build', 'app/resources'
], function(_, Reflux, BuildActions, resources) {
var resource = resources.builds;
var Store = Reflux.createStore({
listenables: BuildActions,
builds: [],
getInitialState: function() {
return this.builds;
},
onChanged: function(data) {
var oldBuild = _(this.builds).findWhere({id: data.buildId});
if (oldBuild) {
_(oldBuild).extend(data.changes);
} else {
this.builds.unshift(
_({id: data.buildId}).extend(data.changes)
);
}
this.trigger(this.builds);
},
onCancelled: function(data) {
// WORKAROUND: client that trigger `onCancel` gets one `onCancelled`
// call other clients get 2 calls (second with empty data)
if (!data) {
return;
}
var index = _(this.builds).findIndex({id: data.buildId});
if (index !== -1) {
this.builds.splice(index, 1);
}
this.trigger(this.builds);
},
init: function() {
resource.subscribe('change', this.onChanged);
resource.subscribe('cancel', this.onCancelled);
},
onReadAll: function(params) {
var self = this;
resource.sync('readAll', params, function(err, builds) {
if (err) throw err;
self.builds = builds;
self.trigger(self.builds);
});
},
onCancel: function(buildId) {
resource.sync('cancel', {buildId: buildId}, function(err) {
if (err) throw err;
});
}
});
return Store;
});

View File

@ -1,36 +0,0 @@
'use strict';
define([
'underscore',
'reflux', 'app/actions/project', 'app/resources'
], function(_, Reflux, ProjectActions, resources) {
var resource = resources.projects;
var Store = Reflux.createStore({
listenables: ProjectActions,
project: {},
getInitialState: function() {
return this.project;
},
onChange: function(data, action) {
this.trigger(data.project);
},
init: function() {
resource.subscribe('change', this.onChange);
},
onRead: function(params) {
var self = this;
resource.sync('read', params, function(err, project) {
if (err) throw err;
self.project = project;
self.trigger(self.project);
});
}
});
return Store;
});

View File

@ -1,25 +0,0 @@
'use strict';
define([
'reflux', 'app/actions/project', 'app/resources'
], function(Reflux, ProjectActions, resources) {
var resource = resources.projects;
var Store = Reflux.createStore({
listenables: ProjectActions,
onRun: function(projectName) {
resource.sync('run', {projectName: projectName}, function(err) {
if (err) throw err;
});
},
onReadAll: function(params) {
var self = this;
resource.sync('readAll', params, function(err, projects) {
if (err) throw err;
self.trigger(projects);
});
}
});
return Store;
});

View File

@ -1,65 +0,0 @@
'use strict';
define([
'underscore', 'reflux', 'app/actions/build', 'app/connect'
], function(
_, Reflux, BuildActions, connect
) {
var Store = Reflux.createStore({
listenables: BuildActions,
init: function() {
// the only purpose of this hash to reconnect all the time
// except first, see notes at using
this.connectedResourcesHash = {};
},
onReadTerminalOutput: function(build) {
var self = this,
output = [],
resourceName = 'build' + build.id;
var connectToBuildDataResource = function() {
// reconnect for get data below (at subscribe), coz
// data emitted only once during connect
if (self.connectedResourcesHash[resourceName]) {
connect.resource(resourceName).reconnect();
} else {
self.connectedResourcesHash[resourceName] = 1;
}
connect.resource(resourceName).subscribe('data', function(data) {
var lastLine = _(self.lines).last();
if (lastLine && (_(data.lines).first().number === lastLine.number)) {
self.lines = _(self.lines).initial();
}
self.lines = self.lines.concat(data.lines);
self.trigger({
buildId: build.id,
name: 'Console for build #' + build.id,
data: _(self.lines).pluck('text')
});
});
};
this.lines = [];
this.currentLine = '';
// create data resource for completed build
if (build.completed) {
connect.resource('projects').sync(
'createBuildDataResource',
{buildId: build.id},
function(err) {
if (err) throw err;
connectToBuildDataResource();
}
);
} else {
connectToBuildDataResource();
}
}
});
return Store;
});

View File

@ -1,9 +0,0 @@
'use strict';
define(['underscore', 'shared/utils'], function(_, sharedUtils) {
var utils = {};
_(utils).extend(sharedUtils);
return utils;
});

34
transforms/jade.js Normal file
View File

@ -0,0 +1,34 @@
'use strict';
var through = require('through'),
jade = require('react-jade');
module.exports = function(fileName, options) {
if (!/\.jade$/i.test(fileName)) {
return through();
}
var template = '';
return through(
function(chunk) {
template += chunk.toString();
},
function() {
options.filename = fileName;
options.globalReact = true;
try {
template = jade.compileClient(template, options);
} catch (e) {
this.emit('error', e);
return;
}
var moduleBody = 'var React = require("react");\n' +
'module.exports = ' + template;
this.queue(moduleBody);
this.queue(null);
}
);
};

View File

@ -5,23 +5,27 @@ html
link(href="/css/index.css", rel="stylesheet", type="text/css")
if env === 'development'
script(type="text/javascript", src="/js/libs/requirejs/require.js")
else
script
include ../static/js/libs/almond/almond.js
if env === 'development'
script
include ../static/js/requirejs/development.js
else
script
include ../static/js/requirejs/production.js
script
include ../static/js/app.build.js
//-if env === 'development'
//-script(type="text/javascript", src="/js/libs/requirejs/require.js")
//-else
//-script
//-include ../static/js/libs/almond/almond.js
script(type="text/javascript").
require(['app/app']);
//-if env === 'development'
//-script
//-include ../static/js/requirejs/development.js
//-else
//-script
//-include ../static/js/requirejs/production.js
//-script
//-include ../static/js/app.build.js
//-script(type="text/javascript").
//-require(['app/app']);
body
#content
script
include ../static/js/app.build.js