356 lines
12 KiB
HTML
356 lines
12 KiB
HTML
|
|
<!-- Copyright 2020 Google Inc.
|
||
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
|
you may not use this file except in compliance with the License.
|
||
|
|
You may obtain a copy of the License at
|
||
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
||
|
|
Unless required by applicable law or agreed to in writing, software
|
||
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
|
See the License for the specific language governing permissions and
|
||
|
|
limitations under the License. -->
|
||
|
|
|
||
|
|
<link rel="import" href="../../bower_components/polymer/polymer-element.html">
|
||
|
|
<link rel="import" href="../../bower_components/app-layout/app-drawer-layout/app-drawer-layout.html">
|
||
|
|
<link rel="import" href="../../bower_components/app-layout/app-drawer/app-drawer.html">
|
||
|
|
<link rel="import" href="../../bower_components/app-layout/app-scroll-effects/app-scroll-effects.html">
|
||
|
|
<link rel="import" href="../../bower_components/app-layout/app-header/app-header.html">
|
||
|
|
<link rel="import" href="../../bower_components/app-layout/app-header-layout/app-header-layout.html">
|
||
|
|
<link rel="import" href="../../bower_components/app-layout/app-toolbar/app-toolbar.html">
|
||
|
|
<link rel="import" href="../../bower_components/paper-item/paper-item.html">
|
||
|
|
<link rel="import" href="../../bower_components/paper-item/paper-item-body.html">
|
||
|
|
<link rel="import" href="../../bower_components/paper-button/paper-button.html">
|
||
|
|
<link rel="import" href="../../bower_components/paper-card/paper-card.html">
|
||
|
|
<link rel="import" href="../../bower_components/paper-tabs/paper-tabs.html">
|
||
|
|
<link rel="import" href="../../bower_components/paper-icon-button/paper-icon-button.html">
|
||
|
|
<link rel="import" href="../../bower_components/iron-icons/iron-icons.html">
|
||
|
|
<link rel="import" href="../../bower_components/iron-ajax/iron-ajax.html">
|
||
|
|
<link rel="import" href="../../bower_components/iron-flex-layout/iron-flex-layout-classes.html">
|
||
|
|
<link rel="import" href="../../bower_components/polymer/lib/elements/dom-if.html">
|
||
|
|
<link rel="import" href="../../bower_components/polymer/lib/elements/dom-repeat.html">
|
||
|
|
<link rel="import" href="../../bower_components/app-route/app-location.html">
|
||
|
|
<link rel="import" href="../../bower_components/app-route/app-route.html">
|
||
|
|
|
||
|
|
<dom-module id="build-status">
|
||
|
|
<template>
|
||
|
|
<app-location route="{{route}}" use-hash-as-path></app-location>
|
||
|
|
<app-route route="{{route}}"
|
||
|
|
pattern=":project_name"
|
||
|
|
data="{{routeData}}">
|
||
|
|
</app-route>
|
||
|
|
<style is="custom-style" include="iron-flex iron-flex-alignment">
|
||
|
|
<style>
|
||
|
|
.paper-item-link {
|
||
|
|
color: inherit;
|
||
|
|
text-decoration: none;
|
||
|
|
}
|
||
|
|
|
||
|
|
a {
|
||
|
|
text-decoration: none;
|
||
|
|
}
|
||
|
|
|
||
|
|
app-header {
|
||
|
|
background-color: #2ba4ad;
|
||
|
|
color: #fff;
|
||
|
|
}
|
||
|
|
|
||
|
|
paper-button {
|
||
|
|
font-weight: normal;
|
||
|
|
font-size: 14px;
|
||
|
|
-webkit-font-smoothing: antialiased;
|
||
|
|
}
|
||
|
|
|
||
|
|
paper-button.green:hover {
|
||
|
|
background-color: var(--paper-green-400);
|
||
|
|
}
|
||
|
|
|
||
|
|
paper-button.green {
|
||
|
|
background-color: var(--paper-green-500);
|
||
|
|
color: white;
|
||
|
|
}
|
||
|
|
|
||
|
|
paper-card {
|
||
|
|
margin: 0.5em;
|
||
|
|
}
|
||
|
|
|
||
|
|
paper-item {
|
||
|
|
cursor: pointer;
|
||
|
|
}
|
||
|
|
|
||
|
|
paper-tabs {
|
||
|
|
-webkit-font-smoothing: antialiased;
|
||
|
|
width: 100%;
|
||
|
|
margin-bottom: 1px;
|
||
|
|
height: 40px;
|
||
|
|
}
|
||
|
|
|
||
|
|
:host {
|
||
|
|
display: block;
|
||
|
|
}
|
||
|
|
|
||
|
|
.icon-error {
|
||
|
|
color: #e83030;
|
||
|
|
margin-right: 0.2em;
|
||
|
|
}
|
||
|
|
|
||
|
|
.icon-success {
|
||
|
|
color: var(--paper-green-500);
|
||
|
|
margin-right: 0.2em;
|
||
|
|
}
|
||
|
|
|
||
|
|
.icon-waiting {
|
||
|
|
color: var(--paper-yellow-500);
|
||
|
|
margin-right: 0.2em;
|
||
|
|
}
|
||
|
|
|
||
|
|
.projects {
|
||
|
|
min-width: 10em;
|
||
|
|
}
|
||
|
|
|
||
|
|
.log {
|
||
|
|
width: 80%;
|
||
|
|
display: inline;
|
||
|
|
}
|
||
|
|
|
||
|
|
.buildHistory {
|
||
|
|
margin: 20px 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
pre {
|
||
|
|
white-space: pre-wrap;
|
||
|
|
}
|
||
|
|
</style>
|
||
|
|
<app-header reveals>
|
||
|
|
<app-toolbar>
|
||
|
|
<div main-title>OSS-Fuzz build status</div>
|
||
|
|
<div><small>(Updated every 30 minutes)</small></div>
|
||
|
|
</app-toolbar>
|
||
|
|
</app-header>
|
||
|
|
<div class="layout horizontal">
|
||
|
|
<paper-card class="projects">
|
||
|
|
<div class="card-tabs">
|
||
|
|
<paper-tabs selected="fuzzing" id="build_type" attr-for-selected="type" on-click="onChanged">
|
||
|
|
<paper-tab type="fuzzing">Fuzzing Builds</paper-tab>
|
||
|
|
<paper-tab type="coverage">Coverage Builds</paper-tab>
|
||
|
|
</paper-tabs>
|
||
|
|
</div>
|
||
|
|
<div class="card-content">
|
||
|
|
<template is="dom-repeat" items="[[status.projects]]" as="project">
|
||
|
|
<paper-item on-tap="onTap">
|
||
|
|
<paper-item-body two-line>
|
||
|
|
<div>
|
||
|
|
<template is="dom-if" if="[[!isSuccessful(project)]]">
|
||
|
|
<iron-icon class="icon-error" icon="icons:error"></iron-icon>
|
||
|
|
</template>
|
||
|
|
<template is="dom-if" if="[[!project.history.length]]">
|
||
|
|
<iron-icon class="icon-waiting" icon="icons:error"></iron-icon>
|
||
|
|
</template>
|
||
|
|
[[project.name]]
|
||
|
|
</div>
|
||
|
|
<template is="dom-if" if="[[project.history.length]]">
|
||
|
|
<div secondary title$="Last built [[toLocalDate(project.finish_time)]]">
|
||
|
|
Last built [[toLocalDate(project.history.0.finish_time)]]
|
||
|
|
</div>
|
||
|
|
</template>
|
||
|
|
<template is="dom-if" if="[[!project.history.length]]">
|
||
|
|
<div secondary title$="Not built yet">
|
||
|
|
Not built yet
|
||
|
|
</div>
|
||
|
|
</template>
|
||
|
|
</paper-item-body>
|
||
|
|
</paper-item>
|
||
|
|
</template>
|
||
|
|
</div>
|
||
|
|
</paper-card>
|
||
|
|
<paper-card class="log">
|
||
|
|
<div class="card-content">
|
||
|
|
<template is="dom-if" if="[[!project]]">
|
||
|
|
Select a project to see logs.
|
||
|
|
</template>
|
||
|
|
<template is="dom-if" if="[[build_history.length]]">
|
||
|
|
Last Successful build:
|
||
|
|
<template is="dom-if" if="[[last_successful_build]]">
|
||
|
|
<paper-button raised on-click="onLastBuildSuccessful" class="green">
|
||
|
|
[[getLocalDate(last_successful_build.finish_time)]]
|
||
|
|
</paper-button>
|
||
|
|
</template>
|
||
|
|
<template is="dom-if" if="[[!last_successful_build]]">
|
||
|
|
None yet.
|
||
|
|
</template>
|
||
|
|
<div class="buildHistory">
|
||
|
|
Build History: <br>
|
||
|
|
<template is="dom-repeat" items="[[build_history]]" as="history">
|
||
|
|
<paper-button raised on-click="onBuildHistory">
|
||
|
|
<template is="dom-if" if="[[history.success]]">
|
||
|
|
<iron-icon class="icon-success" icon="icons:done"></iron-icon>
|
||
|
|
</template>
|
||
|
|
<template is="dom-if" if="[[!history.success]]">
|
||
|
|
<iron-icon class="icon-error" icon="icons:error"></iron-icon>
|
||
|
|
</template>
|
||
|
|
[[getLocalDate(history.finish_time)]]
|
||
|
|
</paper-button>
|
||
|
|
</template>
|
||
|
|
</div>
|
||
|
|
<template is="dom-if" if=[[!finish_time]]>
|
||
|
|
<pre>Select a build to see logs.</pre>
|
||
|
|
</template>
|
||
|
|
<template is="dom-if" if="[[finish_time]]">
|
||
|
|
<a href="/log-[[build_id]].txt" target="_blank" tabindex="-1">
|
||
|
|
<paper-button raised>
|
||
|
|
Open in new tab
|
||
|
|
<iron-icon icon="icons:link"></iron-iron>
|
||
|
|
</paper-button>
|
||
|
|
</a>
|
||
|
|
<pre>Finish Time : [[finish_time]]</pre>
|
||
|
|
</template>
|
||
|
|
</template>
|
||
|
|
<template is="dom-if" if="[[loading_log]]">
|
||
|
|
Loading...
|
||
|
|
</template>
|
||
|
|
<pre>[[log]]</pre>
|
||
|
|
</div>
|
||
|
|
</paper-card>
|
||
|
|
</div>
|
||
|
|
<iron-ajax id="status_fuzzing" auto handle-as="json" url="/status.json" on-response="onResponseForFuzzing"></iron-ajax>
|
||
|
|
<iron-ajax id="status_coverage" auto handle-as="json" url="/status-coverage.json" on-response="onResponseForCoverage"></iron-ajax>
|
||
|
|
<iron-ajax id="logxhr" handle-as="text" on-response="onLogResponse"></iron-ajax>
|
||
|
|
</template>
|
||
|
|
|
||
|
|
<script>
|
||
|
|
/** @polymerElement */
|
||
|
|
class BuildStatus extends Polymer.Element {
|
||
|
|
static get is() {
|
||
|
|
return "build-status";
|
||
|
|
}
|
||
|
|
static get properties() {
|
||
|
|
return {
|
||
|
|
log: {
|
||
|
|
type: String,
|
||
|
|
value: '',
|
||
|
|
},
|
||
|
|
loading_log: {
|
||
|
|
type: Boolean,
|
||
|
|
value: false,
|
||
|
|
},
|
||
|
|
finish_time: {
|
||
|
|
type: String,
|
||
|
|
value: '',
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
static get observers() {
|
||
|
|
return ["_routeChanged(route.*)"]
|
||
|
|
}
|
||
|
|
|
||
|
|
_routeChanged() {
|
||
|
|
if(!this.routeData.project_name)
|
||
|
|
this.project = "";
|
||
|
|
if (!this.status || !this.routeData.project_name) {
|
||
|
|
// If our status json is loaded and there is a project_name specified
|
||
|
|
// in the URL, we can proceed to load that project's log.
|
||
|
|
return
|
||
|
|
}
|
||
|
|
this.project = this.getProjectByName(this.routeData.project_name);
|
||
|
|
this.build_history = this.project.history;
|
||
|
|
if (this.project['last_successful_build']){
|
||
|
|
this.last_successful_build = this.project.last_successful_build;
|
||
|
|
} else{
|
||
|
|
this.last_successful_build = "";
|
||
|
|
}
|
||
|
|
this.log = "";
|
||
|
|
this.finish_time = "";
|
||
|
|
}
|
||
|
|
|
||
|
|
getProjectByName(project_name) {
|
||
|
|
return this.status.projects.find(
|
||
|
|
p => p.name === project_name
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
onResponseForFuzzing(e) {
|
||
|
|
this.status_fuzzing = e.detail.response;
|
||
|
|
if (!this.status) {
|
||
|
|
// Show status of the fuzzing builds by default.
|
||
|
|
this.status = this.status_fuzzing;
|
||
|
|
// Manually invoke a _routeChanged call, in order to load the log for
|
||
|
|
// someone going directly to a project's URL.
|
||
|
|
this._routeChanged();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
onResponseForCoverage(e) {
|
||
|
|
this.status_coverage = e.detail.response;
|
||
|
|
}
|
||
|
|
|
||
|
|
onLogResponse(e) {
|
||
|
|
this.log = e.detail.response;
|
||
|
|
this.loading_log = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
onTap(e) {
|
||
|
|
// Change the route, this should auto-magically update the url in the
|
||
|
|
// browser and invoke the _routeChanged method.
|
||
|
|
this.set("route.path", e.model.project.name);
|
||
|
|
}
|
||
|
|
|
||
|
|
onChanged(e) {
|
||
|
|
if (this.$.build_type.selected == "coverage") {
|
||
|
|
this.status = this.status_coverage;
|
||
|
|
} else {
|
||
|
|
this.status = this.status_fuzzing;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
requestLog() {
|
||
|
|
var ajax = this.$.logxhr;
|
||
|
|
ajax.url = "/log-" + this.build_id + ".txt";
|
||
|
|
ajax.generateRequest();
|
||
|
|
this.loading_log = true;
|
||
|
|
this.log = "";
|
||
|
|
this.finish_time = this.toLocalDate(this.finish_time);
|
||
|
|
}
|
||
|
|
|
||
|
|
onLastBuildSuccessful(e) {
|
||
|
|
this.build_id = this.last_successful_build.build_id
|
||
|
|
this.finish_time = this.last_successful_build.finish_time
|
||
|
|
this.requestLog()
|
||
|
|
}
|
||
|
|
|
||
|
|
onBuildHistory(e) {
|
||
|
|
this.build_id = e.model.history.build_id
|
||
|
|
this.finish_time = e.model.history.finish_time
|
||
|
|
this.requestLog()
|
||
|
|
}
|
||
|
|
|
||
|
|
showLog(log) {
|
||
|
|
return log !== "";
|
||
|
|
}
|
||
|
|
|
||
|
|
showTabs() {
|
||
|
|
return this.tab_count !== 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
toLocalDate(str) {
|
||
|
|
let date = new Date(str);
|
||
|
|
let ds = date.toString();
|
||
|
|
let timezone = ds.substring(ds.indexOf("("));
|
||
|
|
return date.toLocaleString() + " " + timezone;
|
||
|
|
}
|
||
|
|
|
||
|
|
pad(n) {
|
||
|
|
return n<10 ? '0'+n : n;
|
||
|
|
}
|
||
|
|
|
||
|
|
getLocalDate(str) {
|
||
|
|
let date = new Date(str);
|
||
|
|
return date.toLocaleString()
|
||
|
|
}
|
||
|
|
|
||
|
|
isSuccessful(project) {
|
||
|
|
return (!project.history.length || project.history[0].success)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
window.customElements.define(BuildStatus.is, BuildStatus)
|
||
|
|
</script>
|
||
|
|
</dom-module>
|