Bring back log engine from master
This commit is contained in:
parent
03dbe11c55
commit
b1edd8e510
|
@ -24,5 +24,5 @@ Travis.BuildController = Ember.Controller.extend
|
||||||
).property('commit.committerEmail')
|
).property('commit.committerEmail')
|
||||||
|
|
||||||
hasLoaded: (->
|
hasLoaded: (->
|
||||||
@set('controllers.log.job', @get('build.firstJob')) if @get('build.isLoaded') && !@get('build.isMatrix')
|
@set('controllers.log.job', @get('build.firstJob')) if @get('build.firstJob') && !@get('build.isMatrix')
|
||||||
).observes('build.id', 'loading')
|
).observes('build.id', 'build.firstJob')
|
||||||
|
|
|
@ -31,7 +31,7 @@ require 'travis/model'
|
||||||
).property('jobs.length')
|
).property('jobs.length')
|
||||||
|
|
||||||
firstJob: (->
|
firstJob: (->
|
||||||
@get('jobs.firstObject')
|
@get('jobs').objectAt(0)
|
||||||
).property('jobs.length')
|
).property('jobs.length')
|
||||||
|
|
||||||
isFinished: (->
|
isFinished: (->
|
||||||
|
|
|
@ -26,6 +26,7 @@ require 'travis/model'
|
||||||
_config: DS.attr('object')
|
_config: DS.attr('object')
|
||||||
|
|
||||||
log: ( ->
|
log: ( ->
|
||||||
|
@set('isLogAccessed', true)
|
||||||
Travis.Log.create(job: this)
|
Travis.Log.create(job: this)
|
||||||
).property()
|
).property()
|
||||||
|
|
||||||
|
@ -46,7 +47,9 @@ require 'travis/model'
|
||||||
).property('state')
|
).property('state')
|
||||||
|
|
||||||
clearLog: ->
|
clearLog: ->
|
||||||
@get('log').clear()
|
# This is needed if we don't want to fetch log just to clear it
|
||||||
|
if @get('isLogAccessed')
|
||||||
|
@get('log').clear()
|
||||||
|
|
||||||
sponsor: (->
|
sponsor: (->
|
||||||
worker = @get('log.workerName')
|
worker = @get('log.workerName')
|
||||||
|
|
|
@ -1,7 +1,159 @@
|
||||||
require 'log'
|
require 'log'
|
||||||
|
require 'travis/ordered_log'
|
||||||
|
|
||||||
Log.DEBUG = true
|
Log.DEBUG = true
|
||||||
|
|
||||||
|
Travis.UnorderedLogEngineMixin = Ember.Mixin.create
|
||||||
|
setupEngine: ->
|
||||||
|
console.log 'log view: create engine' if Log.DEBUG
|
||||||
|
@limit = new Log.Limit
|
||||||
|
@scroll = new Log.Scroll
|
||||||
|
@engine = Log.create(listeners: [new Log.FragmentRenderer, new Log.Folds, @scroll])
|
||||||
|
@observeParts()
|
||||||
|
@numberLineOnHover()
|
||||||
|
|
||||||
|
destroyEngine: ->
|
||||||
|
parts = @get('log.parts')
|
||||||
|
parts.removeArrayObserver(@, didChange: 'partsDidChange', willChange: 'noop')
|
||||||
|
|
||||||
|
observeParts: ->
|
||||||
|
parts = @get('log.parts')
|
||||||
|
parts.addArrayObserver(@, didChange: 'partsDidChange', willChange: 'noop')
|
||||||
|
parts = parts.slice(0)
|
||||||
|
@partsDidChange(parts, 0, null, parts.length)
|
||||||
|
|
||||||
|
partsDidChange: (parts, start, _, added) ->
|
||||||
|
console.log 'log view: parts did change' if Log.DEBUG
|
||||||
|
for part, i in parts.slice(start, start + added)
|
||||||
|
# console.log "limit in log view: #{@get('limited')}"
|
||||||
|
break if @get('limited')
|
||||||
|
@engine.set(part.number, part.content)
|
||||||
|
@propertyDidChange('limited')
|
||||||
|
|
||||||
|
lineNumberDidChange: (->
|
||||||
|
@scroll.set(number) if !@get('isDestroyed') && number = @get('controller.lineNumber')
|
||||||
|
).observes('controller.lineNumber')
|
||||||
|
|
||||||
|
Travis.OrderedLogEngineMixin = Ember.Mixin.create
|
||||||
|
setupEngine: ->
|
||||||
|
@set('logManager', Travis.OrderedLog.create(target: this))
|
||||||
|
|
||||||
|
@get('logManager').append @get('log.parts').map( (part) -> Ember.get(part, 'content') )
|
||||||
|
|
||||||
|
@get('log.parts').addArrayObserver this,
|
||||||
|
didChange: 'partsDidChange'
|
||||||
|
willChange: 'partsWillChange'
|
||||||
|
|
||||||
|
destroyEngine: (view) ->
|
||||||
|
@get('logManager').destroy()
|
||||||
|
@get('log.parts').removeArrayObserver this,
|
||||||
|
didChange: 'partsDidChange'
|
||||||
|
willChange: 'noop'
|
||||||
|
|
||||||
|
partsDidChange: (parts, index, removedCount, addedCount) ->
|
||||||
|
addedParts = parts.slice(index, index + addedCount).map( (part) -> Ember.get(part, 'content') )
|
||||||
|
@get('logManager').append addedParts
|
||||||
|
|
||||||
|
lineNumberDidChange: (->
|
||||||
|
if number = @get('controller.lineNumber')
|
||||||
|
@tryScrollingToHashLineNumber(number)
|
||||||
|
).observes('controller.lineNumber')
|
||||||
|
|
||||||
|
scrollTo: (id) ->
|
||||||
|
# and this is even more weird, when changing hash in URL in firefox
|
||||||
|
# to other value, for example #L10, it actually scrolls just #main
|
||||||
|
# element... this is probably some CSS issue, I don't have time to
|
||||||
|
# investigate at the moment
|
||||||
|
# TODO: fix this
|
||||||
|
$('#main').scrollTop 0
|
||||||
|
|
||||||
|
# weird, html works in chrome, body in firefox
|
||||||
|
$('html,body').scrollTop $(id).offset().top
|
||||||
|
|
||||||
|
@set 'controller.lineNumber', null
|
||||||
|
|
||||||
|
tryScrollingToHashLineNumber: (number) ->
|
||||||
|
id = "#L#{number}"
|
||||||
|
checker = =>
|
||||||
|
return if @get('isDestroyed')
|
||||||
|
|
||||||
|
if $(id).length
|
||||||
|
@scrollTo(id)
|
||||||
|
else
|
||||||
|
setTimeout checker, 100
|
||||||
|
|
||||||
|
checker()
|
||||||
|
|
||||||
|
appendLog: (payloads) ->
|
||||||
|
url = @get('logUrl')
|
||||||
|
|
||||||
|
leftOut = []
|
||||||
|
cut = false
|
||||||
|
fragment = document.createDocumentFragment()
|
||||||
|
|
||||||
|
# TODO: refactor this loop, it's getting messy
|
||||||
|
for payload in payloads
|
||||||
|
line = payload.content
|
||||||
|
number = payload.number
|
||||||
|
|
||||||
|
if payload.logWasCut
|
||||||
|
cut = true
|
||||||
|
else
|
||||||
|
unless payload.append
|
||||||
|
pathWithNumber = "#{url}#L#{number}"
|
||||||
|
p = document.createElement('p')
|
||||||
|
p.innerHTML = "<a href=\"#{pathWithNumber}\" id=\"L#{number}\"></a>#{line}"
|
||||||
|
line = p
|
||||||
|
|
||||||
|
if payload.fold && !payload.foldContinuation
|
||||||
|
div = document.createElement('div')
|
||||||
|
div.appendChild line
|
||||||
|
div.className = "fold #{payload.fold} show-first-line"
|
||||||
|
line = div
|
||||||
|
|
||||||
|
if payload.replace
|
||||||
|
if link = fragment.querySelector("#L#{number}")
|
||||||
|
link.parentElement.innerHTML = line.innerHTML
|
||||||
|
else
|
||||||
|
this.$("#L#{number}").parent().replaceWith line
|
||||||
|
else if payload.append
|
||||||
|
if link = fragment.querySelector("#L#{number}")
|
||||||
|
link.parentElement.innerHTML += line
|
||||||
|
else
|
||||||
|
this.$("#L#{number}").parent().append line
|
||||||
|
else if payload.foldContinuation
|
||||||
|
folds = fragment.querySelectorAll(".fold.#{payload.fold}")
|
||||||
|
if fold = folds[folds.length - 1]
|
||||||
|
fold.appendChild line
|
||||||
|
else
|
||||||
|
this.$("#log .fold.#{payload.fold}:last").append line
|
||||||
|
else
|
||||||
|
fragment.appendChild(line)
|
||||||
|
|
||||||
|
if payload.openFold
|
||||||
|
folds = fragment.querySelectorAll(".fold.#{payload.openFold}")
|
||||||
|
if fold = folds[folds.length - 1]
|
||||||
|
fold = $(fold)
|
||||||
|
else
|
||||||
|
fold = this.$(".fold.#{payload.openFold}:last")
|
||||||
|
|
||||||
|
fold.removeClass('show-first-line').addClass('open')
|
||||||
|
|
||||||
|
if payload.foldEnd
|
||||||
|
folds = fragment.querySelectorAll(".fold.#{payload.fold}")
|
||||||
|
if fold = folds[folds.length - 1]
|
||||||
|
fold = $(fold)
|
||||||
|
else
|
||||||
|
fold = this.$(".fold.#{payload.fold}:last")
|
||||||
|
|
||||||
|
fold.removeClass('show-first-line')
|
||||||
|
|
||||||
|
this.$('#log')[0].appendChild fragment
|
||||||
|
if cut
|
||||||
|
url = Travis.Urls.plainTextLog(@get('log.id'))
|
||||||
|
this.$("#log").append $("<p class=\"cut\">Log was too long to display. Download the <a href=\"#{url}\">the raw version</a> to get the full log.</p>")
|
||||||
|
|
||||||
|
|
||||||
Travis.reopen
|
Travis.reopen
|
||||||
LogView: Travis.View.extend
|
LogView: Travis.View.extend
|
||||||
templateName: 'jobs/log'
|
templateName: 'jobs/log'
|
||||||
|
@ -15,19 +167,18 @@ Travis.reopen
|
||||||
toTop: () ->
|
toTop: () ->
|
||||||
$(window).scrollTop(0)
|
$(window).scrollTop(0)
|
||||||
|
|
||||||
PreView: Em.View.extend
|
PreView: Em.View.extend(Travis.OrderedLogEngineMixin, {
|
||||||
templateName: 'jobs/pre'
|
templateName: 'jobs/pre'
|
||||||
|
|
||||||
didInsertElement: ->
|
didInsertElement: ->
|
||||||
console.log 'log view: did insert' if Log.DEBUG
|
console.log 'log view: did insert' if Log.DEBUG
|
||||||
@_super.apply this, arguments
|
@_super.apply this, arguments
|
||||||
@createEngine()
|
@setupEngine()
|
||||||
@lineNumberDidChange()
|
@lineNumberDidChange()
|
||||||
|
|
||||||
willDestroyElement: ->
|
willDestroyElement: ->
|
||||||
console.log 'log view: will destroy' if Log.DEBUG
|
console.log 'log view: will destroy' if Log.DEBUG
|
||||||
parts = @get('log.parts')
|
@destroyEngine()
|
||||||
parts.removeArrayObserver(@, didChange: 'partsDidChange', willChange: 'noop')
|
|
||||||
|
|
||||||
versionDidChange: (->
|
versionDidChange: (->
|
||||||
@rerender() if @get('inDOM')
|
@rerender() if @get('inDOM')
|
||||||
|
@ -38,35 +189,9 @@ Travis.reopen
|
||||||
@rerender() if @get('inDOM')
|
@rerender() if @get('inDOM')
|
||||||
).observes('log')
|
).observes('log')
|
||||||
|
|
||||||
createEngine: ->
|
#limited: (->
|
||||||
console.log 'log view: create engine' if Log.DEBUG
|
# @limit && @limit.limited
|
||||||
@limit = new Log.Limit
|
#).property()
|
||||||
@scroll = new Log.Scroll
|
|
||||||
@engine = Log.create(listeners: [new Log.FragmentRenderer, new Log.Folds, @scroll])
|
|
||||||
@observeParts()
|
|
||||||
@numberLineOnHover()
|
|
||||||
|
|
||||||
observeParts: ->
|
|
||||||
parts = @get('log.parts')
|
|
||||||
parts.addArrayObserver(@, didChange: 'partsDidChange', willChange: 'noop')
|
|
||||||
parts = parts.slice(0)
|
|
||||||
@partsDidChange(parts, 0, null, parts.length)
|
|
||||||
|
|
||||||
partsDidChange: (parts, start, _, added) ->
|
|
||||||
console.log 'log view: parts did change' if Log.DEBUG
|
|
||||||
for part, i in parts.slice(start, start + added)
|
|
||||||
# console.log "limit in log view: #{@get('limited')}"
|
|
||||||
break if @get('limited')
|
|
||||||
@engine.set(part.number, part.content)
|
|
||||||
@propertyDidChange('limited')
|
|
||||||
|
|
||||||
lineNumberDidChange: (->
|
|
||||||
@scroll.set(number) if !@get('isDestroyed') && number = @get('controller.lineNumber')
|
|
||||||
).observes('controller.lineNumber')
|
|
||||||
|
|
||||||
limited: (->
|
|
||||||
@limit && @limit.limited
|
|
||||||
).property()
|
|
||||||
|
|
||||||
plainTextLogUrl: (->
|
plainTextLogUrl: (->
|
||||||
Travis.Urls.plainTextLog(id) if id = @get('log.job.id')
|
Travis.Urls.plainTextLog(id) if id = @get('log.job.id')
|
||||||
|
@ -89,11 +214,26 @@ Travis.reopen
|
||||||
target = $(event.target)
|
target = $(event.target)
|
||||||
target.closest('.fold').toggleClass('open')
|
target.closest('.fold').toggleClass('open')
|
||||||
|
|
||||||
|
logUrl: (->
|
||||||
|
repo = @get('log.job.repo')
|
||||||
|
item = @get('controller.currentItem')
|
||||||
|
|
||||||
|
if repo && item
|
||||||
|
name = if item.constructor == Travis.Build
|
||||||
|
'build'
|
||||||
|
else
|
||||||
|
'job'
|
||||||
|
|
||||||
|
Travis.__container__.lookup('router:main').generate(name, repo, item)
|
||||||
|
).property('job.repo', 'parentView.currentItem')
|
||||||
|
|
||||||
lineNumberClicked: (number) ->
|
lineNumberClicked: (number) ->
|
||||||
window.history.pushState(null, null, "#{window.location.pathname}#L#{number}");
|
path = @get('logUrl') + "#L#{number}"
|
||||||
|
window.history.replaceState({ path: path }, null, path);
|
||||||
@set('controller.lineNumber', number)
|
@set('controller.lineNumber', number)
|
||||||
|
|
||||||
noop: -> # TODO required?
|
noop: -> # TODO required?
|
||||||
|
})
|
||||||
|
|
||||||
Log.Scroll = ->
|
Log.Scroll = ->
|
||||||
Log.Scroll.prototype = $.extend new Log.Listener,
|
Log.Scroll.prototype = $.extend new Log.Listener,
|
||||||
|
|
152
assets/scripts/lib/travis/ordered_log.coffee
Normal file
152
assets/scripts/lib/travis/ordered_log.coffee
Normal file
|
@ -0,0 +1,152 @@
|
||||||
|
# TODO: revisit those patterns
|
||||||
|
FOLDS = [
|
||||||
|
Em.Object.create(name: 'schema', startPattern: /^\$ (?:bundle exec )?rake( db:create)? db:schema:load/, endPattern: /^(<\/span>)?\$/)
|
||||||
|
Em.Object.create(name: 'migrate', startPattern: /^\$ (?:bundle exec )?rake( db:create)? db:migrate/, endPattern: /^(<\/span>)?\$/)
|
||||||
|
Em.Object.create(name: 'bundle', startPattern: /^\$ bundle install/, endPattern: /^(<\/span>)?\$/)
|
||||||
|
]
|
||||||
|
|
||||||
|
@Travis.OrderedLog = Em.Object.extend
|
||||||
|
init: ->
|
||||||
|
@set 'folds', []
|
||||||
|
@set 'line', 1
|
||||||
|
@set 'lineNumber', 1
|
||||||
|
@initial = true
|
||||||
|
|
||||||
|
for fold in FOLDS
|
||||||
|
@addFold fold
|
||||||
|
|
||||||
|
append: (lines) ->
|
||||||
|
return unless lines
|
||||||
|
return if @get('lineNumber') > 5000
|
||||||
|
|
||||||
|
log = @join lines
|
||||||
|
log = @escape log
|
||||||
|
log = @deansi log
|
||||||
|
lines = @split log
|
||||||
|
|
||||||
|
target = @get 'target'
|
||||||
|
index = 0
|
||||||
|
currentFold = @currentFold
|
||||||
|
|
||||||
|
result = []
|
||||||
|
|
||||||
|
for line in lines
|
||||||
|
if line == '\r'
|
||||||
|
@set 'replace', true
|
||||||
|
else if line == '\n'
|
||||||
|
@set 'newline', true
|
||||||
|
index += 1
|
||||||
|
else
|
||||||
|
if currentFold && ( @isFoldEnding(currentFold, line) )
|
||||||
|
# end of the fold, send fold to target
|
||||||
|
if result.length > 0
|
||||||
|
result.slice(-1)[0].foldEnd = true
|
||||||
|
target.appendLog result
|
||||||
|
|
||||||
|
@currentFold = currentFold = null
|
||||||
|
@set 'foldContinuation', false
|
||||||
|
result = []
|
||||||
|
|
||||||
|
if !currentFold && ( currentFold = @foldByStart(line) )
|
||||||
|
# beginning new fold, send current lines to target
|
||||||
|
if result.length > 0
|
||||||
|
target.appendLog result
|
||||||
|
|
||||||
|
result = []
|
||||||
|
start = index
|
||||||
|
|
||||||
|
payload = { content: line }
|
||||||
|
|
||||||
|
if currentFold
|
||||||
|
payload.fold = currentFold.get('name')
|
||||||
|
|
||||||
|
if @get 'foldContinuation'
|
||||||
|
payload.foldContinuation = true
|
||||||
|
|
||||||
|
payload.number = @get('lineNumber') + index
|
||||||
|
|
||||||
|
if @get 'replace'
|
||||||
|
@set 'replace', false
|
||||||
|
payload.replace = true
|
||||||
|
else if @get 'newline'
|
||||||
|
@set 'newline', false
|
||||||
|
else if !@initial
|
||||||
|
payload.append = true
|
||||||
|
|
||||||
|
@initial = false
|
||||||
|
|
||||||
|
if payload.foldContinuation && payload.content.match(/Done. Build script exited|Your build has been stopped/)
|
||||||
|
# script ended, but fold is still closed, which most probably means
|
||||||
|
# error, end the fold and open it.
|
||||||
|
# TODO: we need log marks to make it easier
|
||||||
|
payload.foldContinuation = null
|
||||||
|
payload.openFold = payload.fold
|
||||||
|
payload.fold = null
|
||||||
|
|
||||||
|
result.pushObject payload
|
||||||
|
|
||||||
|
if currentFold
|
||||||
|
@set 'foldContinuation', true
|
||||||
|
|
||||||
|
if @get('lineNumber') + index >= 5000
|
||||||
|
result.pushObject logWasCut: true
|
||||||
|
break
|
||||||
|
|
||||||
|
if result.length > 0
|
||||||
|
if currentFold
|
||||||
|
@currentFold = currentFold
|
||||||
|
|
||||||
|
target.appendLog result
|
||||||
|
|
||||||
|
nextLineNumber = @get('lineNumber') + index
|
||||||
|
@set 'lineNumber', nextLineNumber
|
||||||
|
|
||||||
|
join: (lines) ->
|
||||||
|
if typeof lines == 'string'
|
||||||
|
lines
|
||||||
|
else
|
||||||
|
lines.toArray().join ''
|
||||||
|
|
||||||
|
split: (log) ->
|
||||||
|
log = log.replace /\r\n/g, '\n'
|
||||||
|
lines = log.split(/(\n)/)
|
||||||
|
|
||||||
|
if lines.slice(-1)[0] == ''
|
||||||
|
lines.popObject()
|
||||||
|
|
||||||
|
result = []
|
||||||
|
for line in lines
|
||||||
|
result.pushObjects line.split(/(\r)/)
|
||||||
|
|
||||||
|
result
|
||||||
|
|
||||||
|
escape: (log) ->
|
||||||
|
Handlebars.Utils.escapeExpression log
|
||||||
|
|
||||||
|
deansi: (log) ->
|
||||||
|
log = log.replace(/\r\r/g, '\r')
|
||||||
|
.replace(/\033\[K\r/g, '\r')
|
||||||
|
.replace(/\[2K/g, '')
|
||||||
|
.replace(/\033\(B/g, '')
|
||||||
|
.replace(/\033\[\d+G/g, '')
|
||||||
|
|
||||||
|
ansi = ansiparse(log)
|
||||||
|
|
||||||
|
text = ''
|
||||||
|
ansi.forEach (part) ->
|
||||||
|
classes = []
|
||||||
|
part.foreground and classes.push(part.foreground)
|
||||||
|
part.background and classes.push('bg-' + part.background)
|
||||||
|
part.bold and classes.push('bold')
|
||||||
|
part.italic and classes.push('italic')
|
||||||
|
text += (if classes.length then ('<span class=\'' + classes.join(' ') + '\'>' + part.text + '</span>') else part.text)
|
||||||
|
text.replace /\033/g, ''
|
||||||
|
|
||||||
|
addFold: (fold) ->
|
||||||
|
@get('folds').pushObject fold
|
||||||
|
|
||||||
|
foldByStart: (line) ->
|
||||||
|
@get('folds').find (fold) -> line.match(fold.get('startPattern'))
|
||||||
|
|
||||||
|
isFoldEnding: (fold, line) ->
|
||||||
|
line.match(fold.get('endPattern'))
|
Loading…
Reference in New Issue
Block a user