mirror of
git://git.yoctoproject.org/yocto-autobuilder2.git
synced 2025-07-19 20:59:02 +02:00
yocto_console_view: Convert to new plugin standards to match upstream buildbot
Our plugin is based on upstream buildbot's console plugin. Upstream moved away from coffescript and made a number of other fixes and improvements. Run decaffeinate on the coffeescript to translate to angularjs and then convert to the new build environment and standards. Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
This commit is contained in:
parent
8245de5e6e
commit
36ae2de715
|
@ -1,28 +0,0 @@
|
||||||
### ###############################################################################################
|
|
||||||
#
|
|
||||||
# This module contains all configuration for the build process
|
|
||||||
#
|
|
||||||
### ###############################################################################################
|
|
||||||
ANGULAR_TAG = "~1.5.3"
|
|
||||||
module.exports =
|
|
||||||
|
|
||||||
### ###########################################################################################
|
|
||||||
# Name of the plugin
|
|
||||||
### ###########################################################################################
|
|
||||||
name: 'yocto_console_view'
|
|
||||||
dir: build: 'yocto_console_view/static'
|
|
||||||
bower:
|
|
||||||
testdeps:
|
|
||||||
"guanlecoja-ui":
|
|
||||||
version: '~1.6.0'
|
|
||||||
files: ['vendors.js', 'scripts.js']
|
|
||||||
"angular-mocks":
|
|
||||||
version: ANGULAR_TAG
|
|
||||||
files: "angular-mocks.js"
|
|
||||||
'buildbot-data':
|
|
||||||
version: '~2.1.0'
|
|
||||||
files: 'dist/buildbot-data.js'
|
|
||||||
|
|
||||||
karma:
|
|
||||||
# we put tests first, so that we have angular, and fake app defined
|
|
||||||
files: ["tests.js", "scripts.js", 'fixtures.js']
|
|
|
@ -1 +0,0 @@
|
||||||
require("guanlecoja")(require("gulp"))
|
|
|
@ -1,11 +1,25 @@
|
||||||
{
|
{
|
||||||
"name": "yocto-console-view",
|
"name": "yocto-console-view",
|
||||||
"engines": {
|
"plugin_name": "console_view",
|
||||||
"node": ">=0.10.0",
|
"private": true,
|
||||||
"npm": ">=1.4.0"
|
"main": "yocto_console_view/static/scripts.js",
|
||||||
|
"style": "yocto_console_view/static/styles.js",
|
||||||
|
"scripts": {
|
||||||
|
"build": "rimraf yocto_console_view/static && webpack --bail --progress --profile --env prod",
|
||||||
|
"build-dev": "rimraf yocto_console_view/static && webpack --bail --progress --profile --env dev",
|
||||||
|
"dev": "webpack --bail --progress --profile --watch --env dev"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"angular-mocks": "^1.7.9",
|
||||||
|
"buildbot-build-common": ">0.1",
|
||||||
|
"lodash": "^4.17.11",
|
||||||
|
"rimraf": "^2.6.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"guanlecoja": "~0.8.3",
|
"@uirouter/angularjs": "^1.0.15",
|
||||||
"gulp": "3.9.0"
|
"angular": "^1.7.9",
|
||||||
|
"angular-animate": "^1.7.9",
|
||||||
|
"buildbot-data-js": ">0.1",
|
||||||
|
"guanlecoja-ui": ">0.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
[bdist_wheel]
|
|
||||||
universal=1
|
|
|
@ -15,10 +15,6 @@
|
||||||
#
|
#
|
||||||
# Copyright Buildbot Team Members
|
# Copyright Buildbot Team Members
|
||||||
|
|
||||||
from __future__ import absolute_import
|
|
||||||
from __future__ import division
|
|
||||||
from __future__ import print_function
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from buildbot_pkg import setup_www_plugin
|
from buildbot_pkg import setup_www_plugin
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
@ -32,7 +28,6 @@ setup_www_plugin(
|
||||||
author=u'Richard Purdie',
|
author=u'Richard Purdie',
|
||||||
author_email=u'richard.purdie@linuxfoundation.org',
|
author_email=u'richard.purdie@linuxfoundation.org',
|
||||||
url='http://autobuilder.yoctoproject.org/',
|
url='http://autobuilder.yoctoproject.org/',
|
||||||
license='MIT',
|
|
||||||
packages=['yocto_console_view'],
|
packages=['yocto_console_view'],
|
||||||
package_data={
|
package_data={
|
||||||
'': [
|
'': [
|
||||||
|
@ -44,4 +39,7 @@ setup_www_plugin(
|
||||||
[buildbot.www]
|
[buildbot.www]
|
||||||
console_view = yocto_console_view:ep
|
console_view = yocto_console_view:ep
|
||||||
""",
|
""",
|
||||||
|
classifiers=[
|
||||||
|
'License :: OSI Approved :: GNU General Public License v2 (GPLv2)'
|
||||||
|
],
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,441 +0,0 @@
|
||||||
# Register new module
|
|
||||||
class App extends App
|
|
||||||
constructor: ->
|
|
||||||
return [
|
|
||||||
'ui.router'
|
|
||||||
'ui.bootstrap'
|
|
||||||
'ngAnimate'
|
|
||||||
'guanlecoja.ui'
|
|
||||||
'bbData'
|
|
||||||
]
|
|
||||||
|
|
||||||
class State extends Config
|
|
||||||
constructor: ($stateProvider, glMenuServiceProvider, bbSettingsServiceProvider) ->
|
|
||||||
|
|
||||||
# Name of the state
|
|
||||||
name = 'console'
|
|
||||||
|
|
||||||
# Menu configuration
|
|
||||||
glMenuServiceProvider.addGroup
|
|
||||||
name: name
|
|
||||||
caption: 'Yocto Console View'
|
|
||||||
icon: 'exclamation-circle'
|
|
||||||
order: 5
|
|
||||||
|
|
||||||
# Configuration
|
|
||||||
cfg =
|
|
||||||
group: name
|
|
||||||
caption: 'Yocto Console View'
|
|
||||||
|
|
||||||
# Register new state
|
|
||||||
state =
|
|
||||||
controller: "#{name}Controller"
|
|
||||||
controllerAs: "c"
|
|
||||||
templateUrl: "yocto_console_view/views/#{name}.html"
|
|
||||||
name: name
|
|
||||||
url: "/#{name}"
|
|
||||||
data: cfg
|
|
||||||
|
|
||||||
$stateProvider.state(state)
|
|
||||||
|
|
||||||
bbSettingsServiceProvider.addSettingsGroup
|
|
||||||
name: 'Console'
|
|
||||||
caption: 'Console related settings'
|
|
||||||
items: [
|
|
||||||
type: 'integer'
|
|
||||||
name: 'buildLimit'
|
|
||||||
caption: 'Number of builds to fetch'
|
|
||||||
default_value: 200
|
|
||||||
,
|
|
||||||
type: 'integer'
|
|
||||||
name: 'changeLimit'
|
|
||||||
caption: 'Number of changes to fetch'
|
|
||||||
default_value: 30
|
|
||||||
]
|
|
||||||
|
|
||||||
class Console extends Controller
|
|
||||||
constructor: (@$scope, $q, @$window, dataService, bbSettingsService, resultsService,
|
|
||||||
@$uibModal, @$timeout) ->
|
|
||||||
angular.extend this, resultsService
|
|
||||||
settings = bbSettingsService.getSettingsGroup('Console')
|
|
||||||
@buildLimit = settings.buildLimit.value
|
|
||||||
@changeLimit = settings.changeLimit.value
|
|
||||||
@dataAccessor = dataService.open().closeOnDestroy(@$scope)
|
|
||||||
@_infoIsExpanded = {}
|
|
||||||
@$scope.all_builders = @all_builders = @dataAccessor.getBuilders()
|
|
||||||
@$scope.builders = @builders = []
|
|
||||||
if Intl?
|
|
||||||
collator = new Intl.Collator(undefined, {numeric: true, sensitivity: 'base'})
|
|
||||||
@strcompare = collator.compare
|
|
||||||
else
|
|
||||||
@strcompare = (a, b) ->
|
|
||||||
if a < b
|
|
||||||
return -1
|
|
||||||
if a == b
|
|
||||||
return 0
|
|
||||||
return 1
|
|
||||||
|
|
||||||
@$scope.revmapping = @revmapping = {}
|
|
||||||
@$scope.branchmapping = @branchmapping = {}
|
|
||||||
|
|
||||||
@$scope.builds = @builds = @dataAccessor.getBuilds
|
|
||||||
property: ["yp_build_revision", "yp_build_branch", "reason"]
|
|
||||||
limit: @buildLimit
|
|
||||||
order: '-started_at'
|
|
||||||
@changes = @dataAccessor.getChanges({limit: @changeLimit, order: '-changeid'})
|
|
||||||
@buildrequests = @dataAccessor.getBuildrequests({limit: @buildLimit, order: '-submitted_at'})
|
|
||||||
@buildsets = @dataAccessor.getBuildsets({limit: @buildLimit, order: '-submitted_at'})
|
|
||||||
|
|
||||||
@builds.onChange = @changes.onChange = @buildrequests.onChange = @buildsets.onChange = @onChange
|
|
||||||
|
|
||||||
@builds.onNew = (build) =>
|
|
||||||
change = false
|
|
||||||
buildid = build.buildid
|
|
||||||
if build.properties?.yp_build_revision?
|
|
||||||
@revmapping[build.buildid] = build.properties.yp_build_revision[0]
|
|
||||||
change = true
|
|
||||||
if build.properties?.yp_build_branch?
|
|
||||||
@branchmapping[build.buildid] = build.properties.yp_build_branch[0]
|
|
||||||
change = true
|
|
||||||
if (! @revmapping[buildid] || ! @branchmapping[buildid]) && ! build.complete_at
|
|
||||||
build.getProperties().onChange = (properties) =>
|
|
||||||
change = false
|
|
||||||
buildid = properties.endpoint.split('/')[1]
|
|
||||||
if ! @revmapping[buildid]
|
|
||||||
rev = @getBuildProperty(properties[0], 'yp_build_revision')
|
|
||||||
if rev?
|
|
||||||
@revmapping[buildid] = rev
|
|
||||||
change = true
|
|
||||||
if ! @branchmapping[buildid]
|
|
||||||
branch = @getBuildProperty(properties[0], 'yp_build_branch')
|
|
||||||
if branch?
|
|
||||||
@branchmapping[buildid] = branch
|
|
||||||
change = true
|
|
||||||
if change and not @onchange_debounce?
|
|
||||||
@onchange_debounce = @$timeout(@_onChange, 100)
|
|
||||||
if change and not @onchange_debounce?
|
|
||||||
@onchange_debounce = @$timeout(@_onChange, 100)
|
|
||||||
|
|
||||||
getBuildProperty: (properties, property) ->
|
|
||||||
hasProperty = properties && properties.hasOwnProperty(property)
|
|
||||||
return if hasProperty then properties[property][0] else null
|
|
||||||
|
|
||||||
onChange: (s) =>
|
|
||||||
# if there is no data, no need to try and build something.
|
|
||||||
if @builds.length == 0 or @all_builders.length == 0 or not @changes.$resolved or
|
|
||||||
@buildsets.length == 0 or @buildrequests == 0
|
|
||||||
return
|
|
||||||
if not @onchange_debounce?
|
|
||||||
@onchange_debounce = @$timeout(@_onChange, 100)
|
|
||||||
|
|
||||||
_onChange: =>
|
|
||||||
@onchange_debounce = undefined
|
|
||||||
# we only display builders who actually have builds
|
|
||||||
for build in @builds
|
|
||||||
@all_builders.get(build.builderid).hasBuild = true
|
|
||||||
|
|
||||||
@sortBuildersByTags(@all_builders)
|
|
||||||
|
|
||||||
@changesBySSID = {}
|
|
||||||
@changesByRevision = {}
|
|
||||||
for change in @changes
|
|
||||||
@changesBySSID[change.sourcestamp.ssid] = change
|
|
||||||
@changesByRevision[change.revision] = change
|
|
||||||
@populateChange(change)
|
|
||||||
|
|
||||||
|
|
||||||
for build in @builds
|
|
||||||
@matchBuildWithChange(build)
|
|
||||||
|
|
||||||
@filtered_changes = []
|
|
||||||
for ssid, change of @changesBySSID
|
|
||||||
if change.comments
|
|
||||||
change.subject = change.comments.split("\n")[0]
|
|
||||||
for builder in change.builders
|
|
||||||
if builder.builds.length > 0
|
|
||||||
@filtered_changes.push(change)
|
|
||||||
break
|
|
||||||
###
|
|
||||||
# Sort builders by tags
|
|
||||||
# Buildbot eight has the category option, but it was only limited to one category per builder,
|
|
||||||
# which make it easy to sort by category
|
|
||||||
# Here, we have multiple tags per builder, we need to try to group builders with same tags together
|
|
||||||
# The algorithm is rather twisted. It is a first try at the concept of grouping builders by tags..
|
|
||||||
###
|
|
||||||
|
|
||||||
sortBuildersByTags: (all_builders) ->
|
|
||||||
# first we only want builders with builds
|
|
||||||
builders_with_builds = []
|
|
||||||
builderids_with_builds = ""
|
|
||||||
for builder in all_builders
|
|
||||||
if builder.hasBuild
|
|
||||||
builders_with_builds.push(builder)
|
|
||||||
builderids_with_builds += "." + builder.builderid
|
|
||||||
|
|
||||||
if builderids_with_builds == @last_builderids_with_builds
|
|
||||||
# don't recalculate if it hasn't changed!
|
|
||||||
return
|
|
||||||
# we call recursive function, which finds non-overlapping groups
|
|
||||||
tag_line = @_sortBuildersByTags(builders_with_builds)
|
|
||||||
# we get a tree of builders grouped by tags
|
|
||||||
# we now need to flatten the tree, in order to build several lines of tags
|
|
||||||
# (each line is representing a depth in the tag tree)
|
|
||||||
# we walk the tree left to right and build the list of builders in the tree order, and the tag_lines
|
|
||||||
# in the tree, there are groups of remaining builders, which could not be grouped together,
|
|
||||||
# those have the empty tag ''
|
|
||||||
tag_lines = []
|
|
||||||
|
|
||||||
sorted_builders = []
|
|
||||||
set_tag_line = (depth, tag, colspan) ->
|
|
||||||
# we build the tag lines by using a sparse array
|
|
||||||
_tag_line = tag_lines[depth]
|
|
||||||
if not _tag_line?
|
|
||||||
# initialize the sparse array
|
|
||||||
_tag_line = tag_lines[depth] = []
|
|
||||||
else
|
|
||||||
# if we were already initialized, look at the last tag if this is the same
|
|
||||||
# we merge the two entries
|
|
||||||
last_tag = _tag_line[_tag_line.length - 1]
|
|
||||||
if last_tag.tag == tag
|
|
||||||
last_tag.colspan += colspan
|
|
||||||
return
|
|
||||||
_tag_line.push(tag: tag, colspan: colspan)
|
|
||||||
self = @
|
|
||||||
# recursive tree walking
|
|
||||||
walk_tree = (tag, depth) ->
|
|
||||||
set_tag_line(depth, tag.tag, tag.builders.length)
|
|
||||||
if not tag.tag_line? or tag.tag_line.length == 0
|
|
||||||
# this is the leaf of the tree, sort by buildername, and add them to the
|
|
||||||
# list of sorted builders
|
|
||||||
tag.builders.sort (a, b) -> self.strcompare(a.name, b.name)
|
|
||||||
sorted_builders = sorted_builders.concat(tag.builders)
|
|
||||||
for i in [1..100] # set the remaining depth of the tree to the same colspan
|
|
||||||
# (we hardcode the maximum depth for now :/ )
|
|
||||||
set_tag_line(depth + i, '', tag.builders.length)
|
|
||||||
return
|
|
||||||
for _tag in tag.tag_line
|
|
||||||
walk_tree(_tag, depth + 1)
|
|
||||||
|
|
||||||
for tag in tag_line
|
|
||||||
walk_tree(tag, 0)
|
|
||||||
|
|
||||||
@builders = sorted_builders
|
|
||||||
@tag_lines = []
|
|
||||||
# make a new array to avoid it to be sparse, and to remove lines filled with null tags
|
|
||||||
for tag_line in tag_lines
|
|
||||||
if not (tag_line.length == 1 and tag_line[0].tag == "")
|
|
||||||
@tag_lines.push(tag_line)
|
|
||||||
@last_builderids_with_builds = builderids_with_builds
|
|
||||||
###
|
|
||||||
# recursive function which sorts the builders by tags
|
|
||||||
# call recursively with groups of builders smaller and smaller
|
|
||||||
###
|
|
||||||
_sortBuildersByTags: (all_builders) ->
|
|
||||||
|
|
||||||
# first find out how many builders there is by tags in that group
|
|
||||||
builders_by_tags = {}
|
|
||||||
for builder in all_builders
|
|
||||||
if builder.tags?
|
|
||||||
for tag in builder.tags
|
|
||||||
if not builders_by_tags[tag]?
|
|
||||||
builders_by_tags[tag] = []
|
|
||||||
builders_by_tags[tag].push(builder)
|
|
||||||
tags = []
|
|
||||||
for tag, builders of builders_by_tags
|
|
||||||
# we don't want the tags that are on all the builders
|
|
||||||
if builders.length < all_builders.length
|
|
||||||
tags.push(tag: tag, builders: builders)
|
|
||||||
|
|
||||||
# sort the tags to first look at tags with the larger number of builders
|
|
||||||
# @FIXME maybe this is not the best method to find the best groups
|
|
||||||
tags.sort (a, b) -> b.builders.length - a.builders.length
|
|
||||||
|
|
||||||
tag_line = []
|
|
||||||
chosen_builderids = {}
|
|
||||||
# pick the tags one by one, by making sure we make non-overalaping groups
|
|
||||||
for tag in tags
|
|
||||||
excluded = false
|
|
||||||
for builder in tag.builders
|
|
||||||
if chosen_builderids.hasOwnProperty(builder.builderid)
|
|
||||||
excluded = true
|
|
||||||
break
|
|
||||||
if not excluded
|
|
||||||
for builder in tag.builders
|
|
||||||
chosen_builderids[builder.builderid] = tag.tag
|
|
||||||
tag_line.push(tag)
|
|
||||||
|
|
||||||
# some builders do not have tags, we put them in another group
|
|
||||||
remaining_builders = []
|
|
||||||
for builder in all_builders
|
|
||||||
if not chosen_builderids.hasOwnProperty(builder.builderid)
|
|
||||||
remaining_builders.push(builder)
|
|
||||||
|
|
||||||
if remaining_builders.length
|
|
||||||
tag_line.push(tag: "", builders: remaining_builders)
|
|
||||||
|
|
||||||
# if there is more than one tag in this line, we need to recurse
|
|
||||||
if tag_line.length > 1
|
|
||||||
for tag in tag_line
|
|
||||||
tag.tag_line = @_sortBuildersByTags(tag.builders)
|
|
||||||
return tag_line
|
|
||||||
|
|
||||||
###
|
|
||||||
# fill a change with a list of builders
|
|
||||||
###
|
|
||||||
populateChange: (change) ->
|
|
||||||
change.builders = []
|
|
||||||
change.buildersById = {}
|
|
||||||
for builder in @builders
|
|
||||||
builder = builderid: builder.builderid, name: builder.name, builds: []
|
|
||||||
change.builders.push(builder)
|
|
||||||
change.buildersById[builder.builderid] = builder
|
|
||||||
###
|
|
||||||
# Match builds with a change
|
|
||||||
###
|
|
||||||
matchBuildWithChange: (build) =>
|
|
||||||
buildrequest = @buildrequests.get(build.buildrequestid)
|
|
||||||
if not buildrequest?
|
|
||||||
return
|
|
||||||
buildset = @buildsets.get(buildrequest.buildsetid)
|
|
||||||
if not buildset?
|
|
||||||
return
|
|
||||||
if buildset? and buildset.sourcestamps?
|
|
||||||
for sourcestamp in buildset.sourcestamps
|
|
||||||
change = @changesBySSID[sourcestamp.ssid]
|
|
||||||
|
|
||||||
if build.properties?.yp_build_revision? or @revmapping[build.buildid]
|
|
||||||
if build.properties?.yp_build_revision?
|
|
||||||
rev = build.properties.yp_build_revision[0]
|
|
||||||
else
|
|
||||||
rev = @revmapping[build.buildid]
|
|
||||||
change = @changesByRevision[rev]
|
|
||||||
if not change?
|
|
||||||
change = @changesBySSID[rev]
|
|
||||||
if not change?
|
|
||||||
change = @makeFakeChange(rev, build.started_at, rev)
|
|
||||||
if buildset? and buildset.parent_buildid?
|
|
||||||
oldrev = "Unresolved #{buildset.parent_buildid}"
|
|
||||||
delete @changesBySSID[oldrev]
|
|
||||||
oldrev = "Unresolved #{build.builderid}-#{build.buildid}"
|
|
||||||
delete @changesBySSID[oldrev]
|
|
||||||
|
|
||||||
change.caption = "Commit"
|
|
||||||
if build.properties?.yp_build_branch?
|
|
||||||
change.caption = build.properties.yp_build_branch[0]
|
|
||||||
if @branchmapping[build.buildid]
|
|
||||||
change.caption = @branchmapping[build.buildid]
|
|
||||||
change.revlink = "http://git.yoctoproject.org/cgit.cgi/poky/commit/?id=" + rev
|
|
||||||
change.errorlink = "http://errors.yoctoproject.org/Errors/Latest/?filter=" + rev + "&type=commit&limit=150"
|
|
||||||
bid = build.buildid
|
|
||||||
if buildset? and buildset.parent_buildid?
|
|
||||||
bid = buildset.parent_buildid
|
|
||||||
if (change.bid? and bid > change.bid) or !change.bid?
|
|
||||||
change.bid = bid
|
|
||||||
change.loglink = "https://wiki.yoctoproject.org/wiki/BuildLog#" + bid
|
|
||||||
if build.properties?.reason?
|
|
||||||
change.reason = build.properties.reason[0]
|
|
||||||
else
|
|
||||||
if buildset? and buildset.parent_buildid?
|
|
||||||
rev = "Unresolved #{buildset.parent_buildid}"
|
|
||||||
if not change?
|
|
||||||
change = @changesBySSID[rev]
|
|
||||||
if not change?
|
|
||||||
oldrev = "Unresolved #{build.builderid}-#{build.buildid}"
|
|
||||||
delete @changesBySSID[oldrev]
|
|
||||||
change = @makeFakeChange(rev, build.started_at, rev)
|
|
||||||
if not change?
|
|
||||||
rev = "Unresolved #{build.builderid}-#{build.buildid}"
|
|
||||||
if not change?
|
|
||||||
change = @changesBySSID[rev]
|
|
||||||
if not change?
|
|
||||||
change = @makeFakeChange(rev, build.started_at, rev)
|
|
||||||
change.caption = rev
|
|
||||||
|
|
||||||
change.buildersById[build.builderid].builds.push(build)
|
|
||||||
|
|
||||||
makeFakeChange: (revision, when_timestamp, comments) =>
|
|
||||||
change =
|
|
||||||
revision: revision
|
|
||||||
changeid: revision
|
|
||||||
when_timestamp: when_timestamp
|
|
||||||
comments: comments
|
|
||||||
@changesBySSID[revision] = change
|
|
||||||
@populateChange(change)
|
|
||||||
return change
|
|
||||||
###
|
|
||||||
# Open all change row information
|
|
||||||
###
|
|
||||||
openAll: ->
|
|
||||||
for change in @filtered_changes
|
|
||||||
change.show_details = true
|
|
||||||
|
|
||||||
###
|
|
||||||
# Close all change row information
|
|
||||||
###
|
|
||||||
closeAll: ->
|
|
||||||
for change in @filtered_changes
|
|
||||||
change.show_details = false
|
|
||||||
|
|
||||||
###
|
|
||||||
# Calculate row header (aka first column) width
|
|
||||||
# depending if we display commit comment, we reserve more space
|
|
||||||
###
|
|
||||||
getRowHeaderWidth: ->
|
|
||||||
if @hasExpanded()
|
|
||||||
return 400 # magic value enough to hold 78 characters lines
|
|
||||||
else
|
|
||||||
return 200
|
|
||||||
###
|
|
||||||
# Calculate col header (aka first row) height
|
|
||||||
# It depends on the length of the longest builder
|
|
||||||
###
|
|
||||||
getColHeaderHeight: ->
|
|
||||||
max_buildername = 0
|
|
||||||
for builder in @builders
|
|
||||||
max_buildername = Math.max(builder.name.length, max_buildername)
|
|
||||||
return Math.max(100, max_buildername * 3)
|
|
||||||
|
|
||||||
###
|
|
||||||
#
|
|
||||||
# Determine if we use a 100% width table or if we allow horizontal scrollbar
|
|
||||||
# depending on number of builders, and size of window, we need a fixed column size or a 100% width table
|
|
||||||
#
|
|
||||||
###
|
|
||||||
isBigTable: ->
|
|
||||||
padding = @getRowHeaderWidth()
|
|
||||||
if ((@$window.innerWidth - padding) / @builders.length) < 40
|
|
||||||
return true
|
|
||||||
return false
|
|
||||||
###
|
|
||||||
#
|
|
||||||
# do we have at least one change expanded?
|
|
||||||
#
|
|
||||||
###
|
|
||||||
hasExpanded: ->
|
|
||||||
for change in @changes
|
|
||||||
if @infoIsExpanded(change)
|
|
||||||
return true
|
|
||||||
return false
|
|
||||||
|
|
||||||
###
|
|
||||||
#
|
|
||||||
# display build details
|
|
||||||
#
|
|
||||||
###
|
|
||||||
selectBuild: (build) ->
|
|
||||||
modal = @$uibModal.open
|
|
||||||
templateUrl: 'yocto_console_view/views/modal.html'
|
|
||||||
controller: 'consoleModalController as modal'
|
|
||||||
windowClass: 'modal-big'
|
|
||||||
resolve:
|
|
||||||
selectedBuild: -> build
|
|
||||||
|
|
||||||
###
|
|
||||||
#
|
|
||||||
# toggle display of additional info for that change
|
|
||||||
#
|
|
||||||
###
|
|
||||||
toggleInfo: (change) ->
|
|
||||||
change.show_details = !change.show_details
|
|
||||||
infoIsExpanded: (change) ->
|
|
||||||
return change.show_details
|
|
593
yocto_console_view/src/module/main.module.js
Normal file
593
yocto_console_view/src/module/main.module.js
Normal file
|
@ -0,0 +1,593 @@
|
||||||
|
/*
|
||||||
|
* decaffeinate suggestions:
|
||||||
|
* DS101: Remove unnecessary use of Array.from
|
||||||
|
* DS102: Remove unnecessary code created because of implicit returns
|
||||||
|
* DS205: Consider reworking code to avoid use of IIFEs
|
||||||
|
* DS207: Consider shorter variations of null checks
|
||||||
|
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||||
|
*/
|
||||||
|
|
||||||
|
import 'angular-animate';
|
||||||
|
import '@uirouter/angularjs';
|
||||||
|
import 'guanlecoja-ui';
|
||||||
|
import 'buildbot-data-js';
|
||||||
|
|
||||||
|
class ConsoleState {
|
||||||
|
constructor($stateProvider, glMenuServiceProvider, bbSettingsServiceProvider) {
|
||||||
|
|
||||||
|
// Name of the state
|
||||||
|
const name = 'console';
|
||||||
|
|
||||||
|
// Menu configuration
|
||||||
|
glMenuServiceProvider.addGroup({
|
||||||
|
name,
|
||||||
|
caption: 'Yocto Console View',
|
||||||
|
icon: 'exclamation-circle',
|
||||||
|
order: 5
|
||||||
|
});
|
||||||
|
|
||||||
|
// Configuration
|
||||||
|
const cfg = {
|
||||||
|
group: name,
|
||||||
|
caption: 'Yocto Console View'
|
||||||
|
};
|
||||||
|
|
||||||
|
// Register new state
|
||||||
|
const state = {
|
||||||
|
controller: `${name}Controller`,
|
||||||
|
controllerAs: "c",
|
||||||
|
template: require('./console.tpl.jade'),
|
||||||
|
name,
|
||||||
|
url: `/${name}`,
|
||||||
|
data: cfg
|
||||||
|
};
|
||||||
|
|
||||||
|
$stateProvider.state(state);
|
||||||
|
|
||||||
|
bbSettingsServiceProvider.addSettingsGroup({
|
||||||
|
name: 'Console',
|
||||||
|
caption: 'Console related settings',
|
||||||
|
items: [{
|
||||||
|
type: 'integer',
|
||||||
|
name: 'buildLimit',
|
||||||
|
caption: 'Number of builds to fetch',
|
||||||
|
default_value: 200
|
||||||
|
}
|
||||||
|
, {
|
||||||
|
type: 'integer',
|
||||||
|
name: 'changeLimit',
|
||||||
|
caption: 'Number of changes to fetch',
|
||||||
|
default_value: 30
|
||||||
|
}
|
||||||
|
]});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Console {
|
||||||
|
constructor($scope, $q, $window, dataService, bbSettingsService, resultsService,
|
||||||
|
$uibModal, $timeout) {
|
||||||
|
this.onChange = this.onChange.bind(this);
|
||||||
|
this._onChange = this._onChange.bind(this);
|
||||||
|
this.matchBuildWithChange = this.matchBuildWithChange.bind(this);
|
||||||
|
this.makeFakeChange = this.makeFakeChange.bind(this);
|
||||||
|
this.$scope = $scope;
|
||||||
|
this.$window = $window;
|
||||||
|
this.$uibModal = $uibModal;
|
||||||
|
this.$timeout = $timeout;
|
||||||
|
angular.extend(this, resultsService);
|
||||||
|
const settings = bbSettingsService.getSettingsGroup('Console');
|
||||||
|
this.buildLimit = settings.buildLimit.value;
|
||||||
|
this.changeLimit = settings.changeLimit.value;
|
||||||
|
this.dataAccessor = dataService.open().closeOnDestroy(this.$scope);
|
||||||
|
this._infoIsExpanded = {};
|
||||||
|
this.$scope.all_builders = (this.all_builders = this.dataAccessor.getBuilders());
|
||||||
|
this.$scope.builders = (this.builders = []);
|
||||||
|
if (typeof Intl !== 'undefined' && Intl !== null) {
|
||||||
|
const collator = new Intl.Collator(undefined, {numeric: true, sensitivity: 'base'});
|
||||||
|
this.strcompare = collator.compare;
|
||||||
|
} else {
|
||||||
|
this.strcompare = function(a, b) {
|
||||||
|
if (a < b) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (a === b) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$scope.revmapping = (this.revmapping = {});
|
||||||
|
this.$scope.branchmapping = (this.branchmapping = {});
|
||||||
|
|
||||||
|
this.$scope.builds = (this.builds = this.dataAccessor.getBuilds({
|
||||||
|
property: ["yp_build_revision", "yp_build_branch", "reason"],
|
||||||
|
limit: this.buildLimit,
|
||||||
|
order: '-started_at'
|
||||||
|
}));
|
||||||
|
this.changes = this.dataAccessor.getChanges({limit: this.changeLimit, order: '-changeid'});
|
||||||
|
this.buildrequests = this.dataAccessor.getBuildrequests({limit: this.buildLimit, order: '-submitted_at'});
|
||||||
|
this.buildsets = this.dataAccessor.getBuildsets({limit: this.buildLimit, order: '-submitted_at'});
|
||||||
|
|
||||||
|
this.builds.onChange = (this.changes.onChange = (this.buildrequests.onChange = (this.buildsets.onChange = this.onChange)));
|
||||||
|
|
||||||
|
this.builds.onNew = build => {
|
||||||
|
let change = false;
|
||||||
|
let {
|
||||||
|
buildid
|
||||||
|
} = build;
|
||||||
|
if ((build.properties != null ? build.properties.yp_build_revision : undefined) != null) {
|
||||||
|
this.revmapping[build.buildid] = build.properties.yp_build_revision[0];
|
||||||
|
change = true;
|
||||||
|
}
|
||||||
|
if ((build.properties != null ? build.properties.yp_build_branch : undefined) != null) {
|
||||||
|
this.branchmapping[build.buildid] = build.properties.yp_build_branch[0];
|
||||||
|
change = true;
|
||||||
|
}
|
||||||
|
if ((!this.revmapping[buildid] || !this.branchmapping[buildid]) && !build.complete_at) {
|
||||||
|
build.getProperties().onChange = properties => {
|
||||||
|
change = false;
|
||||||
|
buildid = properties.endpoint.split('/')[1];
|
||||||
|
if (!this.revmapping[buildid]) {
|
||||||
|
const rev = this.getBuildProperty(properties[0], 'yp_build_revision');
|
||||||
|
if (rev != null) {
|
||||||
|
this.revmapping[buildid] = rev;
|
||||||
|
change = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!this.branchmapping[buildid]) {
|
||||||
|
const branch = this.getBuildProperty(properties[0], 'yp_build_branch');
|
||||||
|
if (branch != null) {
|
||||||
|
this.branchmapping[buildid] = branch;
|
||||||
|
}
|
||||||
|
return change = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (change && (this.onchange_debounce == null)) {
|
||||||
|
this.onchange_debounce = this.$timeout(this._onChange, 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (change && (this.onchange_debounce == null)) {
|
||||||
|
return this.onchange_debounce = this.$timeout(this._onChange, 100);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
getBuildProperty(properties, property) {
|
||||||
|
const hasProperty = properties && properties.hasOwnProperty(property);
|
||||||
|
if (hasProperty) { return properties[property][0]; } else { return null; }
|
||||||
|
}
|
||||||
|
|
||||||
|
onChange(s) {
|
||||||
|
// if there is no data, no need to try and build something.
|
||||||
|
if ((this.builds.length === 0) || (this.all_builders.length === 0) || !this.changes.$resolved ||
|
||||||
|
(this.buildsets.length === 0) || (this.buildrequests === 0)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((this.onchange_debounce == null)) {
|
||||||
|
return this.onchange_debounce = this.$timeout(this._onChange, 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_onChange() {
|
||||||
|
let build, change;
|
||||||
|
this.onchange_debounce = undefined;
|
||||||
|
// we only display builders who actually have builds
|
||||||
|
for (build of Array.from(this.builds)) {
|
||||||
|
this.all_builders.get(build.builderid).hasBuild = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.sortBuildersByTags(this.all_builders);
|
||||||
|
|
||||||
|
if (this.changesBySSID == null) { this.changesBySSID = {}; }
|
||||||
|
if (this.changesByRevision == null) { this.changesByRevision = {}; }
|
||||||
|
for (change of Array.from(this.changes)) {
|
||||||
|
this.changesBySSID[change.sourcestamp.ssid] = change;
|
||||||
|
this.changesByRevision[change.revision] = change;
|
||||||
|
this.populateChange(change);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for (build of Array.from(this.builds)) {
|
||||||
|
this.matchBuildWithChange(build);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.filtered_changes = [];
|
||||||
|
return (() => {
|
||||||
|
const result = [];
|
||||||
|
for (let ssid in this.changesBySSID) {
|
||||||
|
change = this.changesBySSID[ssid];
|
||||||
|
if (change.comments) {
|
||||||
|
change.subject = change.comments.split("\n")[0];
|
||||||
|
}
|
||||||
|
result.push((() => {
|
||||||
|
const result1 = [];
|
||||||
|
for (let builder of Array.from(change.builders)) {
|
||||||
|
if (builder.builds.length > 0) {
|
||||||
|
this.filtered_changes.push(change);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
result1.push(undefined);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result1;
|
||||||
|
})());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Sort builders by tags
|
||||||
|
* Buildbot eight has the category option, but it was only limited to one category per builder,
|
||||||
|
* which make it easy to sort by category
|
||||||
|
* Here, we have multiple tags per builder, we need to try to group builders with same tags together
|
||||||
|
* The algorithm is rather twisted. It is a first try at the concept of grouping builders by tags..
|
||||||
|
*/
|
||||||
|
|
||||||
|
sortBuildersByTags(all_builders) {
|
||||||
|
// first we only want builders with builds
|
||||||
|
let tag;
|
||||||
|
const builders_with_builds = [];
|
||||||
|
let builderids_with_builds = "";
|
||||||
|
for (let builder of Array.from(all_builders)) {
|
||||||
|
if (builder.hasBuild) {
|
||||||
|
builders_with_builds.push(builder);
|
||||||
|
builderids_with_builds += "." + builder.builderid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (builderids_with_builds === this.last_builderids_with_builds) {
|
||||||
|
// don't recalculate if it hasn't changed!
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// we call recursive function, which finds non-overlapping groups
|
||||||
|
let tag_line = this._sortBuildersByTags(builders_with_builds);
|
||||||
|
// we get a tree of builders grouped by tags
|
||||||
|
// we now need to flatten the tree, in order to build several lines of tags
|
||||||
|
// (each line is representing a depth in the tag tree)
|
||||||
|
// we walk the tree left to right and build the list of builders in the tree order, and the tag_lines
|
||||||
|
// in the tree, there are groups of remaining builders, which could not be grouped together,
|
||||||
|
// those have the empty tag ''
|
||||||
|
const tag_lines = [];
|
||||||
|
|
||||||
|
let sorted_builders = [];
|
||||||
|
const set_tag_line = function(depth, tag, colspan) {
|
||||||
|
// we build the tag lines by using a sparse array
|
||||||
|
let _tag_line = tag_lines[depth];
|
||||||
|
if ((_tag_line == null)) {
|
||||||
|
// initialize the sparse array
|
||||||
|
_tag_line = (tag_lines[depth] = []);
|
||||||
|
} else {
|
||||||
|
// if we were already initialized, look at the last tag if this is the same
|
||||||
|
// we merge the two entries
|
||||||
|
const last_tag = _tag_line[_tag_line.length - 1];
|
||||||
|
if (last_tag.tag === tag) {
|
||||||
|
last_tag.colspan += colspan;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _tag_line.push({tag, colspan});
|
||||||
|
};
|
||||||
|
const self = this;
|
||||||
|
// recursive tree walking
|
||||||
|
var walk_tree = function(tag, depth) {
|
||||||
|
set_tag_line(depth, tag.tag, tag.builders.length);
|
||||||
|
if ((tag.tag_line == null) || (tag.tag_line.length === 0)) {
|
||||||
|
// this is the leaf of the tree, sort by buildername, and add them to the
|
||||||
|
// list of sorted builders
|
||||||
|
tag.builders.sort((a, b) => self.strcompare(a.name, b.name));
|
||||||
|
sorted_builders = sorted_builders.concat(tag.builders);
|
||||||
|
for (let i = 1; i <= 100; i++) { // set the remaining depth of the tree to the same colspan
|
||||||
|
// (we hardcode the maximum depth for now :/ )
|
||||||
|
set_tag_line(depth + i, '', tag.builders.length);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return Array.from(tag.tag_line).map((_tag) =>
|
||||||
|
walk_tree(_tag, depth + 1));
|
||||||
|
};
|
||||||
|
|
||||||
|
for (tag of Array.from(tag_line)) {
|
||||||
|
walk_tree(tag, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.builders = sorted_builders;
|
||||||
|
this.tag_lines = [];
|
||||||
|
// make a new array to avoid it to be sparse, and to remove lines filled with null tags
|
||||||
|
for (tag_line of Array.from(tag_lines)) {
|
||||||
|
if (!((tag_line.length === 1) && (tag_line[0].tag === ""))) {
|
||||||
|
this.tag_lines.push(tag_line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this.last_builderids_with_builds = builderids_with_builds;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* recursive function which sorts the builders by tags
|
||||||
|
* call recursively with groups of builders smaller and smaller
|
||||||
|
*/
|
||||||
|
_sortBuildersByTags(all_builders) {
|
||||||
|
|
||||||
|
// first find out how many builders there is by tags in that group
|
||||||
|
let builder, builders, tag;
|
||||||
|
const builders_by_tags = {};
|
||||||
|
for (builder of Array.from(all_builders)) {
|
||||||
|
if (builder.tags != null) {
|
||||||
|
for (tag of Array.from(builder.tags)) {
|
||||||
|
if ((builders_by_tags[tag] == null)) {
|
||||||
|
builders_by_tags[tag] = [];
|
||||||
|
}
|
||||||
|
builders_by_tags[tag].push(builder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const tags = [];
|
||||||
|
for (tag in builders_by_tags) {
|
||||||
|
// we don't want the tags that are on all the builders
|
||||||
|
builders = builders_by_tags[tag];
|
||||||
|
if (builders.length < all_builders.length) {
|
||||||
|
tags.push({tag, builders});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// sort the tags to first look at tags with the larger number of builders
|
||||||
|
// @FIXME maybe this is not the best method to find the best groups
|
||||||
|
tags.sort((a, b) => b.builders.length - a.builders.length);
|
||||||
|
|
||||||
|
const tag_line = [];
|
||||||
|
const chosen_builderids = {};
|
||||||
|
// pick the tags one by one, by making sure we make non-overalaping groups
|
||||||
|
for (tag of Array.from(tags)) {
|
||||||
|
let excluded = false;
|
||||||
|
for (builder of Array.from(tag.builders)) {
|
||||||
|
if (chosen_builderids.hasOwnProperty(builder.builderid)) {
|
||||||
|
excluded = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!excluded) {
|
||||||
|
for (builder of Array.from(tag.builders)) {
|
||||||
|
chosen_builderids[builder.builderid] = tag.tag;
|
||||||
|
}
|
||||||
|
tag_line.push(tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// some builders do not have tags, we put them in another group
|
||||||
|
const remaining_builders = [];
|
||||||
|
for (builder of Array.from(all_builders)) {
|
||||||
|
if (!chosen_builderids.hasOwnProperty(builder.builderid)) {
|
||||||
|
remaining_builders.push(builder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (remaining_builders.length) {
|
||||||
|
tag_line.push({tag: "", builders: remaining_builders});
|
||||||
|
}
|
||||||
|
|
||||||
|
// if there is more than one tag in this line, we need to recurse
|
||||||
|
if (tag_line.length > 1) {
|
||||||
|
for (tag of Array.from(tag_line)) {
|
||||||
|
tag.tag_line = this._sortBuildersByTags(tag.builders);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tag_line;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* fill a change with a list of builders
|
||||||
|
*/
|
||||||
|
populateChange(change) {
|
||||||
|
change.builders = [];
|
||||||
|
change.buildersById = {};
|
||||||
|
return (() => {
|
||||||
|
const result = [];
|
||||||
|
for (let builder of Array.from(this.builders)) {
|
||||||
|
builder = {builderid: builder.builderid, name: builder.name, builds: []};
|
||||||
|
change.builders.push(builder);
|
||||||
|
result.push(change.buildersById[builder.builderid] = builder);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Match builds with a change
|
||||||
|
*/
|
||||||
|
matchBuildWithChange(build) {
|
||||||
|
let change, oldrev, rev;
|
||||||
|
const buildrequest = this.buildrequests.get(build.buildrequestid);
|
||||||
|
if ((buildrequest == null)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const buildset = this.buildsets.get(buildrequest.buildsetid);
|
||||||
|
if ((buildset == null)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((buildset != null) && (buildset.sourcestamps != null)) {
|
||||||
|
for (let sourcestamp of Array.from(buildset.sourcestamps)) {
|
||||||
|
change = this.changesBySSID[sourcestamp.ssid];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (((build.properties != null ? build.properties.yp_build_revision : undefined) != null) || this.revmapping[build.buildid]) {
|
||||||
|
if ((build.properties != null ? build.properties.yp_build_revision : undefined) != null) {
|
||||||
|
rev = build.properties.yp_build_revision[0];
|
||||||
|
} else {
|
||||||
|
rev = this.revmapping[build.buildid];
|
||||||
|
}
|
||||||
|
change = this.changesByRevision[rev];
|
||||||
|
if ((change == null)) {
|
||||||
|
change = this.changesBySSID[rev];
|
||||||
|
}
|
||||||
|
if ((change == null)) {
|
||||||
|
change = this.makeFakeChange(rev, build.started_at, rev);
|
||||||
|
}
|
||||||
|
if ((buildset != null) && (buildset.parent_buildid != null)) {
|
||||||
|
oldrev = `Unresolved ${buildset.parent_buildid}`;
|
||||||
|
delete this.changesBySSID[oldrev];
|
||||||
|
}
|
||||||
|
oldrev = `Unresolved ${build.builderid}-${build.buildid}`;
|
||||||
|
delete this.changesBySSID[oldrev];
|
||||||
|
|
||||||
|
change.caption = "Commit";
|
||||||
|
if ((build.properties != null ? build.properties.yp_build_branch : undefined) != null) {
|
||||||
|
change.caption = build.properties.yp_build_branch[0];
|
||||||
|
}
|
||||||
|
if (this.branchmapping[build.buildid]) {
|
||||||
|
change.caption = this.branchmapping[build.buildid];
|
||||||
|
}
|
||||||
|
change.revlink = "http://git.yoctoproject.org/cgit.cgi/poky/commit/?id=" + rev;
|
||||||
|
change.errorlink = "http://errors.yoctoproject.org/Errors/Latest/?filter=" + rev + "&type=commit&limit=150";
|
||||||
|
let bid = build.buildid;
|
||||||
|
if ((buildset != null) && (buildset.parent_buildid != null)) {
|
||||||
|
bid = buildset.parent_buildid;
|
||||||
|
}
|
||||||
|
if (((change.bid != null) && (bid > change.bid)) || (change.bid == null)) {
|
||||||
|
change.bid = bid;
|
||||||
|
change.loglink = "https://wiki.yoctoproject.org/wiki/BuildLog#" + bid;
|
||||||
|
}
|
||||||
|
if ((build.properties != null ? build.properties.reason : undefined) != null) {
|
||||||
|
change.reason = build.properties.reason[0];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ((buildset != null) && (buildset.parent_buildid != null)) {
|
||||||
|
rev = `Unresolved ${buildset.parent_buildid}`;
|
||||||
|
if ((change == null)) {
|
||||||
|
change = this.changesBySSID[rev];
|
||||||
|
}
|
||||||
|
if ((change == null)) {
|
||||||
|
oldrev = `Unresolved ${build.builderid}-${build.buildid}`;
|
||||||
|
delete this.changesBySSID[oldrev];
|
||||||
|
change = this.makeFakeChange(rev, build.started_at, rev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((change == null)) {
|
||||||
|
rev = `Unresolved ${build.builderid}-${build.buildid}`;
|
||||||
|
if ((change == null)) {
|
||||||
|
change = this.changesBySSID[rev];
|
||||||
|
}
|
||||||
|
if ((change == null)) {
|
||||||
|
change = this.makeFakeChange(rev, build.started_at, rev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
change.caption = rev;
|
||||||
|
}
|
||||||
|
|
||||||
|
return change.buildersById[build.builderid].builds.push(build);
|
||||||
|
}
|
||||||
|
|
||||||
|
makeFakeChange(revision, when_timestamp, comments) {
|
||||||
|
const change = {
|
||||||
|
revision,
|
||||||
|
changeid: revision,
|
||||||
|
when_timestamp,
|
||||||
|
comments
|
||||||
|
};
|
||||||
|
this.changesBySSID[revision] = change;
|
||||||
|
this.populateChange(change);
|
||||||
|
return change;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Open all change row information
|
||||||
|
*/
|
||||||
|
openAll() {
|
||||||
|
return Array.from(this.filtered_changes).map((change) =>
|
||||||
|
(change.show_details = true));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Close all change row information
|
||||||
|
*/
|
||||||
|
closeAll() {
|
||||||
|
return Array.from(this.filtered_changes).map((change) =>
|
||||||
|
(change.show_details = false));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Calculate row header (aka first column) width
|
||||||
|
* depending if we display commit comment, we reserve more space
|
||||||
|
*/
|
||||||
|
getRowHeaderWidth() {
|
||||||
|
if (this.hasExpanded()) {
|
||||||
|
return 400; // magic value enough to hold 78 characters lines
|
||||||
|
} else {
|
||||||
|
return 200;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Calculate col header (aka first row) height
|
||||||
|
* It depends on the length of the longest builder
|
||||||
|
*/
|
||||||
|
getColHeaderHeight() {
|
||||||
|
let max_buildername = 0;
|
||||||
|
for (let builder of Array.from(this.builders)) {
|
||||||
|
max_buildername = Math.max(builder.name.length, max_buildername);
|
||||||
|
}
|
||||||
|
return Math.max(100, max_buildername * 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Determine if we use a 100% width table or if we allow horizontal scrollbar
|
||||||
|
* depending on number of builders, and size of window, we need a fixed column size or a 100% width table
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
isBigTable() {
|
||||||
|
const padding = this.getRowHeaderWidth();
|
||||||
|
if (((this.$window.innerWidth - padding) / this.builders.length) < 40) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* do we have at least one change expanded?
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
hasExpanded() {
|
||||||
|
for (let change of Array.from(this.changes)) {
|
||||||
|
if (this.infoIsExpanded(change)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* display build details
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
selectBuild(build) {
|
||||||
|
let modal;
|
||||||
|
return modal = this.$uibModal.open({
|
||||||
|
template: require('./view/modal/modal.tpl.jade'),
|
||||||
|
controller: 'consoleModalController as modal',
|
||||||
|
windowClass: 'modal-big',
|
||||||
|
resolve: {
|
||||||
|
selectedBuild() { return build; }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* toggle display of additional info for that change
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
toggleInfo(change) {
|
||||||
|
return change.show_details = !change.show_details;
|
||||||
|
}
|
||||||
|
infoIsExpanded(change) {
|
||||||
|
return change.show_details;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
angular.module('yocto_console_view', [
|
||||||
|
'ui.router', 'ui.bootstrap', 'ngAnimate', 'guanlecoja.ui', 'bbData'])
|
||||||
|
.config(['$stateProvider', 'glMenuServiceProvider', 'bbSettingsServiceProvider', ConsoleState])
|
||||||
|
.controller('consoleController', ['$scope', '$q', '$window', 'dataService', 'bbSettingsService', 'resultsService', '$uibModal', '$timeout', Console]);
|
||||||
|
|
||||||
|
require('./view/modal/modal.controller.js');
|
||||||
|
require('./releaseselectorfield.directive.js');
|
||||||
|
require('./yoctochangedetails.directive.js');
|
|
@ -1,177 +0,0 @@
|
||||||
beforeEach ->
|
|
||||||
module ($provide) ->
|
|
||||||
$provide.service '$uibModal', -> open: ->
|
|
||||||
null
|
|
||||||
module ($provide) ->
|
|
||||||
$provide.service 'resultsService', -> results2class: ->
|
|
||||||
null
|
|
||||||
|
|
||||||
# Mock bbSettingsProvider
|
|
||||||
module ($provide) ->
|
|
||||||
$provide.provider 'bbSettingsService', class
|
|
||||||
group = {}
|
|
||||||
addSettingsGroup: (g) -> g.items.map (i) ->
|
|
||||||
if i.name is 'lazy_limit_waterfall'
|
|
||||||
i.default_value = 2
|
|
||||||
group[i.name] = value: i.default_value
|
|
||||||
$get: ->
|
|
||||||
getSettingsGroup: ->
|
|
||||||
return group
|
|
||||||
save: ->
|
|
||||||
null
|
|
||||||
module 'yocto_console_view'
|
|
||||||
|
|
||||||
describe 'Console view', ->
|
|
||||||
$state = null
|
|
||||||
beforeEach inject ($injector) ->
|
|
||||||
$state = $injector.get('$state')
|
|
||||||
|
|
||||||
it 'should register a new state with the correct configuration', ->
|
|
||||||
name = 'console'
|
|
||||||
state = $state.get().pop()
|
|
||||||
data = state.data
|
|
||||||
expect(state.controller).toBe("#{name}Controller")
|
|
||||||
expect(state.controllerAs).toBe('c')
|
|
||||||
expect(state.templateUrl).toBe("yocto_console_view/views/#{name}.html")
|
|
||||||
expect(state.url).toBe("/#{name}")
|
|
||||||
|
|
||||||
describe 'Console view controller', ->
|
|
||||||
# Test data
|
|
||||||
|
|
||||||
builders = [
|
|
||||||
builderid: 1
|
|
||||||
masterids: [1]
|
|
||||||
,
|
|
||||||
builderid: 2
|
|
||||||
masterids: [1]
|
|
||||||
,
|
|
||||||
builderid: 3
|
|
||||||
masterids: [1]
|
|
||||||
,
|
|
||||||
builderid: 4
|
|
||||||
masterids: [1]
|
|
||||||
]
|
|
||||||
|
|
||||||
builds1 = [
|
|
||||||
buildid: 1
|
|
||||||
builderid: 1
|
|
||||||
buildrequestid: 1
|
|
||||||
,
|
|
||||||
buildid: 2
|
|
||||||
builderid: 2
|
|
||||||
buildrequestid: 1
|
|
||||||
,
|
|
||||||
buildid: 3
|
|
||||||
builderid: 4
|
|
||||||
buildrequestid: 2
|
|
||||||
,
|
|
||||||
buildid: 4
|
|
||||||
builderid: 3
|
|
||||||
buildrequestid: 2
|
|
||||||
]
|
|
||||||
|
|
||||||
builds2 = [
|
|
||||||
buildid: 5
|
|
||||||
builderid: 2
|
|
||||||
buildrequestid: 3
|
|
||||||
]
|
|
||||||
|
|
||||||
builds = builds1.concat(builds2)
|
|
||||||
|
|
||||||
buildrequests = [
|
|
||||||
builderid: 1
|
|
||||||
buildrequestid: 1
|
|
||||||
buildsetid: 1
|
|
||||||
,
|
|
||||||
builderid: 1
|
|
||||||
buildrequestid: 2
|
|
||||||
buildsetid: 1
|
|
||||||
,
|
|
||||||
builderid: 1
|
|
||||||
buildrequestid: 3
|
|
||||||
buildsetid: 2
|
|
||||||
]
|
|
||||||
|
|
||||||
buildsets = [
|
|
||||||
bsid: 1
|
|
||||||
sourcestamps: [
|
|
||||||
ssid: 1
|
|
||||||
]
|
|
||||||
,
|
|
||||||
bsid: 2
|
|
||||||
sourcestamps: [
|
|
||||||
ssid: 2
|
|
||||||
]
|
|
||||||
]
|
|
||||||
|
|
||||||
changes = [
|
|
||||||
changeid: 1
|
|
||||||
sourcestamp:
|
|
||||||
ssid: 1
|
|
||||||
]
|
|
||||||
createController = scope = $rootScope = dataService = $window = $timeout = null
|
|
||||||
|
|
||||||
injected = ($injector) ->
|
|
||||||
$q = $injector.get('$q')
|
|
||||||
$rootScope = $injector.get('$rootScope')
|
|
||||||
$window = $injector.get('$window')
|
|
||||||
$timeout = $injector.get('$timeout')
|
|
||||||
dataService = $injector.get('dataService')
|
|
||||||
scope = $rootScope.$new()
|
|
||||||
dataService.when('builds', builds)
|
|
||||||
dataService.when('builders', builders)
|
|
||||||
dataService.when('changes', changes)
|
|
||||||
dataService.when('buildrequests', buildrequests)
|
|
||||||
dataService.when('buildsets', buildsets)
|
|
||||||
|
|
||||||
# Create new controller using controller as syntax
|
|
||||||
$controller = $injector.get('$controller')
|
|
||||||
createController = ->
|
|
||||||
return $controller 'consoleController as c',
|
|
||||||
# Inject controller dependencies
|
|
||||||
$q: $q
|
|
||||||
$window: $window
|
|
||||||
$scope: scope
|
|
||||||
|
|
||||||
beforeEach(inject(injected))
|
|
||||||
|
|
||||||
it 'should be defined', ->
|
|
||||||
createController()
|
|
||||||
expect(scope.c).toBeDefined()
|
|
||||||
|
|
||||||
it 'should bind the builds, builders, changes, buildrequests and buildsets to scope', ->
|
|
||||||
createController()
|
|
||||||
$rootScope.$digest()
|
|
||||||
$timeout.flush()
|
|
||||||
expect(scope.c.builds).toBeDefined()
|
|
||||||
expect(scope.c.builds.length).toBe(builds.length)
|
|
||||||
expect(scope.c.all_builders).toBeDefined()
|
|
||||||
expect(scope.c.all_builders.length).toBe(builders.length)
|
|
||||||
expect(scope.c.changes).toBeDefined()
|
|
||||||
expect(scope.c.changes.length).toBe(changes.length)
|
|
||||||
expect(scope.c.buildrequests).toBeDefined()
|
|
||||||
expect(scope.c.buildrequests.length).toBe(buildrequests.length)
|
|
||||||
expect(scope.c.buildsets).toBeDefined()
|
|
||||||
expect(scope.c.buildsets.length).toBe(buildsets.length)
|
|
||||||
|
|
||||||
it 'should match the builds with the change', ->
|
|
||||||
createController()
|
|
||||||
$timeout.flush()
|
|
||||||
$rootScope.$digest()
|
|
||||||
$timeout.flush()
|
|
||||||
expect(scope.c.changes[0]).toBeDefined()
|
|
||||||
expect(scope.c.changes[0].builders).toBeDefined()
|
|
||||||
builders = scope.c.changes[0].builders
|
|
||||||
expect(builders[0].builds[0].buildid).toBe(1)
|
|
||||||
expect(builders[1].builds[0].buildid).toBe(2)
|
|
||||||
expect(builders[2].builds[0].buildid).toBe(4)
|
|
||||||
expect(builders[3].builds[0].buildid).toBe(3)
|
|
||||||
|
|
||||||
xit 'should match sort the builders by tag groups', ->
|
|
||||||
createController()
|
|
||||||
_builders = FIXTURES['builders.fixture.json'].builders
|
|
||||||
for builder in _builders
|
|
||||||
builder.hasBuild = true
|
|
||||||
scope.c.sortBuildersByTags(_builders)
|
|
||||||
expect(_builders.length).toBe(scope.c.builders.length)
|
|
||||||
expect(scope.c.tag_lines.length).toEqual(5)
|
|
228
yocto_console_view/src/module/main.module.spec.js
Normal file
228
yocto_console_view/src/module/main.module.spec.js
Normal file
|
@ -0,0 +1,228 @@
|
||||||
|
/*
|
||||||
|
* decaffeinate suggestions:
|
||||||
|
* DS101: Remove unnecessary use of Array.from
|
||||||
|
* DS102: Remove unnecessary code created because of implicit returns
|
||||||
|
* DS206: Consider reworking classes to avoid initClass
|
||||||
|
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||||
|
*/
|
||||||
|
beforeEach(function() {
|
||||||
|
angular.mock.module(function($provide) {
|
||||||
|
$provide.service('$uibModal', function() { return {open() {}}; });
|
||||||
|
});
|
||||||
|
angular.mock.module(function($provide) {
|
||||||
|
$provide.service('resultsService', function() { return {results2class() {}}; });
|
||||||
|
});
|
||||||
|
|
||||||
|
// Mock bbSettingsProvider
|
||||||
|
angular.mock.module(function($provide) {
|
||||||
|
$provide.provider('bbSettingsService', (function() {
|
||||||
|
let group = undefined;
|
||||||
|
const Cls = class {
|
||||||
|
static initClass() {
|
||||||
|
group = {};
|
||||||
|
}
|
||||||
|
addSettingsGroup(g) { return g.items.map(function(i) {
|
||||||
|
if (i.name === 'lazy_limit_waterfall') {
|
||||||
|
i.default_value = 2;
|
||||||
|
}
|
||||||
|
return group[i.name] = {value: i.default_value};
|
||||||
|
}); }
|
||||||
|
$get() {
|
||||||
|
return {
|
||||||
|
getSettingsGroup() {
|
||||||
|
return group;
|
||||||
|
},
|
||||||
|
save() {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Cls.initClass();
|
||||||
|
return Cls;
|
||||||
|
})()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
angular.mock.module('yocto_console_view');
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Console view', function() {
|
||||||
|
let $state = null;
|
||||||
|
beforeEach(inject($injector => $state = $injector.get('$state'))
|
||||||
|
);
|
||||||
|
|
||||||
|
it('should register a new state with the correct configuration', function() {
|
||||||
|
const name = 'console';
|
||||||
|
const state = $state.get().pop();
|
||||||
|
const { data } = state;
|
||||||
|
expect(state.controller).toBe(`${name}Controller`);
|
||||||
|
expect(state.controllerAs).toBe('c');
|
||||||
|
expect(state.url).toBe(`/${name}`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Console view controller', function() {
|
||||||
|
// Test data
|
||||||
|
|
||||||
|
let $rootScope, $timeout, $window, dataService, scope;
|
||||||
|
let builders = [{
|
||||||
|
builderid: 1,
|
||||||
|
masterids: [1]
|
||||||
|
}
|
||||||
|
, {
|
||||||
|
builderid: 2,
|
||||||
|
masterids: [1]
|
||||||
|
}
|
||||||
|
, {
|
||||||
|
builderid: 3,
|
||||||
|
masterids: [1]
|
||||||
|
}
|
||||||
|
, {
|
||||||
|
builderid: 4,
|
||||||
|
masterids: [1]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const builds1 = [{
|
||||||
|
buildid: 1,
|
||||||
|
builderid: 1,
|
||||||
|
buildrequestid: 1
|
||||||
|
}
|
||||||
|
, {
|
||||||
|
buildid: 2,
|
||||||
|
builderid: 2,
|
||||||
|
buildrequestid: 1
|
||||||
|
}
|
||||||
|
, {
|
||||||
|
buildid: 3,
|
||||||
|
builderid: 4,
|
||||||
|
buildrequestid: 2
|
||||||
|
}
|
||||||
|
, {
|
||||||
|
buildid: 4,
|
||||||
|
builderid: 3,
|
||||||
|
buildrequestid: 2
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const builds2 = [{
|
||||||
|
buildid: 5,
|
||||||
|
builderid: 2,
|
||||||
|
buildrequestid: 3
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const builds = builds1.concat(builds2);
|
||||||
|
|
||||||
|
const buildrequests = [{
|
||||||
|
builderid: 1,
|
||||||
|
buildrequestid: 1,
|
||||||
|
buildsetid: 1
|
||||||
|
}
|
||||||
|
, {
|
||||||
|
builderid: 1,
|
||||||
|
buildrequestid: 2,
|
||||||
|
buildsetid: 1
|
||||||
|
}
|
||||||
|
, {
|
||||||
|
builderid: 1,
|
||||||
|
buildrequestid: 3,
|
||||||
|
buildsetid: 2
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const buildsets = [{
|
||||||
|
bsid: 1,
|
||||||
|
sourcestamps: [
|
||||||
|
{ssid: 1}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
, {
|
||||||
|
bsid: 2,
|
||||||
|
sourcestamps: [
|
||||||
|
{ssid: 2}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const changes = [{
|
||||||
|
changeid: 1,
|
||||||
|
sourcestamp: {
|
||||||
|
ssid: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
let createController = (scope = ($rootScope = (dataService = ($window = ($timeout = null)))));
|
||||||
|
|
||||||
|
const injected = function($injector) {
|
||||||
|
const $q = $injector.get('$q');
|
||||||
|
$rootScope = $injector.get('$rootScope');
|
||||||
|
$window = $injector.get('$window');
|
||||||
|
$timeout = $injector.get('$timeout');
|
||||||
|
dataService = $injector.get('dataService');
|
||||||
|
scope = $rootScope.$new();
|
||||||
|
dataService.when('builds', builds);
|
||||||
|
dataService.when('builders', builders);
|
||||||
|
dataService.when('changes', changes);
|
||||||
|
dataService.when('buildrequests', buildrequests);
|
||||||
|
dataService.when('buildsets', buildsets);
|
||||||
|
|
||||||
|
// Create new controller using controller as syntax
|
||||||
|
const $controller = $injector.get('$controller');
|
||||||
|
createController = () =>
|
||||||
|
$controller('consoleController as c', {
|
||||||
|
// Inject controller dependencies
|
||||||
|
$q,
|
||||||
|
$window,
|
||||||
|
$scope: scope
|
||||||
|
}
|
||||||
|
)
|
||||||
|
;
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(inject(injected));
|
||||||
|
|
||||||
|
it('should be defined', function() {
|
||||||
|
createController();
|
||||||
|
expect(scope.c).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should bind the builds, builders, changes, buildrequests and buildsets to scope', function() {
|
||||||
|
createController();
|
||||||
|
$rootScope.$digest();
|
||||||
|
$timeout.flush();
|
||||||
|
expect(scope.c.builds).toBeDefined();
|
||||||
|
expect(scope.c.builds.length).toBe(builds.length);
|
||||||
|
expect(scope.c.all_builders).toBeDefined();
|
||||||
|
expect(scope.c.all_builders.length).toBe(builders.length);
|
||||||
|
expect(scope.c.changes).toBeDefined();
|
||||||
|
expect(scope.c.changes.length).toBe(changes.length);
|
||||||
|
expect(scope.c.buildrequests).toBeDefined();
|
||||||
|
expect(scope.c.buildrequests.length).toBe(buildrequests.length);
|
||||||
|
expect(scope.c.buildsets).toBeDefined();
|
||||||
|
expect(scope.c.buildsets.length).toBe(buildsets.length);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should match the builds with the change', function() {
|
||||||
|
createController();
|
||||||
|
$timeout.flush();
|
||||||
|
$rootScope.$digest();
|
||||||
|
$timeout.flush();
|
||||||
|
expect(scope.c.changes[0]).toBeDefined();
|
||||||
|
expect(scope.c.changes[0].builders).toBeDefined();
|
||||||
|
({ builders } = scope.c.changes[0]);
|
||||||
|
expect(builders[0].builds[0].buildid).toBe(1);
|
||||||
|
expect(builders[1].builds[0].buildid).toBe(2);
|
||||||
|
expect(builders[2].builds[0].buildid).toBe(4);
|
||||||
|
expect(builders[3].builds[0].buildid).toBe(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
xit('should match sort the builders by tag groups', function() {
|
||||||
|
createController();
|
||||||
|
const _builders = FIXTURES['builders.fixture.json'].builders;
|
||||||
|
for (let builder of Array.from(_builders)) {
|
||||||
|
builder.hasBuild = true;
|
||||||
|
}
|
||||||
|
scope.c.sortBuildersByTags(_builders);
|
||||||
|
expect(_builders.length).toBe(scope.c.builders.length);
|
||||||
|
expect(scope.c.tag_lines.length).toEqual(5);
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,43 +0,0 @@
|
||||||
|
|
||||||
class Releaseselectorfield extends Directive
|
|
||||||
constructor: ->
|
|
||||||
return {
|
|
||||||
replace: false
|
|
||||||
restrict: 'E'
|
|
||||||
scope: false
|
|
||||||
templateUrl: "yocto_console_view/views/releaseselectorfield.html"
|
|
||||||
controller: '_ReleaseselectorfieldController'
|
|
||||||
}
|
|
||||||
|
|
||||||
class _Releaseselectorfield extends Controller
|
|
||||||
constructor: ($scope, $http) ->
|
|
||||||
# HACK: we find the rootfield by doing $scope.$parent.$parent
|
|
||||||
rootfield = $scope
|
|
||||||
while rootfield? and not rootfield.rootfield?
|
|
||||||
rootfield = rootfield.$parent
|
|
||||||
|
|
||||||
if not rootfield?
|
|
||||||
console.log "rootfield not found!?!?"
|
|
||||||
return
|
|
||||||
|
|
||||||
# copy paste of code in forcedialog, which flatten the fields to be able to find easily
|
|
||||||
fields_ref = {}
|
|
||||||
gatherFields = (fields) ->
|
|
||||||
for field in fields
|
|
||||||
if field.fields?
|
|
||||||
gatherFields(field.fields)
|
|
||||||
else
|
|
||||||
fields_ref[field.fullName] = field
|
|
||||||
|
|
||||||
gatherFields(rootfield.rootfield.fields)
|
|
||||||
|
|
||||||
console.log fields_ref
|
|
||||||
|
|
||||||
# when our field change, we update the fields that we are suppose to
|
|
||||||
$scope.$watch "field.value", (n, o) ->
|
|
||||||
|
|
||||||
selector = $scope.field.selectors[n]
|
|
||||||
if selector?
|
|
||||||
for k, v of selector
|
|
||||||
console.log k
|
|
||||||
fields_ref[k].value = v
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
/*
|
||||||
|
* decaffeinate suggestions:
|
||||||
|
* DS002: Fix invalid constructor
|
||||||
|
* DS101: Remove unnecessary use of Array.from
|
||||||
|
* DS102: Remove unnecessary code created because of implicit returns
|
||||||
|
* DS205: Consider reworking code to avoid use of IIFEs
|
||||||
|
* DS207: Consider shorter variations of null checks
|
||||||
|
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||||
|
*/
|
||||||
|
|
||||||
|
class Releaseselectorfield {
|
||||||
|
constructor() {
|
||||||
|
return {
|
||||||
|
replace: false,
|
||||||
|
restrict: 'E',
|
||||||
|
scope: false,
|
||||||
|
template: require('./releaseselectorfield.tpl.jade'),
|
||||||
|
controller: '_ReleaseselectorfieldController'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _releaseselectorfield {
|
||||||
|
constructor($scope, $http) {
|
||||||
|
// HACK: we find the rootfield by doing $scope.$parent.$parent
|
||||||
|
let rootfield = $scope;
|
||||||
|
while ((rootfield != null) && (rootfield.rootfield == null)) {
|
||||||
|
rootfield = rootfield.$parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((rootfield == null)) {
|
||||||
|
console.log("rootfield not found!?!?");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy paste of code in forcedialog, which flatten the fields to be able to find easily
|
||||||
|
const fields_ref = {};
|
||||||
|
var gatherFields = fields => Array.from(fields).map((field) =>
|
||||||
|
(field.fields != null) ?
|
||||||
|
gatherFields(field.fields)
|
||||||
|
:
|
||||||
|
(fields_ref[field.fullName] = field));
|
||||||
|
|
||||||
|
gatherFields(rootfield.rootfield.fields);
|
||||||
|
|
||||||
|
console.log(fields_ref);
|
||||||
|
|
||||||
|
// when our field change, we update the fields that we are suppose to
|
||||||
|
$scope.$watch("field.value", function(n, o) {
|
||||||
|
|
||||||
|
const selector = $scope.field.selectors[n];
|
||||||
|
if (selector != null) {
|
||||||
|
return (() => {
|
||||||
|
const result = [];
|
||||||
|
for (let k in selector) {
|
||||||
|
const v = selector[k];
|
||||||
|
console.log(k);
|
||||||
|
result.push(fields_ref[k].value = v);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
angular.module('yocto_console_view')
|
||||||
|
.directive('releaseselectorfield', [Releaseselectorfield])
|
||||||
|
.controller('_ReleaseselectorfieldController', ['$scope', '$http', _releaseselectorfield])
|
|
@ -1,7 +0,0 @@
|
||||||
class ConsoleModal extends Controller
|
|
||||||
constructor: ($scope, @$uibModalInstance, @selectedBuild) ->
|
|
||||||
$scope.$on '$stateChangeStart', =>
|
|
||||||
@close()
|
|
||||||
|
|
||||||
close: ->
|
|
||||||
@$uibModalInstance.close()
|
|
22
yocto_console_view/src/module/view/modal/modal.controller.js
Normal file
22
yocto_console_view/src/module/view/modal/modal.controller.js
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
* decaffeinate suggestions:
|
||||||
|
* DS102: Remove unnecessary code created because of implicit returns
|
||||||
|
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||||
|
*/
|
||||||
|
class ConsoleModal {
|
||||||
|
constructor($scope, $uibModalInstance, selectedBuild) {
|
||||||
|
this.$uibModalInstance = $uibModalInstance;
|
||||||
|
this.selectedBuild = selectedBuild;
|
||||||
|
$scope.$on('$stateChangeStart', () => {
|
||||||
|
return this.close();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
close() {
|
||||||
|
return this.$uibModalInstance.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
angular.module('yocto_console_view')
|
||||||
|
.controller('consoleModalController', ['$scope', '$uibModalInstance', 'selectedBuild', ConsoleModal]);
|
|
@ -1,10 +0,0 @@
|
||||||
class Yoctochangedetails extends Directive('common')
|
|
||||||
constructor: ->
|
|
||||||
return {
|
|
||||||
replace: true
|
|
||||||
restrict: 'E'
|
|
||||||
scope:
|
|
||||||
change: '='
|
|
||||||
compact: '=?'
|
|
||||||
templateUrl: 'yocto_console_view/views/yoctochangedetails.html'
|
|
||||||
}
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
/*
|
||||||
|
* decaffeinate suggestions:
|
||||||
|
* DS002: Fix invalid constructor
|
||||||
|
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||||
|
*/
|
||||||
|
class Yoctochangedetails {
|
||||||
|
constructor() {
|
||||||
|
return {
|
||||||
|
replace: true,
|
||||||
|
restrict: 'E',
|
||||||
|
scope: {
|
||||||
|
change: '=',
|
||||||
|
compact: '=?'
|
||||||
|
},
|
||||||
|
template: require('./yoctochangedetails.tpl.jade')
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
angular.module('yocto_console_view')
|
||||||
|
.directive('yoctochangedetails', [Yoctochangedetails])
|
26
yocto_console_view/webpack.config.js
Normal file
26
yocto_console_view/webpack.config.js
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const common = require('buildbot-build-common');
|
||||||
|
const env = require('yargs').argv.env;
|
||||||
|
const pkg = require('./package.json');
|
||||||
|
|
||||||
|
var event = process.env.npm_lifecycle_event;
|
||||||
|
|
||||||
|
var isTest = event === 'test' || event === 'test-watch';
|
||||||
|
var isProd = env === 'prod';
|
||||||
|
|
||||||
|
module.exports = function() {
|
||||||
|
return common.createTemplateWebpackConfig({
|
||||||
|
entry: {
|
||||||
|
scripts: './src/module/main.module.js',
|
||||||
|
styles: './src/styles/styles.less',
|
||||||
|
},
|
||||||
|
libraryName: pkg.name,
|
||||||
|
pluginName: pkg.plugin_name,
|
||||||
|
dirname: __dirname,
|
||||||
|
isTest: isTest,
|
||||||
|
isProd: isProd,
|
||||||
|
outputPath: __dirname + '/yocto_console_view/static',
|
||||||
|
extractStyles: true,
|
||||||
|
});
|
||||||
|
}();
|
|
@ -1,8 +1,23 @@
|
||||||
|
# This file is part of Buildbot. Buildbot is free software: you can
|
||||||
|
# redistribute it and/or modify it under the terms of the GNU General Public
|
||||||
|
# License as published by the Free Software Foundation, version 2.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||||
|
# details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along with
|
||||||
|
# this program; if not, write to the Free Software Foundation, Inc., 51
|
||||||
|
# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
#
|
||||||
|
# Copyright Buildbot Team Members
|
||||||
|
|
||||||
from buildbot.www.plugin import Application
|
from buildbot.www.plugin import Application
|
||||||
from buildbot.schedulers.forcesched import ChoiceStringParameter
|
from buildbot.schedulers.forcesched import ChoiceStringParameter
|
||||||
|
|
||||||
# create the interface for the setuptools entry point
|
# create the interface for the setuptools entry point
|
||||||
ep = Application(__name__, "Yocto Console View UI")
|
ep = Application(__name__, "Buildbot Console View UI")
|
||||||
|
|
||||||
class ReleaseSelector(ChoiceStringParameter):
|
class ReleaseSelector(ChoiceStringParameter):
|
||||||
|
|
||||||
|
|
|
@ -1,347 +0,0 @@
|
||||||
window.FIXTURES = {
|
|
||||||
"builders.fixture.json": {
|
|
||||||
"builders": [
|
|
||||||
{
|
|
||||||
"builderid": 1,
|
|
||||||
"description": null,
|
|
||||||
"masterids": [
|
|
||||||
1
|
|
||||||
],
|
|
||||||
"name": "buildbot-job",
|
|
||||||
"tags": [
|
|
||||||
"job",
|
|
||||||
"buildbot"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"builderid": 2,
|
|
||||||
"description": null,
|
|
||||||
"masterids": [
|
|
||||||
1
|
|
||||||
],
|
|
||||||
"name": "buildbot",
|
|
||||||
"tags": [
|
|
||||||
"buildbot",
|
|
||||||
"trunk"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"builderid": 3,
|
|
||||||
"description": null,
|
|
||||||
"masterids": [],
|
|
||||||
"name": "buildbot SQLALCHEMY:latest TESTS:pylint TWISTED:latest python:2.7",
|
|
||||||
"tags": [
|
|
||||||
"buildbot",
|
|
||||||
"SQLALCHEMY:latest",
|
|
||||||
"TESTS:pylint",
|
|
||||||
"TWISTED:latest",
|
|
||||||
"python:2.7"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"builderid": 4,
|
|
||||||
"description": null,
|
|
||||||
"masterids": [],
|
|
||||||
"name": "buildbot SQLALCHEMY:latest TESTS:flake8 TWISTED:latest python:2.7",
|
|
||||||
"tags": [
|
|
||||||
"buildbot",
|
|
||||||
"SQLALCHEMY:latest",
|
|
||||||
"TWISTED:latest",
|
|
||||||
"python:2.7",
|
|
||||||
"TESTS:flake8"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"builderid": 5,
|
|
||||||
"description": null,
|
|
||||||
"masterids": [],
|
|
||||||
"name": "buildbot SQLALCHEMY:latest TESTS:isort TWISTED:latest python:2.7",
|
|
||||||
"tags": [
|
|
||||||
"buildbot",
|
|
||||||
"SQLALCHEMY:latest",
|
|
||||||
"TWISTED:latest",
|
|
||||||
"python:2.7",
|
|
||||||
"TESTS:isort"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"builderid": 6,
|
|
||||||
"description": null,
|
|
||||||
"masterids": [],
|
|
||||||
"name": "buildbot SQLALCHEMY:latest TESTS:docs TWISTED:latest python:2.7",
|
|
||||||
"tags": [
|
|
||||||
"buildbot",
|
|
||||||
"SQLALCHEMY:latest",
|
|
||||||
"TWISTED:latest",
|
|
||||||
"python:2.7",
|
|
||||||
"TESTS:docs"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"builderid": 7,
|
|
||||||
"description": null,
|
|
||||||
"masterids": [],
|
|
||||||
"name": "buildbot SQLALCHEMY:latest TESTS:coverage TWISTED:latest python:2.7",
|
|
||||||
"tags": [
|
|
||||||
"buildbot",
|
|
||||||
"SQLALCHEMY:latest",
|
|
||||||
"TWISTED:latest",
|
|
||||||
"python:2.7",
|
|
||||||
"TESTS:coverage"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"builderid": 8,
|
|
||||||
"description": null,
|
|
||||||
"masterids": [],
|
|
||||||
"name": "buildbot SQLALCHEMY:latest TESTS:js TWISTED:latest python:2.7",
|
|
||||||
"tags": [
|
|
||||||
"buildbot",
|
|
||||||
"SQLALCHEMY:latest",
|
|
||||||
"TWISTED:latest",
|
|
||||||
"python:2.7",
|
|
||||||
"TESTS:js"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"builderid": 9,
|
|
||||||
"description": null,
|
|
||||||
"masterids": [],
|
|
||||||
"name": "buildbot SQLALCHEMY:latest TESTS:smokes TWISTED:latest python:2.7",
|
|
||||||
"tags": [
|
|
||||||
"buildbot",
|
|
||||||
"SQLALCHEMY:latest",
|
|
||||||
"TWISTED:latest",
|
|
||||||
"python:2.7",
|
|
||||||
"TESTS:smokes"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"builderid": 10,
|
|
||||||
"description": null,
|
|
||||||
"masterids": [],
|
|
||||||
"name": "buildbot SQLALCHEMY:latest TESTS:trial TWISTED:14.0.2 python:2.7",
|
|
||||||
"tags": [
|
|
||||||
"buildbot",
|
|
||||||
"SQLALCHEMY:latest",
|
|
||||||
"python:2.7",
|
|
||||||
"TESTS:trial",
|
|
||||||
"TWISTED:14.0.2"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"builderid": 11,
|
|
||||||
"description": null,
|
|
||||||
"masterids": [],
|
|
||||||
"name": "buildbot SQLALCHEMY:latest TESTS:trial TWISTED:15.4.0 python:2.7",
|
|
||||||
"tags": [
|
|
||||||
"buildbot",
|
|
||||||
"SQLALCHEMY:latest",
|
|
||||||
"python:2.7",
|
|
||||||
"TESTS:trial",
|
|
||||||
"TWISTED:15.4.0"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"builderid": 12,
|
|
||||||
"description": null,
|
|
||||||
"masterids": [],
|
|
||||||
"name": "buildbot SQLALCHEMY:latest TESTS:trial TWISTED:latest python:2.7",
|
|
||||||
"tags": [
|
|
||||||
"buildbot",
|
|
||||||
"SQLALCHEMY:latest",
|
|
||||||
"TWISTED:latest",
|
|
||||||
"python:2.7",
|
|
||||||
"TESTS:trial"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"builderid": 13,
|
|
||||||
"description": null,
|
|
||||||
"masterids": [],
|
|
||||||
"name": "buildbot DB_TYPE:sqlite SQLALCHEMY:latest TESTS:trial TWISTED:latest python:2.7",
|
|
||||||
"tags": [
|
|
||||||
"buildbot",
|
|
||||||
"SQLALCHEMY:latest",
|
|
||||||
"TWISTED:latest",
|
|
||||||
"python:2.7",
|
|
||||||
"TESTS:trial",
|
|
||||||
"DB_TYPE:sqlite"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"builderid": 14,
|
|
||||||
"description": null,
|
|
||||||
"masterids": [],
|
|
||||||
"name": "buildbot DB_TYPE:mysql SQLALCHEMY:latest TESTS:trial TWISTED:latest python:2.7",
|
|
||||||
"tags": [
|
|
||||||
"buildbot",
|
|
||||||
"SQLALCHEMY:latest",
|
|
||||||
"TWISTED:latest",
|
|
||||||
"python:2.7",
|
|
||||||
"TESTS:trial",
|
|
||||||
"DB_TYPE:mysql"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"builderid": 15,
|
|
||||||
"description": null,
|
|
||||||
"masterids": [],
|
|
||||||
"name": "buildbot SQLALCHEMY:0.8.0 TESTS:trial TWISTED:15.5.0 python:2.7",
|
|
||||||
"tags": [
|
|
||||||
"buildbot",
|
|
||||||
"python:2.7",
|
|
||||||
"TESTS:trial",
|
|
||||||
"SQLALCHEMY:0.8.0",
|
|
||||||
"TWISTED:15.5.0"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"builderid": 16,
|
|
||||||
"description": null,
|
|
||||||
"masterids": [],
|
|
||||||
"name": "buildbot SQLALCHEMY:latest TESTS:trial TWISTED:15.5.0 python:2.7",
|
|
||||||
"tags": [
|
|
||||||
"buildbot",
|
|
||||||
"SQLALCHEMY:latest",
|
|
||||||
"python:2.7",
|
|
||||||
"TESTS:trial",
|
|
||||||
"TWISTED:15.5.0"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"builderid": 17,
|
|
||||||
"description": null,
|
|
||||||
"masterids": [],
|
|
||||||
"name": "buildbot SQLALCHEMY:latest TESTS:trial_worker TWISTED:10.2.0 python:2.7",
|
|
||||||
"tags": [
|
|
||||||
"buildbot",
|
|
||||||
"SQLALCHEMY:latest",
|
|
||||||
"python:2.7",
|
|
||||||
"TESTS:trial_worker",
|
|
||||||
"TWISTED:10.2.0"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"builderid": 18,
|
|
||||||
"description": null,
|
|
||||||
"masterids": [],
|
|
||||||
"name": "buildbot SQLALCHEMY:latest TESTS:trial_worker TWISTED:11.1.0 python:2.7",
|
|
||||||
"tags": [
|
|
||||||
"buildbot",
|
|
||||||
"SQLALCHEMY:latest",
|
|
||||||
"python:2.7",
|
|
||||||
"TESTS:trial_worker",
|
|
||||||
"TWISTED:11.1.0"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"builderid": 19,
|
|
||||||
"description": null,
|
|
||||||
"masterids": [],
|
|
||||||
"name": "buildbot SQLALCHEMY:latest TESTS:trial_worker TWISTED:12.2.0 python:2.7",
|
|
||||||
"tags": [
|
|
||||||
"buildbot",
|
|
||||||
"SQLALCHEMY:latest",
|
|
||||||
"python:2.7",
|
|
||||||
"TESTS:trial_worker",
|
|
||||||
"TWISTED:12.2.0"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"builderid": 20,
|
|
||||||
"description": null,
|
|
||||||
"masterids": [],
|
|
||||||
"name": "buildbot SQLALCHEMY:latest TESTS:trial_worker TWISTED:13.2.0 python:2.7",
|
|
||||||
"tags": [
|
|
||||||
"buildbot",
|
|
||||||
"SQLALCHEMY:latest",
|
|
||||||
"python:2.7",
|
|
||||||
"TESTS:trial_worker",
|
|
||||||
"TWISTED:13.2.0"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"builderid": 21,
|
|
||||||
"description": null,
|
|
||||||
"masterids": [],
|
|
||||||
"name": "buildbot SQLALCHEMY:latest TESTS:trial_worker TWISTED:14.0.2 python:2.6",
|
|
||||||
"tags": [
|
|
||||||
"buildbot",
|
|
||||||
"SQLALCHEMY:latest",
|
|
||||||
"TWISTED:14.0.2",
|
|
||||||
"TESTS:trial_worker",
|
|
||||||
"python:2.6"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"builderid": 22,
|
|
||||||
"description": null,
|
|
||||||
"masterids": [],
|
|
||||||
"name": "buildbot SQLALCHEMY:latest TESTS:trial_worker TWISTED:15.4.0 python:2.6",
|
|
||||||
"tags": [
|
|
||||||
"buildbot",
|
|
||||||
"SQLALCHEMY:latest",
|
|
||||||
"TWISTED:15.4.0",
|
|
||||||
"TESTS:trial_worker",
|
|
||||||
"python:2.6"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"builderid": 23,
|
|
||||||
"description": null,
|
|
||||||
"masterids": [],
|
|
||||||
"name": "buildbot SQLALCHEMY:latest TESTS:coverage TWISTED:trunk python:3.5",
|
|
||||||
"tags": [
|
|
||||||
"buildbot",
|
|
||||||
"SQLALCHEMY:latest",
|
|
||||||
"TESTS:coverage",
|
|
||||||
"TWISTED:trunk",
|
|
||||||
"python:3.5"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"builderid": 24,
|
|
||||||
"description": null,
|
|
||||||
"masterids": [],
|
|
||||||
"name": "buildbot SQLALCHEMY:latest TESTS:flake8 TWISTED:trunk python:3.5",
|
|
||||||
"tags": [
|
|
||||||
"buildbot",
|
|
||||||
"SQLALCHEMY:latest",
|
|
||||||
"TESTS:flake8",
|
|
||||||
"TWISTED:trunk",
|
|
||||||
"python:3.5"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"builderid": 25,
|
|
||||||
"description": null,
|
|
||||||
"masterids": [],
|
|
||||||
"name": "buildbot SQLALCHEMY:latest TESTS:smokes TWISTED:latest python:3.5",
|
|
||||||
"tags": [
|
|
||||||
"buildbot",
|
|
||||||
"SQLALCHEMY:latest",
|
|
||||||
"TWISTED:latest",
|
|
||||||
"TESTS:smokes",
|
|
||||||
"python:3.5"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"builderid": 26,
|
|
||||||
"description": null,
|
|
||||||
"masterids": [],
|
|
||||||
"name": "buildbot SQLALCHEMY:latest TESTS:trial TWISTED:trunk python:3.6",
|
|
||||||
"tags": [
|
|
||||||
"buildbot",
|
|
||||||
"SQLALCHEMY:latest",
|
|
||||||
"TESTS:trial",
|
|
||||||
"TWISTED:trunk",
|
|
||||||
"python:3.6"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"meta": {
|
|
||||||
"total": 26
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1 +1,74 @@
|
||||||
.console .table-fixedwidth{width:initial}.console .load-indicator{width:100%;height:100%;z-index:900;background-color:#fff;display:table}.console .load-indicator .spinner{display:table-cell;vertical-align:middle;text-align:center}.console .load-indicator .spinner p{font-weight:300;margin-top:10px}.console .column{min-width:40px;max-width:40px;width:40px}.console table{border:none}.console .tag_row td{margin:0;padding:0}.console .tag_row span{position:relative;float:left;font-size:10px;overflow:hidden;text-decoration:none;white-space:nowrap}.console tr.first-row{background-color:#fff!important}.console tr.first-row th{border:none;background-color:#fff!important}.console tr.first-row .builder{position:relative;float:left;font-size:12px;text-align:center;transform:rotate(-25deg);transform-origin:0 100%;text-decoration:none;white-space:nowrap}.yoctochangedetails>.no-select>*{margin-left:.3em;margin-right:.3em}.select-editable{position:absolute;top:0;border:none;margin:2px;width:90%;height:29px}.select-editable input:focus{outline:0}.modal-big .modal-dialog{width:80%}.modal-big .fa{cursor:pointer}
|
.console .table-fixedwidth {
|
||||||
|
width: initial;
|
||||||
|
}
|
||||||
|
.console .load-indicator {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
z-index: 900;
|
||||||
|
background-color: #ffffff;
|
||||||
|
display: table;
|
||||||
|
}
|
||||||
|
.console .load-indicator .spinner {
|
||||||
|
display: table-cell;
|
||||||
|
vertical-align: middle;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.console .load-indicator .spinner p {
|
||||||
|
font-weight: 300;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
.console .column {
|
||||||
|
min-width: 40px;
|
||||||
|
max-width: 40px;
|
||||||
|
width: 40px;
|
||||||
|
}
|
||||||
|
.console table {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
.console .tag_row td {
|
||||||
|
margin: 0px;
|
||||||
|
padding: 0px;
|
||||||
|
}
|
||||||
|
.console .tag_row span {
|
||||||
|
position: relative;
|
||||||
|
float: left;
|
||||||
|
font-size: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-decoration: none;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.console tr.first-row {
|
||||||
|
background-color: #fff !important;
|
||||||
|
}
|
||||||
|
.console tr.first-row th {
|
||||||
|
border: none;
|
||||||
|
background-color: #fff !important;
|
||||||
|
}
|
||||||
|
.console tr.first-row .builder {
|
||||||
|
position: relative;
|
||||||
|
float: left;
|
||||||
|
font-size: 12px;
|
||||||
|
text-align: center;
|
||||||
|
transform: rotate(-25deg);
|
||||||
|
transform-origin: 0% 100%;
|
||||||
|
text-decoration: none;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.yoctochangedetails > .no-select > * {
|
||||||
|
margin-left: 0.3em;
|
||||||
|
margin-right: 0.3em;
|
||||||
|
}
|
||||||
|
.select-editable {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
border: none;
|
||||||
|
margin: 2px;
|
||||||
|
width: 90%;
|
||||||
|
height: 29px;
|
||||||
|
}
|
||||||
|
.select-editable input:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*# sourceMappingURL=styles.css.map*/
|
|
@ -0,0 +1 @@
|
||||||
|
{"version":3,"sources":["webpack://yocto-console-view/./src/styles/styles.less"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"styles.css","sourcesContent":[".console .table-fixedwidth {\n width: initial;\n}\n.console .load-indicator {\n width: 100%;\n height: 100%;\n z-index: 900;\n background-color: #ffffff;\n display: table;\n}\n.console .load-indicator .spinner {\n display: table-cell;\n vertical-align: middle;\n text-align: center;\n}\n.console .load-indicator .spinner p {\n font-weight: 300;\n margin-top: 10px;\n}\n.console .column {\n min-width: 40px;\n max-width: 40px;\n width: 40px;\n}\n.console table {\n border: none;\n}\n.console .tag_row td {\n margin: 0px;\n padding: 0px;\n}\n.console .tag_row span {\n position: relative;\n float: left;\n font-size: 10px;\n overflow: hidden;\n text-decoration: none;\n white-space: nowrap;\n}\n.console tr.first-row {\n background-color: #fff !important;\n}\n.console tr.first-row th {\n border: none;\n background-color: #fff !important;\n}\n.console tr.first-row .builder {\n position: relative;\n float: left;\n font-size: 12px;\n text-align: center;\n transform: rotate(-25deg);\n transform-origin: 0% 100%;\n text-decoration: none;\n white-space: nowrap;\n}\n.yoctochangedetails > .no-select > * {\n margin-left: 0.3em;\n margin-right: 0.3em;\n}\n.select-editable {\n position: absolute;\n top: 0;\n border: none;\n margin: 2px;\n width: 90%;\n height: 29px;\n}\n.select-editable input:focus {\n outline: none;\n}\n"],"sourceRoot":""}
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user