[gnome-continuous] build.gnome.org: Add a dependency graph to the detailed view



commit f4006a332797e134cf366879c10dd61e27f15c13
Author: Owen W. Taylor <otaylor fishsoup net>
Date:   Mon Sep 12 21:40:53 2016 -0400

    build.gnome.org: Add a dependency graph to the detailed view
    
    To someone new to build.gnome.org, the tasks and how they relate
    to each other can be a bit obscure. Add a graph that shows how
    the tasks relate to each other and the current state of the build
    to the top of the detailed view.

 .../DejaVuSansMono-graph-subset.woff               |  Bin 0 -> 12968 bytes
 extras/build.gnome.org/README.DejaVu               |  111 +++++++++++
 extras/build.gnome.org/app.css                     |   48 +++++-
 extras/build.gnome.org/app.js                      |    1 +
 extras/build.gnome.org/controllers.js              |   13 ++-
 extras/build.gnome.org/depgraph.js                 |  200 ++++++++++++++++++++
 extras/build.gnome.org/index.html                  |    1 +
 .../partials/gnome-continuous-build.html           |    2 +
 8 files changed, 374 insertions(+), 2 deletions(-)
---
diff --git a/extras/build.gnome.org/DejaVuSansMono-graph-subset.woff 
b/extras/build.gnome.org/DejaVuSansMono-graph-subset.woff
new file mode 100644
index 0000000..4a5ad36
Binary files /dev/null and b/extras/build.gnome.org/DejaVuSansMono-graph-subset.woff differ
diff --git a/extras/build.gnome.org/README.DejaVu b/extras/build.gnome.org/README.DejaVu
new file mode 100644
index 0000000..a4f32a8
--- /dev/null
+++ b/extras/build.gnome.org/README.DejaVu
@@ -0,0 +1,111 @@
+DejaVuSansMono-graph-subset.ttf is a subsetted version of DejaVuSansMono,
+with the license as below.
+
+The subsetting was done with:
+
+ pyftsubset DejaVuSansMono.ttf \
+  --output-file=DejaVuSansMono-graph-subset.woff --flavor=woff \
+  --unicodes=U+0020-007F,U+00A0,U+2501,U+2503,U+2517,U+2523,U+2533
+
+License
+=======
+
+Fonts are (c) Bitstream (see below). DejaVu changes are in public domain.
+Glyphs imported from Arev fonts are (c) Tavmjong Bah (see below)
+
+Bitstream Vera Fonts Copyright
+------------------------------
+
+Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is
+a trademark of Bitstream, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of the fonts accompanying this license ("Fonts") and associated
+documentation files (the "Font Software"), to reproduce and distribute the
+Font Software, including without limitation the rights to use, copy, merge,
+publish, distribute, and/or sell copies of the Font Software, and to permit
+persons to whom the Font Software is furnished to do so, subject to the
+following conditions:
+
+The above copyright and trademark notices and this permission notice shall
+be included in all copies of one or more of the Font Software typefaces.
+
+The Font Software may be modified, altered, or added to, and in particular
+the designs of glyphs or characters in the Fonts may be modified and
+additional glyphs or characters may be added to the Fonts, only if the fonts
+are renamed to names not containing either the words "Bitstream" or the word
+"Vera".
+
+This License becomes null and void to the extent applicable to Fonts or Font
+Software that has been modified and is distributed under the "Bitstream
+Vera" names.
+
+The Font Software may be sold as part of a larger software package but no
+copy of one or more of the Font Software typefaces may be sold by itself.
+
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT,
+TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME
+FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING
+ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE
+FONT SOFTWARE.
+
+Except as contained in this notice, the names of Gnome, the Gnome
+Foundation, and Bitstream Inc., shall not be used in advertising or
+otherwise to promote the sale, use or other dealings in this Font Software
+without prior written authorization from the Gnome Foundation or Bitstream
+Inc., respectively. For further information, contact: fonts at gnome dot
+org. 
+
+Arev Fonts Copyright
+------------------------------
+
+Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the fonts accompanying this license ("Fonts") and
+associated documentation files (the "Font Software"), to reproduce
+and distribute the modifications to the Bitstream Vera Font Software,
+including without limitation the rights to use, copy, merge, publish,
+distribute, and/or sell copies of the Font Software, and to permit
+persons to whom the Font Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright and trademark notices and this permission notice
+shall be included in all copies of one or more of the Font Software
+typefaces.
+
+The Font Software may be modified, altered, or added to, and in
+particular the designs of glyphs or characters in the Fonts may be
+modified and additional glyphs or characters may be added to the
+Fonts, only if the fonts are renamed to names not containing either
+the words "Tavmjong Bah" or the word "Arev".
+
+This License becomes null and void to the extent applicable to Fonts
+or Font Software that has been modified and is distributed under the 
+"Tavmjong Bah Arev" names.
+
+The Font Software may be sold as part of a larger software package but
+no copy of one or more of the Font Software typefaces may be sold by
+itself.
+
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL
+TAVMJONG BAH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
+
+Except as contained in this notice, the name of Tavmjong Bah shall not
+be used in advertising or otherwise to promote the sale, use or other
+dealings in this Font Software without prior written authorization
+from Tavmjong Bah. For further information, contact: tavmjong @ free
+. fr.
+
+$Id: LICENSE 2133 2007-11-28 02:46:28Z lechimp $
diff --git a/extras/build.gnome.org/app.css b/extras/build.gnome.org/app.css
index 6252eb6..7b1a6ca 100644
--- a/extras/build.gnome.org/app.css
+++ b/extras/build.gnome.org/app.css
@@ -55,4 +55,50 @@ body {
 
 .app.bad .emblem {
     background-image: url(images/app-bad.png);
-}
\ No newline at end of file
+}
+
+@font-face{
+    font-family: 'DepGraphSubsetFont';
+    src: url('DejaVuSansMono-graph-subset.woff');
+}
+
+.task-dep-graph {
+    white-space: pre;
+    font-family: DejaVu Sans Mono, DepGraphSubsetFont, monospace;
+    font-size: 10px;
+    line-height: 100%;
+    margin-top: 20px;
+    margin-bottom: 5px;
+}
+
+.graph-task, .graph-hidden {
+    border: 1px solid black;
+    border-radius: 3px;
+    font-size: 10px;
+    padding: 4px;
+    padding-bottom: 2px;
+    vertical-align: middle;
+    margin-left: 3px;
+    margin-right: 3px;
+}
+
+.graph-hidden {
+    visibility: hidden;
+}
+
+.graph-inter-row {
+    line-height: 10px;
+}
+
+.graph-pad {
+    font-size: 10px;
+    vertical-align: middle;
+}
+
+.graph-connector {
+    vertical-align: middle;
+}
+
+.graph-success { background: #dff0d8; }
+.graph-failed  { background: #f2dede; }
+.graph-running { background: #c0d0f2; }
diff --git a/extras/build.gnome.org/app.js b/extras/build.gnome.org/app.js
index 5f7c1e9..7d28211 100644
--- a/extras/build.gnome.org/app.js
+++ b/extras/build.gnome.org/app.js
@@ -4,6 +4,7 @@
     var bgo = angular.module('build.gnome.org', [
         'ngRoute',
         'bgoControllers',
+        'bgoDepGraph',
     ]);
 
     bgo.config(['$routeProvider', function($routeProvider) {
diff --git a/extras/build.gnome.org/controllers.js b/extras/build.gnome.org/controllers.js
index 4296725..61da460 100644
--- a/extras/build.gnome.org/controllers.js
+++ b/extras/build.gnome.org/controllers.js
@@ -3,7 +3,7 @@
 
     var bgoControllers = angular.module('bgoControllers', []);
 
-    var taskNames = ['resolve', 'bdiff', 'build', 'builddisks', 'smoketest', 'smoketest-classic', 
'smoketest-wayland', 'smoketest-timed', 'integrationtest','applicationstest', ];
+    var taskNames = ['resolve', 'bdiff', 'build', 'builddisks', 'zdisks', 'smoketest', 'smoketest-classic', 
'smoketest-wayland', 'smoketest-timed', 'integrationtest','applicationstest', ];
 
     var ROOT = '/continuous/buildmaster/';
 
@@ -84,6 +84,7 @@
 
         var stages = [];
         var tasks = [];
+        var taskStates = {};
         taskNames.forEach(function(taskName) {
 
             $http.get(buildRoot + taskName + '/meta.json').success(function(data) {
@@ -155,6 +156,15 @@
                     });
                 }
                 tasks.push(data);
+
+                if (data['complete']) {
+                    if (data['success'])
+                        taskStates[taskName] = 'success';
+                    else
+                        taskStates[taskName] = 'failed';
+                } else {
+                    taskStates[taskName] = 'running';
+                }
             }).error(function(data, status, headers, config) {
                 data = {};
                 data['name'] = taskName;
@@ -167,6 +177,7 @@
             return tasks.filter(function(item){ return item.name == name })
         };
         $scope.tasks = tasks;
+        $scope.taskStates = taskStates;
     });
 
     function reversedOrder(a, b) {return parseInt(b)-parseInt(a)}
diff --git a/extras/build.gnome.org/depgraph.js b/extras/build.gnome.org/depgraph.js
new file mode 100644
index 0000000..67b0b04
--- /dev/null
+++ b/extras/build.gnome.org/depgraph.js
@@ -0,0 +1,200 @@
+/* This module implements a graph that shows the dependencies between
+ * the components. Instead of using SVG or something like that, the
+ * graph is done as fixed-width text and box-drawing characters with
+ * CSS layered on top to make it look reasonable.
+ *
+ * Having this work depends on having a fixed-width font with bold
+ * box-drawing characters. 'DejaVu Sans Mono' is a freely available
+ * such font; an appropriate subset of it is included here and specified
+ * in the CSS as a fallback.
+ *
+ * Copyright: Red Hat, Inc. 2016
+ * Author: Owen Taylor <otaylor fishsoup net>
+ * License: MIT
+ */
+(function(exports) {
+    'use strict';
+
+    var bgoDepGraph = angular.module('bgoDepGraph', []);
+
+    var CONN_RL = "\u2501\u2501\u2501";
+    var CONN_RL_SINGLE = "\u2501";
+    var CONN_RBL = "\u2501\u2533\u2501";
+    var CONN_TB = "\u00a0\u2503\u00a0";
+    var CONN_TR = "\u00a0\u2517\u2501";
+    var CONN_TRD = "\u00a0\u2523\u2501";
+    var CONN_NONE = "\u00a0\u00a0\u00a0";
+    var CONN_NONE_SINGLE = "\u00a0";
+
+    function DepGraph(taskRoots, taskChildren) {
+        this.taskRoots = taskRoots;
+        this.taskChildren = taskChildren;
+        this.connectors = [];
+        this.cells = [];
+        this.columnWidths = [];
+        this.nRows = 0;
+        this.nColumns = 0;
+
+        var i;
+        for (i = 0; i < this.taskRoots.length; i++)
+            this._findColumnWidth(this.taskRoots[i], 0);
+
+        this.columnHeights = new Array(this.nColumns);
+        for (i = 0; i < this.nColumns; i++)
+            this.columnHeights[i] = 0;
+
+        for (i = 0; i < this.taskRoots.length; i++)
+            this._place(this.taskRoots[i], 0, 0);
+    }
+
+    DepGraph.prototype._findColumnWidth = function(task, depth) {
+        if (depth >= this.nColumns) {
+            this.columnWidths.push(task.length);
+            this.nColumns++;
+        } else {
+            this.columnWidths[depth] = Math.max(this.columnWidths[depth], task.length);
+        }
+        var children = this.taskChildren[task];
+        for (var i = 0; i < children.length; i++)
+            this._findColumnWidth(children[i], depth + 1);
+    }
+
+    DepGraph.prototype._place = function(task, depth, parentY) {
+        var children = this.taskChildren[task];
+        var ypos = Math.max(parentY, this.columnHeights[depth]);
+        if (children.length > 0) {
+            ypos = this._place(children[0], depth + 1, ypos);
+            this.connectors[ypos][depth] = (children.length == 1) ? LR: CONN_RBL;
+        }
+
+        this.columnHeights[depth] = ypos + 1;
+        var connectorPos = ypos + 1;
+        for (var i = 1; i < children.length; i++) {
+            var newY = this._place(children[i], depth + 1, ypos);
+            while (connectorPos < newY)
+                this.connectors[connectorPos++][depth] = CONN_TB;
+            this.connectors[connectorPos++][depth] = (children.length == i + 1) ? CONN_TR : CONN_TRD;
+        }
+
+        if (ypos + 1 >= this.nRows) {
+            this.cells.push(new Array(this.nColumns));
+            this.connectors.push(new Array(this.nColumns));
+            this.nRows++;
+        }
+        this.cells[ypos][depth] = task;
+
+        return ypos;
+    }
+
+    function _repeat(str, width) {
+        var res = "";
+        for (var i = 0; i < width; i++)
+            res += str;
+        return res;
+    }
+
+    DepGraph.prototype.update = function(element, taskStates) {
+        // Not supported in angularjs-1.2 jqLite
+        // element.empty();
+        var e = element[0];
+        while (e.lastChild)
+            e.removeChild(e.lastChild);
+
+        var i, j;
+
+        var output = "";
+        for (j = 0; j < this.nRows; j++) {
+            var row = this.cells[j];
+            var rowConnectors = this.connectors[j];
+            for (i = 0; i < this.nColumns; i++) {
+                var pad;
+                var span = document.createElement("span");
+                if (row[i] !== undefined) {
+                    span.appendChild(document.createTextNode(row[i]));
+                    if (row[i] in taskStates)
+                        span.className = "graph-task " + "graph-" + taskStates[row[i]];
+                    else
+                        span.className = "graph-task";
+                    pad = this.columnWidths[i] - row[i].length;
+                } else {
+                    span.appendChild(document.createTextNode(_repeat(" ", this.columnWidths[i])));
+                    span.className = "graph-hidden";
+                    pad = 0;
+                }
+                element.append(span);
+                if (pad > 0) {
+                    var padSpan = document.createElement("span");
+                    padSpan.className = "graph-pad";
+                    if (rowConnectors[i] == CONN_RL || rowConnectors[i] == CONN_RBL)
+                        padSpan.appendChild(document.createTextNode(_repeat(CONN_RL_SINGLE, pad)));
+                    else
+                        padSpan.appendChild(document.createTextNode(_repeat(CONN_NONE_SINGLE, pad)));
+                    element.append(padSpan);
+                }
+                var connector = rowConnectors[i] !== undefined ? rowConnectors[i] : CONN_NONE;
+                var connectorSpan = document.createElement("span");
+                connectorSpan.className = 'graph-connector';
+                connectorSpan.appendChild(document.createTextNode(connector));
+                element.append(connectorSpan);
+            }
+            element.append(document.createTextNode("\n"));
+            if (j != this.nRows - 1) {
+                var rowSpan = document.createElement("span");
+                rowSpan.className = "graph-inter-row";
+                for (i = 0; i < this.nColumns; i++) {
+                    var span = document.createElement("span");
+                    span.appendChild(document.createTextNode(_repeat(CONN_NONE_SINGLE, 
this.columnWidths[i])));
+                    span.className = "graph-hidden";
+                    rowSpan.appendChild(span);
+                    var connector;
+                    if (rowConnectors[i] == CONN_TB || rowConnectors[i] == CONN_RBL || rowConnectors[i] == 
CONN_TRD)
+                        connector = CONN_TB;
+                    else
+                        connector = CONN_NONE;
+                    var connectorSpan = document.createElement("span");
+                    connectorSpan.className = 'graph-connector';
+                    connectorSpan.appendChild(document.createTextNode(connector));
+                    rowSpan.appendChild(connectorSpan);
+                }
+                rowSpan.appendChild(document.createTextNode("\n"));
+                element.append(rowSpan);
+            }
+        }
+    }
+
+    var graph = new DepGraph(["resolve"],
+                             {
+                                 resolve: ["build", "bdiff"],
+                                 build: ["builddisks", "zdisks"],
+                                 bdiff: [],
+                                 builddisks: ["smoketest", "smoketest-classic", "smoketest-wayland", 
"smoketest-timed"],
+                                 zdisks: [],
+                                 smoketest: ["applicationstest", "integrationtest", "memusage"],
+                                 'smoketest-classic': [],
+                                 'smoketest-wayland': [],
+                                 'smoketest-timed': [],
+                                 applicationstest: [],
+                                 integrationtest: [],
+                                 memusage: []
+                             });
+
+    bgoDepGraph.directive('depGraph', function() {
+        function link(scope, element, attrs) {
+            var taskStates;
+
+            function updateGraph() {
+                graph.update(element, taskStates);
+            }
+
+            scope.$watch(attrs.depGraph, function(value) {
+                taskStates = value;
+                updateGraph();
+            }, true);
+        }
+
+        return {
+            link: link
+        };
+    });
+})(window);
+
diff --git a/extras/build.gnome.org/index.html b/extras/build.gnome.org/index.html
index 55aba6a..d0ff40e 100644
--- a/extras/build.gnome.org/index.html
+++ b/extras/build.gnome.org/index.html
@@ -10,6 +10,7 @@
   <script src="angular-route.min.js"></script>
   <script src="app.js"></script>
   <script src="controllers.js"></script>
+  <script src="depgraph.js"></script>
 </head>
 <body>
   <div class='navbar-header container-fluid navbar navbar-inverse navbar-fixed-top'>
diff --git a/extras/build.gnome.org/partials/gnome-continuous-build.html 
b/extras/build.gnome.org/partials/gnome-continuous-build.html
index 70f8c92..c8793da 100644
--- a/extras/build.gnome.org/partials/gnome-continuous-build.html
+++ b/extras/build.gnome.org/partials/gnome-continuous-build.html
@@ -1,5 +1,7 @@
 <article>
   <h2>Build {{ buildVersion }}</h2>
+  <div dep-graph="taskStates" class="task-dep-graph">
+  </div>
   <ul class="list-group">
 
     <li class='list-group-item ng-scope'>


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]