Initial commit for TF-A CI scripts
Signed-off-by: Fathi Boudra <fathi.boudra@linaro.org>
diff --git a/script/gen_test_report.js b/script/gen_test_report.js
new file mode 100644
index 0000000..9ce703d
--- /dev/null
+++ b/script/gen_test_report.js
@@ -0,0 +1,277 @@
+//<![CDATA[
+//
+// Copyright (c) 2019, Arm Limited. All rights reserved.
+//
+// SPDX-License-Identifier: BSD-3-Clause
+//
+// Get rid of all unhelpful and annoying orbs that Jenkins barfs to indicate sub
+// job status. We'd have that in the HTML report anyway. Unhelpfully, Jenkins
+// doesn't ID the element, nor does it assign a class to them. So, we:
+//
+// - Look for a h2 element with text "Subproject Builds" or "Subprojects";
+//
+// - The orbs are placed in a <ul> immediately following the h2 element; so we
+// remove it altogether.
+//
+document.querySelectorAll("h2").forEach(function(el) {
+ if ((el.innerText !== "Subproject Builds") && (el.innerText !== "Subprojects"))
+ return;
+ if (el.nextSibling.tagName !== "UL")
+ return;
+ el.nextSibling.remove();
+ el.remove();
+});
+
+// For failed jobs, there's this large "Identified problems" table that has no
+// value. Get rid of that as well.
+document.querySelectorAll("h2").forEach(function(el) {
+ if (el.innerText !== "Identified problems")
+ return;
+ el.closest("table").remove();
+});
+
+function onResultHover(e) {
+ var title = this.getAttribute("title");
+ var commandPre = document.querySelector("#tf-selected-commands");
+ var localCmd = "";
+
+ if (!title || title === "") {
+ localCmd = "<i>No local command available!</i>";
+ } else {
+ var titleElement = '<span style="color: red;">' + title + '</span>';
+
+ localCmd = "workspace=/tmp/workspace test_run=1 test_groups=" + titleElement +
+ " script/run_local_ci.sh";
+ }
+
+ commandPre.innerHTML = localCmd;
+}
+
+// Disable re-trigger button
+function retriggerDisable() {
+ var button = document.getElementById("tf-rebuild-button");
+ button.setAttribute("disabled", "");
+}
+
+var checkedCount = 0;
+
+// Enable or disable retrigger button according to its count attribute
+function retriggerEffectCount() {
+ var button = document.getElementById("tf-rebuild-button");
+
+ if (checkedCount === 0)
+ button.setAttribute("disabled", "");
+ else
+ button.removeAttribute("disabled");
+}
+
+function resultCheckboxes() {
+ return document.querySelectorAll("#tf-report-main input[type=checkbox]");
+}
+
+function computeCheckCount() {
+ checkedCount = 0;
+
+ resultCheckboxes().forEach(function(el) {
+ if (el.checked)
+ checkedCount++;
+ });
+
+ retriggerEffectCount();
+}
+
+function onConfigChange(e) {
+ var button = document.getElementById("tf-rebuild-button");
+
+ computeCheckCount();
+
+ // Collapse the re-build frame upon changing config selection
+ document.getElementById("tf-rebuild-frame").style.display = "none";
+}
+
+var retryCount = 0;
+
+function retryRebuild(frame, selectedConfigs, embed) {
+ var doc = frame.contentDocument;
+ var form = doc.querySelector("form[action=configSubmit]");
+ var errMsg = "Error re-triggering. Are you logged in?" +
+ " If this happens repeatedly, please check the browser console for errors.";
+
+ if (!form || !form.querySelector("button")) {
+ retryCount++;
+ if (retryCount > 50)
+ alert(errMsg);
+ else
+ setTimeout(retryRebuild, 100, frame, selectedConfigs, embed);
+ return;
+ }
+
+ try {
+ var groups = form.querySelector("input[value=TEST_GROUPS]");
+ groups = groups.nextElementSibling;
+
+ // Set groups only if there were selections, or leave unchanged.
+ if (selectedConfigs)
+ groups.value = selectedConfigs.join(" ");
+
+ // Clear the parameters derived from clone_repos.sh that had been passed
+ // over to the present job, which have now become stale. They are no more
+ // valid for a re-trigger, and have to be freshly set.
+ const paramsToClear = ["CI_SCRATCH"];
+ paramsToClear.forEach(function(item) {
+ var el = form.querySelector("input[value=" + item + "]");
+ if (!el)
+ return;
+
+ // The value for this parameter is the next sibling, with name=value
+ // property attached.
+ el = el.nextElementSibling;
+ if (el.getAttribute("name") != "value")
+ throw "Unable to clear parameter '" + item + "'";
+
+ // Clear the parameter's value
+ el.value = "";
+ });
+
+ if (embed) {
+ // Leave only the parameter form
+ try {
+ doc.querySelector("#side-panel").remove();
+ doc.querySelector("#page-head").remove();
+ doc.querySelector("footer").remove();
+
+ var mainPanel = doc.querySelector("#main-panel");
+ mainPanel.style.marginLeft = "0px";
+ mainPanel.style.padding = "10px";
+
+ doc.body.style.padding = "0px";
+ } catch (e) {
+ }
+
+ // Have the frame disappear after clicking, and remove event listener
+ var closer = form.querySelector("button").addEventListener("click", function(e) {
+ setTimeout(function() {
+ frame.style.display = "none";
+
+ // We had disabled the retrigger button when we opened the frame. Now
+ // that we're closing the frame, leave the button in the appropriate
+ // state.
+ retriggerEffectCount();
+
+ e.target.removeEventListener(e.type, closer);
+ alert("Build re-triggered for selected configurations.");
+ });
+ });
+
+ frame.style.height = "700px";
+ frame.style.width = "100%";
+ frame.style.display = "block";
+
+ // Disable re-trigger until this frame is closed
+ retriggerDisable();
+
+ window.scrollTo(0, frame.getBoundingClientRect().top);
+ } else {
+ // Trigger rebuild
+ form.querySelector("button").click();
+ if (selectedConfigs)
+ alert("Build re-triggered for selected configurations.");
+ else
+ alert("Job re-triggered.");
+ }
+ } catch (e) {
+ alert("Error triggering job: " + e);
+ }
+}
+
+function onRebuild(e) {
+ var selectedConfigs = [];
+ var parent;
+ var embed;
+ var configs;
+
+ var loc = window.location.href.replace(/\/*$/, "").split("/");
+ var buildNo = loc[loc.length - 1];
+ if (!parseInt(buildNo)) {
+ alert("Please visit the page of a specifc build, and try again.");
+ return;
+ }
+
+ resultCheckboxes().forEach(function(el) {
+ if (el.checked === true) {
+ parent = el.closest("td");
+ selectedConfigs.push(parent.getAttribute("title"));
+ }
+ });
+
+ loc.push("rebuild");
+ loc.push("parameterized");
+
+ // If shift key was pressed when clicking, just open a retrigger window
+ retryCount = 0;
+ if (e.shiftKey)
+ embed = true;
+
+ var frame = document.getElementById("tf-rebuild-frame");
+ frame.style.display = "none";
+ frame.src = loc.join("/");
+
+ configs = (e.target.id === "tf-rebuild-button")? selectedConfigs: null;
+ setTimeout(retryRebuild, 250, frame, configs, embed);
+}
+
+function onSelectAll(e) {
+ var selectClass = e.target.innerHTML.toLowerCase();
+
+ if (selectClass === "none") {
+ resultCheckboxes().forEach(function(checkbox) {
+ checkbox.checked = false;
+ });
+ } else {
+ document.querySelectorAll("." + selectClass).forEach(function(result) {
+ var input = result.querySelector("input");
+ if (input)
+ input.checked = true;
+ });
+ }
+
+ computeCheckCount();
+}
+
+function init() {
+ // The whole of Jenkins job result page is rendered in an HTML table. This
+ // means that anything that alters the size of content elements will cause a
+ // disruptive page layout reflow. That's exactly what happens with local
+ // commands when job results are hovered over. To avoid jitter when result
+ // hovering, fix the width of the element to its initial value.
+ var localCommands = document.querySelector("#tf-selected-commands");
+ localCommands.style.width = window.getComputedStyle(localCommands).width;
+
+ // Add result hover listeners
+ [".success", ".failure", ".unstable"].map(function(sel) {
+ return "#tf-report-main " + sel;
+ }).forEach(function(sel) {
+ document.querySelectorAll(sel).forEach(function(result) {
+ result.addEventListener("mouseover", onResultHover);
+ });
+ });
+
+ // Add checkbox click listeners
+ resultCheckboxes().forEach(function(el) {
+ el.addEventListener("change", onConfigChange);
+ });
+
+ // Add re-trigger button listener
+ document.getElementById("tf-rebuild-button").addEventListener("click", onRebuild);
+ document.getElementById("tf-rebuild-all-button").addEventListener("click", onRebuild);
+
+ // Add listener for select all widgets
+ document.querySelectorAll(".select-all").forEach(function(widget) {
+ widget.addEventListener("click", onSelectAll);
+ });
+
+ computeCheckCount();
+}
+
+document.addEventListener("DOMContentLoaded", init);
+//]]>