@@ -82,6 +82,77 @@ the string is returned unchanged."
8282 (when substituted
8383 (return substituted)))))
8484
85+ (defun reconstitute (string patterns)
86+ (let ((result (maybe-reconstitute string patterns)))
87+ (and (not (eq result string ))
88+ result)))
89+
90+ ; ;; Linking to VCS sources from log lines
91+
92+ (defun parse-vcs-source-log-line (log-line)
93+ " Return a plist of info about log-line."
94+ (when (and (search " dist/build-cache/" log-line)
95+ (not (search " .cache/common-lisp" log-line)))
96+ (ppcre :register-groups-bind (project-name full-path)
97+ (" dist/build-cache/(.*?)/(.*$)" log-line)
98+ (let* ((pos 0 )
99+ (second-slash
100+ (dotimes (i 2 pos)
101+ (setf pos (position #\/ full-path :start (1+ pos)))))
102+ (end (and second-slash (position #\& full-path :start second-slash))))
103+ (let ((source (find-source project-name)))
104+ (when (and source second-slash)
105+ (let* ((path (subseq full-path (1+ second-slash) end))
106+ (start (search path log-line))
107+ (line-number nil ))
108+ (ppcre :register-groups-bind ((#' parse-integer log-line-number)) (" Line: (\\ d+)" log-line)
109+ (setf line-number log-line-number))
110+ (list :source source
111+ :path path
112+ :line-number line-number
113+ :path-bounds (cons start (+ start (length path)))))))))))
114+
115+ (defparameter *location-base-substitutions*
116+ ' ((" (https://github.com/.*?/.*?)\\ .git" 0 " /blob/" )
117+ (" (https://.*gitlab.*)\\ .git$" 0 " /blob/" )
118+ (" (https://bitbucket.org/.*?)\\ .git$" 0 " /src/" )
119+ (" (http://dwim.hu/live/.*$)" 0 )))
120+
121+ (defun location-base (location)
122+ (reconstitute location *location-base-substitutions* ))
123+
124+ (defun source-branch (source)
125+ (typecase source
126+ (tagged-mixin
127+ (tag-data source))
128+ (git-source
129+ " master" )
130+ (t
131+ nil )))
132+
133+ (defun source-file-link-base (source)
134+ (let ((base (location-base (location source))))
135+ (when base
136+ (format nil " ~A ~@[ ~A /~] " base (source-branch source)))))
137+
138+ (defun source-file-link (source path line-number)
139+ (let ((base (source-file-link-base source)))
140+ (when base
141+ (format nil " ~A~A ~@[ #L~A ~] " base path line-number))))
142+
143+ (defun link-subseq (line link bounds)
144+ (destructuring-bind (start . end)
145+ bounds
146+ (concatenate ' string
147+ (subseq line 0 start)
148+ " <a href='"
149+ link
150+ " '>"
151+ (subseq line start end)
152+ " </a>"
153+ (subseq line end))))
154+
155+
85156; ;; Posting to S3
86157
87158(defun report-publishing-enabled-p ()
@@ -90,8 +161,8 @@ the string is returned unchanged."
90161(defun content-type (file)
91162 (cond ((equalp (pathname-type file) " css" )
92163 " text/css" )
93- ((equalp (pathname-type file) " rss" )
94- " application/rss+xml" )
164+ ((equalp (pathname-type file) " rss" )
165+ " application/rss+xml" )
95166 (t
96167 " text/html" )))
97168
@@ -124,9 +195,9 @@ the string is returned unchanged."
124195(defgeneric full-failure-report-url (object)
125196 (:method (object)
126197 (format nil " http://~A /~A~A "
127- *failtail-bucket*
128- (report-prefix)
129- (failure-report-url object))))
198+ *failtail-bucket*
199+ (report-prefix)
200+ (failure-report-url object))))
130201(defgeneric failure-report-html-file (base object))
131202
132203(defgeneric stylesheet-path (object))
@@ -161,8 +232,10 @@ the string is returned unchanged."
161232
162233(defmethod new-failure-p ((object failing-system))
163234 (let* ((dist (ql-dist :find-dist " quicklisp" ))
164- (system (ql-dist :find-system-in-dist (system-name object) dist)))
165- (not (not system))))
235+ (existing-system
236+ (ql-dist :find-system-in-dist (system-name object) dist)))
237+ (or (not (not existing-system))
238+ (< (days-old (source object)) 30 ))))
166239
167240(defmethod failure-data ((source upstream-source))
168241 (let ((result ' ()))
@@ -260,61 +333,61 @@ source is found that matches the filename, return nil."
260333 ; ; reason), so don't try to make a failing-source in that case.
261334 (let ((source (find-source source-name)))
262335 (when source
263- (make-instance ' failing-system
264- :source (find-source source-name)
265- :failure-log-file failure-file
266- :system-file-name system-file-name
267- :system-name (decode-string-from-filesystem
268- failing-system))))))
336+ (make-instance ' failing-system
337+ :source (find-source source-name)
338+ :failure-log-file failure-file
339+ :system-file-name system-file-name
340+ :system-name (decode-string-from-filesystem
341+ failing-system))))))
269342
270343(defun failing-source-log-files ()
271344 (let* ((base (translate-logical-pathname " quicklisp-controller:dist;build-artifacts;" ))
272- (fail-wild (merge-pathnames " **/fail_*_*_*.txt" base)))
345+ (fail-wild (merge-pathnames " **/fail_*_*_*.txt" base)))
273346 (directory fail-wild)))
274347
275348(defun failing-systems ()
276349 (remove nil
277- (mapcar #' parse-failure-file-name
278- (failing-source-log-files))))
350+ (mapcar #' parse-failure-file-name
351+ (failing-source-log-files))))
279352
280353(defun failure-log-failure-report ()
281354 " Scan the failure log files of all projects to produce a failure report."
282355 (let ((systems (make-hash-table :test ' equal)))
283356 (flet ((fsource (source)
284- (or (gethash (name source) systems)
285- (setf (gethash (name source) systems)
286- (make-instance ' failing-source
287- :failure-data nil
288- :source source)))))
357+ (or (gethash (name source) systems)
358+ (setf (gethash (name source) systems)
359+ (make-instance ' failing-source
360+ :failure-data nil
361+ :source source)))))
289362 (let ((table (make-hash-table :test ' eq))
290- (systems (failing-systems))
291- (report (make-instance ' failure-report
292- :failure-data ' ())))
293- (dolist (system systems)
294- (let ((key (fsource (source system))))
295- (push system (gethash key table))))
296- (maphash (lambda (failing-source failing-systems)
297- (setf (failure-data failing-source) failing-systems)
298- (push failing-source (failure-data report)))
299- table)
300- report))))
363+ (systems (failing-systems))
364+ (report (make-instance ' failure-report
365+ :failure-data ' ())))
366+ (dolist (system systems)
367+ (let ((key (fsource (source system))))
368+ (push system (gethash key table))))
369+ (maphash (lambda (failing-source failing-systems)
370+ (setf (failure-data failing-source) failing-systems)
371+ (push failing-source (failure-data report)))
372+ table)
373+ report))))
301374
302375(defmethod failure-data ((object (eql t )))
303376 (failure-log-failure-report))
304377
305378(defparameter *log-lines-that-are-boring*
306379 (mapcar ' ppcre:create-scanner
307- ' (" ^WARNING:" )))
380+ ' (" ^WARNING:" )))
308381
309382(defparameter *log-lines-to-highlight*
310383 (mapcar ' ppcre:create-scanner
311384 ' (" ^; caught (WARNING|ERROR):"
312385 " READ error during"
313- " ^Backtrace for"
386+ " ^Backtrace for"
314387 " ^Unhandled" )))
315388
316389(defparameter *failure-log-reconstitution-patterns*
317- ' ((" (The ANSI Standard, Section )([0-9.]*)"
390+ ' ((" (^.* The ANSI Standard, Section )([0-9.]*)"
318391 0 " <a href='http://l1sp.org/cl/" 1 " '>" 1 " </a>" )))
319392
320393(defun failure-log-reconstitute-line (line)
@@ -363,18 +436,25 @@ source is found that matches the filename, return nil."
363436 (loop for line = (read-line log-stream nil )
364437 while line
365438 do
366- (setf line (cl-who :escape-string line))
367- (cond ((highlighted-log-line-p line)
368- (write-string " <strong>" stream )
369- (write-string line stream )
370- (write-string " </strong>" stream )
371- (terpri stream ))
372- ((boring-log-line-p line)
373- (format stream " <span class='boring'>~A~% </span>" line))
374- (t
375- (write-line line stream )))))
439+ (setf line (failure-log-reconstitute-line (cl-who :escape-string line)))
440+ (let ((upstream-info (parse-vcs-source-log-line line)))
441+ (when upstream-info
442+ (destructuring-bind (&key source path path-bounds line-number &allow-other-keys )
443+ upstream-info
444+ (let ((link (source-file-link source path line-number)))
445+ (when link
446+ (setf line (link-subseq line link path-bounds)))))))
447+ (cond ((highlighted-log-line-p line)
448+ (write-string " <strong>" stream )
449+ (write-string line stream )
450+ (write-string " </strong>" stream )
451+ (terpri stream ))
452+ ((boring-log-line-p line)
453+ (format stream " <span class='boring'>~A~% </span>" line))
454+ (t
455+ (write-line line stream ))))
376456 (format stream " </pre>" )
377- (format stream " </div>~% " ))
457+ (format stream " </div>~% " )))
378458
379459(defmethod write-html-failure-report-content ((source failing-source) stream )
380460 (dolist (system (failure-data source))
0 commit comments