Merge "Add jenkins jobConfigHistory plugin"
diff --git a/Dockerfile b/Dockerfile
index 74fab9f..3cf4aac 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -36,10 +36,10 @@
 
 # jenkins version being bundled in this docker image
 ARG JENKINS_VERSION
-ENV JENKINS_VERSION ${JENKINS_VERSION:-2.52}
+ENV JENKINS_VERSION ${JENKINS_VERSION:-2.83}
 
 # jenkins.war checksum, download will be validated using it
-ARG JENKINS_SHA=7a78fc8b3f1eacd45891d052f7fa03471a60b9df52213b4ee948e05834cffc38
+ARG JENKINS_SHA=7f99c4ae050a1733390578145b8490bfe5d09f0132067bf05d18f2b6be953d8e
 
 # Can be used to customize where jenkins.war get downloaded from
 ARG JENKINS_URL=https://repo.jenkins-ci.org/public/org/jenkins-ci/main/jenkins-war/${JENKINS_VERSION}/jenkins-war-${JENKINS_VERSION}.war
@@ -75,20 +75,22 @@
 COPY theme /usr/share/jenkins/ref/userContent/theme
 
 RUN JENKINS_UC_DOWNLOAD=http://archives.jenkins-ci.org /usr/local/bin/install-plugins.sh \
-        ansicolor \
         artifactory \
         build-blocker-plugin \
         build-monitor-plugin \
         build-user-vars-plugin \
         categorized-view \
+        copyartifact \
         description-setter \
         discard-old-build \
         docker-workflow \
         email-ext \
+        envinject \
         extended-choice-parameter \
         extensible-choice-parameter \
         gerrit-trigger \
         git \
+        github \
         heavy-job \
         jobConfigHistory \
         ldap \
@@ -97,11 +99,13 @@
         monitoring \
         permissive-script-security \
         pipeline-utility-steps \
+        prometheus \
         rebuild \
         simple-theme-plugin \
         slack \
+        ssh-agent \
         test-stability \
-        timestamper \
+        throttle-concurrents \
         workflow-cps \
         workflow-remote-loader \
         workflow-scm-step
diff --git a/theme/js/ansi_up.js b/theme/js/ansi_up.js
new file mode 100644
index 0000000..f24c0b6
--- /dev/null
+++ b/theme/js/ansi_up.js
@@ -0,0 +1,334 @@
+/*  ansi_up.js
+ *  author : Dru Nelson
+ *  license : MIT
+ *  http://github.com/drudru/ansi_up
+ */
+(function (root, factory) {
+    if (typeof define === 'function' && define.amd) {
+        // AMD. Register as an anonymous module.
+        define(['exports'], factory);
+    } else if (typeof exports === 'object' && typeof exports.nodeName !== 'string') {
+        // CommonJS
+        factory(exports);
+    } else {
+        // Browser globals
+        var exp = {};
+        factory(exp);
+        root.AnsiUp = exp.default;
+    }
+}(this, function (exports) {
+"use strict";
+function rgx(tmplObj) {
+    var subst = [];
+    for (var _i = 1; _i < arguments.length; _i++) {
+        subst[_i - 1] = arguments[_i];
+    }
+    var regexText = tmplObj.raw[0];
+    var wsrgx = /^\s+|\s+\n|\s+#[\s\S]+?\n/gm;
+    var txt2 = regexText.replace(wsrgx, '');
+    return new RegExp(txt2, 'm');
+}
+var AnsiUp = (function () {
+    function AnsiUp() {
+        this.VERSION = "2.0.2";
+        this.ansi_colors = [
+            [
+                { rgb: [0, 0, 0], class_name: "ansi-black" },
+                { rgb: [187, 0, 0], class_name: "ansi-red" },
+                { rgb: [0, 187, 0], class_name: "ansi-green" },
+                { rgb: [187, 187, 0], class_name: "ansi-yellow" },
+                { rgb: [0, 0, 187], class_name: "ansi-blue" },
+                { rgb: [187, 0, 187], class_name: "ansi-magenta" },
+                { rgb: [0, 187, 187], class_name: "ansi-cyan" },
+                { rgb: [255, 255, 255], class_name: "ansi-white" }
+            ],
+            [
+                { rgb: [85, 85, 85], class_name: "ansi-bright-black" },
+                { rgb: [255, 85, 85], class_name: "ansi-bright-red" },
+                { rgb: [0, 255, 0], class_name: "ansi-bright-green" },
+                { rgb: [255, 255, 85], class_name: "ansi-bright-yellow" },
+                { rgb: [85, 85, 255], class_name: "ansi-bright-blue" },
+                { rgb: [255, 85, 255], class_name: "ansi-bright-magenta" },
+                { rgb: [85, 255, 255], class_name: "ansi-bright-cyan" },
+                { rgb: [255, 255, 255], class_name: "ansi-bright-white" }
+            ]
+        ];
+        this.htmlFormatter = {
+            transform: function (fragment, instance) {
+                var txt = fragment.text;
+                if (txt.length === 0)
+                    return txt;
+                if (instance._escape_for_html)
+                    txt = instance.old_escape_for_html(txt);
+                if (!fragment.bright && fragment.fg === null && fragment.bg === null)
+                    return txt;
+                var styles = [];
+                var classes = [];
+                var fg = fragment.fg;
+                var bg = fragment.bg;
+                if (fg === null && fragment.bright)
+                    fg = instance.ansi_colors[1][7];
+                if (!instance._use_classes) {
+                    if (fg)
+                        styles.push("color:rgb(" + fg.rgb.join(',') + ")");
+                    if (bg)
+                        styles.push("background-color:rgb(" + bg.rgb + ")");
+                }
+                else {
+                    if (fg) {
+                        if (fg.class_name !== 'truecolor') {
+                            classes.push(fg.class_name + "-fg");
+                        }
+                        else {
+                            styles.push("color:rgb(" + fg.rgb.join(',') + ")");
+                        }
+                    }
+                    if (bg) {
+                        if (bg.class_name !== 'truecolor') {
+                            classes.push(bg.class_name + "-bg");
+                        }
+                        else {
+                            styles.push("background-color:rgb(" + bg.rgb.join(',') + ")");
+                        }
+                    }
+                }
+                var class_string = '';
+                var style_string = '';
+                if (classes.length)
+                    class_string = " class=\"" + classes.join(' ') + "\"";
+                if (styles.length)
+                    style_string = " style=\"" + styles.join(';') + "\"";
+                return "<span" + class_string + style_string + ">" + txt + "</span>";
+            },
+            compose: function (segments, instance) {
+                return segments.join("");
+            }
+        };
+        this.textFormatter = {
+            transform: function (fragment, instance) {
+                return fragment.text;
+            },
+            compose: function (segments, instance) {
+                return segments.join("");
+            }
+        };
+        this.setup_256_palette();
+        this._use_classes = false;
+        this._escape_for_html = true;
+        this.bright = false;
+        this.fg = this.bg = null;
+        this._buffer = '';
+    }
+    Object.defineProperty(AnsiUp.prototype, "use_classes", {
+        get: function () {
+            return this._use_classes;
+        },
+        set: function (arg) {
+            this._use_classes = arg;
+        },
+        enumerable: true,
+        configurable: true
+    });
+    Object.defineProperty(AnsiUp.prototype, "escape_for_html", {
+        get: function () {
+            return this._escape_for_html;
+        },
+        set: function (arg) {
+            this._escape_for_html = arg;
+        },
+        enumerable: true,
+        configurable: true
+    });
+    AnsiUp.prototype.setup_256_palette = function () {
+        var _this = this;
+        this.palette_256 = [];
+        this.ansi_colors.forEach(function (palette) {
+            palette.forEach(function (rec) {
+                _this.palette_256.push(rec);
+            });
+        });
+        var levels = [0, 95, 135, 175, 215, 255];
+        for (var r = 0; r < 6; ++r) {
+            for (var g = 0; g < 6; ++g) {
+                for (var b = 0; b < 6; ++b) {
+                    var col = { rgb: [levels[r], levels[g], levels[b]], class_name: 'truecolor' };
+                    this.palette_256.push(col);
+                }
+            }
+        }
+        var grey_level = 8;
+        for (var i = 0; i < 24; ++i, grey_level += 10) {
+            var gry = { rgb: [grey_level, grey_level, grey_level], class_name: 'truecolor' };
+            this.palette_256.push(gry);
+        }
+    };
+    AnsiUp.prototype.old_escape_for_html = function (txt) {
+        return txt.replace(/[&<>]/gm, function (str) {
+            if (str === "&")
+                return "&amp;";
+            if (str === "<")
+                return "&lt;";
+            if (str === ">")
+                return "&gt;";
+        });
+    };
+    AnsiUp.prototype.old_linkify = function (txt) {
+        return txt.replace(/(https?:\/\/[^\s]+)/gm, function (str) {
+            return "<a href=\"" + str + "\">" + str + "</a>";
+        });
+    };
+    AnsiUp.prototype.detect_incomplete_ansi = function (txt) {
+        return !(/.*?[\x40-\x7e]/.test(txt));
+    };
+    AnsiUp.prototype.detect_incomplete_link = function (txt) {
+        var found = false;
+        for (var i = txt.length - 1; i > 0; i--) {
+            if (/\s|\x1B/.test(txt[i])) {
+                found = true;
+                break;
+            }
+        }
+        if (!found) {
+            if (/(https?:\/\/[^\s]+)/.test(txt))
+                return 0;
+            else
+                return -1;
+        }
+        var prefix = txt.substr(i + 1, 4);
+        if (prefix.length === 0)
+            return -1;
+        if ("http".indexOf(prefix) === 0)
+            return (i + 1);
+    };
+    AnsiUp.prototype.ansi_to = function (txt, formatter) {
+        var pkt = this._buffer + txt;
+        this._buffer = '';
+        var raw_text_pkts = pkt.split(/\x1B\[/);
+        if (raw_text_pkts.length === 1)
+            raw_text_pkts.push('');
+        this.handle_incomplete_sequences(raw_text_pkts);
+        var first_chunk = this.with_state(raw_text_pkts.shift());
+        var blocks = new Array(raw_text_pkts.length);
+        for (var i = 0, len = raw_text_pkts.length; i < len; ++i) {
+            blocks[i] = (formatter.transform(this.process_ansi(raw_text_pkts[i]), this));
+        }
+        if (first_chunk.text.length > 0)
+            blocks.unshift(formatter.transform(first_chunk, this));
+        return formatter.compose(blocks, this);
+    };
+    AnsiUp.prototype.ansi_to_html = function (txt) {
+        return this.ansi_to(txt, this.htmlFormatter);
+    };
+    AnsiUp.prototype.ansi_to_text = function (txt) {
+        return this.ansi_to(txt, this.textFormatter);
+    };
+    AnsiUp.prototype.with_state = function (text) {
+        return { bright: this.bright, fg: this.fg, bg: this.bg, text: text };
+    };
+    AnsiUp.prototype.handle_incomplete_sequences = function (chunks) {
+        var last_chunk = chunks[chunks.length - 1];
+        if ((last_chunk.length > 0) && this.detect_incomplete_ansi(last_chunk)) {
+            this._buffer = "\x1B[" + last_chunk;
+            chunks.pop();
+            chunks.push('');
+        }
+        else {
+            if (last_chunk.slice(-1) === "\x1B") {
+                this._buffer = "\x1B";
+                console.log("raw", chunks);
+                chunks.pop();
+                chunks.push(last_chunk.substr(0, last_chunk.length - 1));
+                console.log(chunks);
+                console.log(last_chunk);
+            }
+            if (chunks.length === 2 &&
+                chunks[1] === "" &&
+                chunks[0].slice(-1) === "\x1B") {
+                this._buffer = "\x1B";
+                last_chunk = chunks.shift();
+                chunks.unshift(last_chunk.substr(0, last_chunk.length - 1));
+            }
+        }
+    };
+    AnsiUp.prototype.process_ansi = function (block) {
+        if (!this._sgr_regex) {
+            this._sgr_regex = (_a = ["\n            ^                           # beginning of line\n            ([!<-?]?)             # a private-mode char (!, <, =, >, ?)\n            ([d;]*)                    # any digits or semicolons\n            ([ -/]?               # an intermediate modifier\n            [@-~])                # the command\n            ([sS]*)                   # any text following this CSI sequence\n          "], _a.raw = ["\n            ^                           # beginning of line\n            ([!\\x3c-\\x3f]?)             # a private-mode char (!, <, =, >, ?)\n            ([\\d;]*)                    # any digits or semicolons\n            ([\\x20-\\x2f]?               # an intermediate modifier\n            [\\x40-\\x7e])                # the command\n            ([\\s\\S]*)                   # any text following this CSI sequence\n          "], rgx(_a));
+        }
+        var matches = block.match(this._sgr_regex);
+        if (!matches) {
+            return this.with_state(block);
+        }
+        var orig_txt = matches[4];
+        if (matches[1] !== '' || matches[3] !== 'm') {
+            return this.with_state(orig_txt);
+        }
+        var sgr_cmds = matches[2].split(';');
+        while (sgr_cmds.length > 0) {
+            var sgr_cmd_str = sgr_cmds.shift();
+            var num = parseInt(sgr_cmd_str, 10);
+            if (isNaN(num) || num === 0) {
+                this.fg = this.bg = null;
+                this.bright = false;
+            }
+            else if (num === 1) {
+                this.bright = true;
+            }
+            else if (num === 22) {
+                this.bright = false;
+            }
+            else if (num === 39) {
+                this.fg = null;
+            }
+            else if (num === 49) {
+                this.bg = null;
+            }
+            else if ((num >= 30) && (num < 38)) {
+                var bidx = this.bright ? 1 : 0;
+                this.fg = this.ansi_colors[bidx][(num - 30)];
+            }
+            else if ((num >= 90) && (num < 98)) {
+                this.fg = this.ansi_colors[1][(num - 90)];
+            }
+            else if ((num >= 40) && (num < 48)) {
+                this.bg = this.ansi_colors[0][(num - 40)];
+            }
+            else if ((num >= 100) && (num < 108)) {
+                this.bg = this.ansi_colors[1][(num - 100)];
+            }
+            else if (num === 38 || num === 48) {
+                if (sgr_cmds.length > 0) {
+                    var is_foreground = (num === 38);
+                    var mode_cmd = sgr_cmds.shift();
+                    if (mode_cmd === '5' && sgr_cmds.length > 0) {
+                        var palette_index = parseInt(sgr_cmds.shift(), 10);
+                        if (palette_index >= 0 && palette_index <= 255) {
+                            if (is_foreground)
+                                this.fg = this.palette_256[palette_index];
+                            else
+                                this.bg = this.palette_256[palette_index];
+                        }
+                    }
+                    if (mode_cmd === '2' && sgr_cmds.length > 2) {
+                        var r = parseInt(sgr_cmds.shift(), 10);
+                        var g = parseInt(sgr_cmds.shift(), 10);
+                        var b = parseInt(sgr_cmds.shift(), 10);
+                        if ((r >= 0 && r <= 255) && (g >= 0 && g <= 255) && (b >= 0 && b <= 255)) {
+                            var c = { rgb: [r, g, b], class_name: 'truecolor' };
+                            if (is_foreground)
+                                this.fg = c;
+                            else
+                                this.bg = c;
+                        }
+                    }
+                }
+            }
+        }
+        return this.with_state(orig_txt);
+        var _a;
+    };
+    return AnsiUp;
+}());
+//# sourceMappingURL=ansi_up.js.map
+    Object.defineProperty(exports, "__esModule", { value: true });
+    exports.default = AnsiUp;
+}));
\ No newline at end of file
diff --git a/theme/mirantis.js b/theme/mirantis.js
index e69de29..d838b85 100644
--- a/theme/mirantis.js
+++ b/theme/mirantis.js
@@ -0,0 +1,58 @@
+
+// ------ Jenkins ANSI color pure JS plugin https://github.com/jakubjosef/jenkins-ansi-color-js ------ //
+function DOMReady(fn) {
+    if (document.attachEvent ? document.readyState === "complete" : document.readyState !== "loading") {
+        fn();
+    } else {
+        document.addEventListener('DOMContentLoaded', fn);
+    }
+}
+
+function addScript(src, callback, async) {
+    var s = document.createElement('script');
+    s.setAttribute('src', src);
+    s.onload = callback;
+    if (async) s.async = true
+    document.body.appendChild(s);
+}
+
+DOMReady(function() {
+    var consoleOutputSelector = ".console-output";
+    if (document.querySelector(consoleOutputSelector)) {
+        // init ansi colors
+        addScript("/userContent/theme/js/ansi_up.js", function() {
+            var ansiUp = new AnsiUp,
+                $console = document.querySelector(consoleOutputSelector),
+                entities = {
+                    'amp': '&',
+                    'apos': '\'',
+                    '#x27': '\'',
+                    '#x2F': '/',
+                    '#39': '\'',
+                    '#47': '/',
+                    'lt': '<',
+                    'gt': '>',
+                    'nbsp': ' ',
+                    'quot': '"'
+                },
+                decodeHTMLEntities = function(text) {
+                    return text.replace(/&([^;]+);/gm, function(match, entity) {
+                        return entities[entity] || match
+                    })
+                },
+                colorizeFn = function() {
+                    $console.innerHTML = decodeHTMLEntities(ansiUp.ansi_to_html($console.innerHTML));
+                };
+
+            colorizeFn();
+            // create prototype.js global ajax handle responder
+            Ajax.Responders.register({
+                onComplete: function(req, res) {
+                    if (req.body.indexOf("start=") != -1 && res.status==200 && res.responseText != "") {
+                        colorizeFn();
+                    }
+                }
+            });
+        }, true);
+    }
+});
\ No newline at end of file