mirror of
https://gitlab.silvrtree.co.uk/martind2000/nci.git
synced 2025-02-10 17:59:16 +00:00
move to browserify
This commit is contained in:
parent
a042876009
commit
5879a7caf3
5
.jshintignore
Normal file
5
.jshintignore
Normal file
@ -0,0 +1,5 @@
|
||||
static/scripts/**
|
||||
static/js/**
|
||||
node_modules/**
|
||||
lib/node/jake/**
|
||||
docs/build_docs/**
|
33
.jshintrc
Normal file
33
.jshintrc
Normal 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
10
app/actions/build.js
Normal 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
9
app/actions/buildLog.js
Normal file
@ -0,0 +1,9 @@
|
||||
'use strict';
|
||||
|
||||
var Reflux = require('reflux');
|
||||
|
||||
module.exports = Reflux.createActions([
|
||||
'getTail',
|
||||
'getLines'
|
||||
]);
|
||||
|
9
app/actions/project.js
Normal file
9
app/actions/project.js
Normal file
@ -0,0 +1,9 @@
|
||||
'use strict';
|
||||
|
||||
var Reflux = require('reflux');
|
||||
|
||||
module.exports = Reflux.createActions([
|
||||
'run',
|
||||
'readAll',
|
||||
'read'
|
||||
]);
|
44
app/app.js
Normal file
44
app/app.js
Normal 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')
|
||||
);
|
||||
});
|
||||
});
|
||||
|
5
app/components/app/index.jade
Normal file
5
app/components/app/index.jade
Normal file
@ -0,0 +1,5 @@
|
||||
div
|
||||
Header()
|
||||
.container-fluid
|
||||
.page-wrapper
|
||||
RouteHandler()
|
23
app/components/app/index.js
Normal file
23
app/components/app/index.js
Normal 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;
|
94
app/components/builds/item/index.jade
Normal file
94
app/components/builds/item/index.jade
Normal 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
|
||||
|
||||
|
25
app/components/builds/item/index.js
Normal file
25
app/components/builds/item/index.js
Normal 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;
|
7
app/components/builds/list/index.jade
Normal file
7
app/components/builds/list/index.jade
Normal 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)
|
||||
|
28
app/components/builds/list/index.js
Normal file
28
app/components/builds/list/index.js
Normal 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;
|
13
app/components/common/dateTime/index.js
Normal file
13
app/components/common/dateTime/index.js
Normal 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;
|
13
app/components/common/duration/index.js
Normal file
13
app/components/common/duration/index.js
Normal 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;
|
13
app/components/common/index.js
Normal file
13
app/components/common/index.js
Normal 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
|
||||
};
|
32
app/components/common/progress/index.js
Normal file
32
app/components/common/progress/index.js
Normal 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()
|
||||
};
|
||||
}
|
||||
});
|
9
app/components/common/scm/index.js
Normal file
9
app/components/common/scm/index.js
Normal file
@ -0,0 +1,9 @@
|
||||
'use strict';
|
||||
|
||||
var React = require('react'),
|
||||
template = require('./index.jade');
|
||||
|
||||
module.exports = React.createClass({
|
||||
render: template
|
||||
});
|
||||
|
5
app/components/dashboard/index.jade
Normal file
5
app/components/dashboard/index.jade
Normal file
@ -0,0 +1,5 @@
|
||||
.main-row
|
||||
.row
|
||||
.col-md-8.col-sm-12
|
||||
h2 Builds history
|
||||
BuildsList()
|
19
app/components/dashboard/index.js
Normal file
19
app/components/dashboard/index.js
Normal 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
|
||||
})
|
||||
});
|
15
app/components/header/index.js
Normal file
15
app/components/header/index.js
Normal 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;
|
24
app/components/projects-selector/index.jade
Normal file
24
app/components/projects-selector/index.jade
Normal 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
|
||||
|
||||
|
54
app/components/projects-selector/index.js
Normal file
54
app/components/projects-selector/index.js
Normal 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
9
app/connect.js
Normal 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
10
app/resources.js
Normal 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
34
app/stores/build.js
Normal 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
43
app/stores/buildLog.js
Normal 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
66
app/stores/builds.js
Normal 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
35
app/stores/project.js
Normal 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
23
app/stores/projects.js
Normal 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
64
app/stores/terminal.js
Normal 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
16
app/utils.js
Normal 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;
|
12
package.json
12
package.json
@ -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",
|
||||
|
@ -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";
|
||||
|
||||
|
36401
static/js/app.build.js
36401
static/js/app.build.js
File diff suppressed because one or more lines are too long
@ -1,12 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
define(['reflux'], function(Reflux) {
|
||||
var Actions = Reflux.createActions([
|
||||
'cancel',
|
||||
'readTerminalOutput',
|
||||
'readAll',
|
||||
'read'
|
||||
]);
|
||||
|
||||
return Actions;
|
||||
});
|
@ -1,10 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
define(['reflux'], function(Reflux) {
|
||||
var Actions = Reflux.createActions([
|
||||
'getTail',
|
||||
'getLines'
|
||||
]);
|
||||
|
||||
return Actions;
|
||||
});
|
@ -1,11 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
define(['reflux'], function(Reflux) {
|
||||
var Actions = Reflux.createActions([
|
||||
'run',
|
||||
'readAll',
|
||||
'read'
|
||||
]);
|
||||
|
||||
return Actions;
|
||||
});
|
@ -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;
|
||||
});
|
@ -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;
|
||||
});
|
@ -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
|
||||
};
|
||||
});
|
@ -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()
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
@ -1,10 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
define([
|
||||
'react', 'templates/app/components/common/scm/index'
|
||||
], function(React, template) {
|
||||
return React.createClass({
|
||||
render: template
|
||||
});
|
||||
});
|
||||
|
@ -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;
|
||||
});
|
@ -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());
|
||||
});
|
@ -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
|
||||
}
|
||||
});
|
@ -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;
|
||||
});
|
@ -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;
|
||||
});
|
@ -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;
|
||||
});
|
@ -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;
|
||||
});
|
@ -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;
|
||||
});
|
@ -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;
|
||||
});
|
@ -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
34
transforms/jade.js
Normal 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);
|
||||
}
|
||||
);
|
||||
};
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user