• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1<!-- Copyright 2020 Google Inc.
2Licensed under the Apache License, Version 2.0 (the "License");
3you may not use this file except in compliance with the License.
4You may obtain a copy of the License at
5     http://www.apache.org/licenses/LICENSE-2.0
6Unless required by applicable law or agreed to in writing, software
7distributed under the License is distributed on an "AS IS" BASIS,
8WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9See the License for the specific language governing permissions and
10limitations under the License. -->
11
12<link rel="import" href="../../bower_components/polymer/polymer-element.html">
13<link rel="import" href="../../bower_components/app-layout/app-drawer-layout/app-drawer-layout.html">
14<link rel="import" href="../../bower_components/app-layout/app-drawer/app-drawer.html">
15<link rel="import" href="../../bower_components/app-layout/app-scroll-effects/app-scroll-effects.html">
16<link rel="import" href="../../bower_components/app-layout/app-header/app-header.html">
17<link rel="import" href="../../bower_components/app-layout/app-header-layout/app-header-layout.html">
18<link rel="import" href="../../bower_components/app-layout/app-toolbar/app-toolbar.html">
19<link rel="import" href="../../bower_components/paper-item/paper-item.html">
20<link rel="import" href="../../bower_components/paper-item/paper-item-body.html">
21<link rel="import" href="../../bower_components/paper-button/paper-button.html">
22<link rel="import" href="../../bower_components/paper-card/paper-card.html">
23<link rel="import" href="../../bower_components/paper-tabs/paper-tabs.html">
24<link rel="import" href="../../bower_components/paper-icon-button/paper-icon-button.html">
25<link rel="import" href="../../bower_components/iron-icons/iron-icons.html">
26<link rel="import" href="../../bower_components/iron-ajax/iron-ajax.html">
27<link rel="import" href="../../bower_components/iron-flex-layout/iron-flex-layout-classes.html">
28<link rel="import" href="../../bower_components/polymer/lib/elements/dom-if.html">
29<link rel="import" href="../../bower_components/polymer/lib/elements/dom-repeat.html">
30<link rel="import" href="../../bower_components/app-route/app-location.html">
31<link rel="import" href="../../bower_components/app-route/app-route.html">
32
33<dom-module id="build-status">
34  <template>
35    <app-location route="{{route}}" use-hash-as-path></app-location>
36    <app-route route="{{route}}"
37      pattern=":project_name"
38      data="{{routeData}}">
39    </app-route>
40    <style is="custom-style" include="iron-flex iron-flex-alignment">
41    <style>
42      .paper-item-link {
43        color: inherit;
44        text-decoration: none;
45      }
46
47      a {
48        text-decoration: none;
49      }
50
51      app-header {
52        background-color: #2ba4ad;
53        color: #fff;
54      }
55
56      paper-button {
57        font-weight: normal;
58        font-size: 14px;
59        -webkit-font-smoothing: antialiased;
60      }
61
62      paper-button.green:hover {
63        background-color: var(--paper-green-400);
64      }
65
66      paper-button.green {
67        background-color: var(--paper-green-500);
68        color: white;
69      }
70
71      paper-card {
72        margin: 0.5em;
73      }
74
75      paper-item {
76        cursor: pointer;
77      }
78
79      paper-tabs {
80        -webkit-font-smoothing: antialiased;
81        width: 100%;
82        margin-bottom: 1px;
83        height: 40px;
84      }
85
86      :host {
87        display: block;
88      }
89
90      .icon-error {
91        color: #e83030;
92        margin-right: 0.2em;
93      }
94
95      .icon-success {
96        color: var(--paper-green-500);
97        margin-right: 0.2em;
98      }
99
100      .icon-waiting {
101        color: var(--paper-yellow-500);
102        margin-right: 0.2em;
103      }
104
105      .projects {
106        min-width: 10em;
107      }
108
109      .log {
110        width: 80%;
111        display: inline;
112      }
113
114      .buildHistory {
115        margin: 20px 0;
116      }
117
118      pre {
119        white-space: pre-wrap;
120      }
121    </style>
122    <app-header reveals>
123      <app-toolbar>
124        <div main-title>OSS-Fuzz build status</div>
125        <div><small>(Updated every 30 minutes)</small></div>
126      </app-toolbar>
127    </app-header>
128    <div class="layout horizontal">
129      <paper-card class="projects">
130        <div class="card-tabs">
131          <paper-tabs selected="fuzzing" id="build_type" attr-for-selected="type" on-click="onChanged">
132            <paper-tab type="fuzzing">Fuzzing Builds</paper-tab>
133            <paper-tab type="coverage">Coverage Builds</paper-tab>
134          </paper-tabs>
135        </div>
136        <div class="card-content">
137          <template is="dom-repeat" items="[[status.projects]]" as="project">
138            <paper-item on-tap="onTap">
139              <paper-item-body two-line>
140                <div>
141                  <template is="dom-if" if="[[!isSuccessful(project)]]">
142                    <iron-icon class="icon-error" icon="icons:error"></iron-icon>
143                  </template>
144                  <template is="dom-if" if="[[!project.history.length]]">
145                    <iron-icon class="icon-waiting" icon="icons:error"></iron-icon>
146                  </template>
147                  [[project.name]]
148                </div>
149                <template is="dom-if" if="[[project.history.length]]">
150                 <div secondary title$="Last built [[toLocalDate(project.finish_time)]]">
151                    Last built [[toLocalDate(project.history.0.finish_time)]]
152                 </div>
153                </template>
154                <template is="dom-if" if="[[!project.history.length]]">
155                  <div secondary title$="Not built yet">
156                    Not built yet
157                  </div>
158                </template>
159              </paper-item-body>
160            </paper-item>
161          </template>
162        </div>
163      </paper-card>
164      <paper-card class="log">
165        <div class="card-content">
166          <template is="dom-if" if="[[!project]]">
167            Select a project to see logs.
168          </template>
169          <template is="dom-if" if="[[build_history.length]]">
170            Last Successful build:
171            <template is="dom-if" if="[[last_successful_build]]">
172              <paper-button raised on-click="onLastBuildSuccessful" class="green">
173                [[getLocalDate(last_successful_build.finish_time)]]
174              </paper-button>
175            </template>
176            <template is="dom-if" if="[[!last_successful_build]]">
177              None yet.
178            </template>
179              <div class="buildHistory">
180                Build History: <br>
181                <template is="dom-repeat" items="[[build_history]]" as="history">
182                  <paper-button raised on-click="onBuildHistory">
183                    <template is="dom-if" if="[[history.success]]">
184                      <iron-icon class="icon-success" icon="icons:done"></iron-icon>
185                    </template>
186                    <template is="dom-if" if="[[!history.success]]">
187                      <iron-icon class="icon-error" icon="icons:error"></iron-icon>
188                    </template>
189                    [[getLocalDate(history.finish_time)]]
190                  </paper-button>
191                </template>
192              </div>
193              <template is="dom-if" if=[[!finish_time]]>
194              <pre>Select a build to see logs.</pre>
195            </template>
196              <template is="dom-if" if="[[finish_time]]">
197                <a href="/log-[[build_id]].txt" target="_blank" tabindex="-1">
198                  <paper-button raised>
199                    Open in new tab
200                    <iron-icon icon="icons:link"></iron-iron>
201                  </paper-button>
202                </a>
203                <pre>Finish Time : [[finish_time]]</pre>
204              </template>
205          </template>
206          <template is="dom-if" if="[[loading_log]]">
207            Loading...
208          </template>
209          <pre>[[log]]</pre>
210        </div>
211      </paper-card>
212    </div>
213      <iron-ajax id="status_fuzzing" auto handle-as="json" url="/status.json" on-response="onResponseForFuzzing"></iron-ajax>
214      <iron-ajax id="status_coverage" auto handle-as="json" url="/status-coverage.json" on-response="onResponseForCoverage"></iron-ajax>
215    <iron-ajax id="logxhr" handle-as="text" on-response="onLogResponse"></iron-ajax>
216  </template>
217
218  <script>
219    /** @polymerElement */
220    class BuildStatus extends Polymer.Element {
221      static get is() {
222        return "build-status";
223      }
224      static get properties() {
225        return {
226          log: {
227            type: String,
228            value: '',
229          },
230          loading_log: {
231            type: Boolean,
232            value: false,
233          },
234          finish_time: {
235            type: String,
236            value: '',
237          }
238        }
239      }
240      static get observers() {
241        return ["_routeChanged(route.*)"]
242      }
243
244      _routeChanged() {
245        if(!this.routeData.project_name)
246          this.project = "";
247        if (!this.status || !this.routeData.project_name) {
248          // If our status json is loaded and there is a project_name specified
249          // in the URL, we can proceed to load that project's log.
250          return
251        }
252        this.project = this.getProjectByName(this.routeData.project_name);
253        this.build_history = this.project.history;
254        if (this.project['last_successful_build']){
255          this.last_successful_build = this.project.last_successful_build;
256        } else{
257          this.last_successful_build = "";
258        }
259        this.log = "";
260        this.finish_time = "";
261      }
262
263      getProjectByName(project_name) {
264        return this.status.projects.find(
265          p => p.name === project_name
266        );
267      }
268
269      onResponseForFuzzing(e) {
270        this.status_fuzzing = e.detail.response;
271        if (!this.status) {
272          // Show status of the fuzzing builds by default.
273          this.status = this.status_fuzzing;
274          // Manually invoke a _routeChanged call, in order to load the log for
275          // someone going directly to a project's URL.
276          this._routeChanged();
277        }
278      }
279
280      onResponseForCoverage(e) {
281        this.status_coverage = e.detail.response;
282      }
283
284      onLogResponse(e) {
285        this.log = e.detail.response;
286        this.loading_log = false;
287      }
288
289      onTap(e) {
290        // Change the route, this should auto-magically update the url in the
291        // browser and invoke the _routeChanged method.
292        this.set("route.path", e.model.project.name);
293      }
294
295      onChanged(e) {
296        if (this.$.build_type.selected == "coverage") {
297          this.status = this.status_coverage;
298        } else {
299          this.status = this.status_fuzzing;
300        }
301      }
302
303      requestLog() {
304        var ajax = this.$.logxhr;
305        ajax.url = "/log-" + this.build_id + ".txt";
306        ajax.generateRequest();
307        this.loading_log = true;
308        this.log = "";
309        this.finish_time = this.toLocalDate(this.finish_time);
310      }
311
312      onLastBuildSuccessful(e) {
313        this.build_id = this.last_successful_build.build_id
314        this.finish_time = this.last_successful_build.finish_time
315        this.requestLog()
316      }
317
318      onBuildHistory(e) {
319        this.build_id = e.model.history.build_id
320        this.finish_time = e.model.history.finish_time
321        this.requestLog()
322      }
323
324      showLog(log) {
325        return log !== "";
326      }
327
328      showTabs() {
329        return this.tab_count !== 0;
330      }
331
332      toLocalDate(str) {
333        let date = new Date(str);
334        let ds = date.toString();
335        let timezone = ds.substring(ds.indexOf("("));
336        return date.toLocaleString() + " " + timezone;
337      }
338
339      pad(n) {
340        return n<10 ? '0'+n : n;
341      }
342
343      getLocalDate(str) {
344        let date = new Date(str);
345        return date.toLocaleString()
346      }
347
348      isSuccessful(project) {
349        return (!project.history.length || project.history[0].success)
350      }
351    }
352
353    window.customElements.define(BuildStatus.is, BuildStatus)
354  </script>
355</dom-module>
356