Add @svenfuchs' log reimplementation
This commit is contained in:
parent
78dc69b61e
commit
553495c56c
|
@ -1,4 +1,3 @@
|
|||
require 'travis/log'
|
||||
require 'config/emoij'
|
||||
|
||||
@Travis.Helpers =
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
require 'models/extensions'
|
||||
require 'models/account'
|
||||
require 'models/artifact'
|
||||
require 'models/broadcast'
|
||||
require 'models/branch'
|
||||
require 'models/build'
|
||||
|
@ -8,6 +7,7 @@ require 'models/commit'
|
|||
require 'models/event'
|
||||
require 'models/hook'
|
||||
require 'models/job'
|
||||
require 'models/log'
|
||||
require 'models/repo'
|
||||
require 'models/sponsor'
|
||||
require 'models/user'
|
||||
|
|
|
@ -1,89 +0,0 @@
|
|||
require 'travis/model'
|
||||
|
||||
@Travis.Artifact = Em.Object.extend
|
||||
version: 1 # used to refresh log on requeue
|
||||
body: null
|
||||
isLoaded: false
|
||||
|
||||
init: ->
|
||||
@_super.apply this, arguments
|
||||
|
||||
@addObserver 'job.id', @fetchBody
|
||||
@fetchBody()
|
||||
|
||||
@set 'queue', Ember.A([])
|
||||
@set 'parts', Ember.ArrayProxy.create(content: [])
|
||||
|
||||
@addObserver 'body', @fetchWorker
|
||||
@fetchWorker()
|
||||
|
||||
id: (->
|
||||
@get('job.id')
|
||||
).property('job.id')
|
||||
|
||||
clear: ->
|
||||
@set('body', '')
|
||||
@incrementProperty('version')
|
||||
|
||||
fetchBody: ->
|
||||
if jobId = @get('job.id')
|
||||
@removeObserver 'job.id', @fetchBody
|
||||
|
||||
self = this
|
||||
Travis.ajax.ajax "/jobs/#{jobId}/log.txt?cors_hax=true", 'GET',
|
||||
dataType: 'text'
|
||||
contentType: 'text/plain'
|
||||
success: (data, textStatus, xhr) ->
|
||||
if xhr.status == 204
|
||||
logUrl = xhr.getResponseHeader('X-Log-Location')
|
||||
|
||||
# For some reason not all browsers can fetch this header
|
||||
unless logUrl
|
||||
logUrl = self.s3Url("/jobs/#{jobId}/log.txt")
|
||||
|
||||
$.ajax
|
||||
url: logUrl
|
||||
type: 'GET'
|
||||
success: (data) ->
|
||||
self.fetchedBody(data)
|
||||
else
|
||||
self.fetchedBody(data)
|
||||
|
||||
s3Url: (path) ->
|
||||
endpoint = Travis.config.api_endpoint
|
||||
staging = if endpoint.match(/-staging/) then '-staging' else ''
|
||||
host = Travis.config.api_endpoint.replace(/^https?:\/\//, '').split('.').slice(-2).join('.')
|
||||
"https://s3.amazonaws.com/archive#{staging}.#{host}#{path}"
|
||||
|
||||
|
||||
fetchedBody: (body) ->
|
||||
@set 'body', body
|
||||
@set 'isLoaded', true
|
||||
|
||||
append: (body) ->
|
||||
if @get('isInitialized')
|
||||
@get('parts').pushObject body
|
||||
@set('body', @get('body') + body)
|
||||
else
|
||||
@get('queue').pushObject(body)
|
||||
|
||||
recordDidLoad: (->
|
||||
if @get('isLoaded')
|
||||
if (body = @get 'body') && @get('parts.length') == 0
|
||||
@get('parts').pushObject body
|
||||
|
||||
@set 'isInitialized', true
|
||||
|
||||
queue = @get('queue')
|
||||
if queue.get('length') > 0
|
||||
@append queue.toArray().join('')
|
||||
).observes('isLoaded')
|
||||
|
||||
fetchWorker: ->
|
||||
if !@get('workerName') && (body = @get('body'))
|
||||
line = body.split("\n")[0]
|
||||
if line && (match = line.match /Using worker: (.*)/)
|
||||
if worker = match[1]
|
||||
worker = worker.trim().split(':')[0]
|
||||
@set('workerName', worker)
|
||||
@removeObserver 'body', @fetchWorker
|
|
@ -18,8 +18,9 @@ require 'travis/model'
|
|||
build: DS.belongsTo('Travis.Build')
|
||||
commit: DS.belongsTo('Travis.Commit')
|
||||
commits: DS.belongsTo('Travis.Commit')
|
||||
|
||||
log: ( ->
|
||||
Travis.Artifact.create(job: this)
|
||||
Travis.Log.create(job: this)
|
||||
).property()
|
||||
|
||||
repoSlug: (->
|
||||
|
@ -39,7 +40,7 @@ require 'travis/model'
|
|||
).property('state')
|
||||
|
||||
clearLog: ->
|
||||
@get('log').clear() if @get('log.isLoaded')
|
||||
@get('log').clear()
|
||||
|
||||
sponsor: (->
|
||||
worker = @get('log.workerName')
|
||||
|
@ -71,13 +72,13 @@ require 'travis/model'
|
|||
requeue: ->
|
||||
Travis.ajax.post '/requests', job_id: @get('id')
|
||||
|
||||
appendLog: (text) ->
|
||||
if log = @get('log')
|
||||
log.append(text)
|
||||
appendLog: (part) ->
|
||||
@get('log').append part
|
||||
|
||||
subscribe: ->
|
||||
if id = @get('id')
|
||||
Travis.pusher.subscribe "job-#{id}"
|
||||
return if @get('subscribed')
|
||||
@set('subscribed', true)
|
||||
Travis.pusher.subscribe "job-#{@get('id')}"
|
||||
|
||||
onStateChange: (->
|
||||
if @get('state') == 'finished' && Travis.pusher
|
||||
|
|
68
assets/scripts/app/models/log.coffee
Normal file
68
assets/scripts/app/models/log.coffee
Normal file
|
@ -0,0 +1,68 @@
|
|||
require 'travis/model'
|
||||
|
||||
@Travis.Log = Em.Object.extend
|
||||
version: 0 # used to refresh log on requeue
|
||||
isLoaded: false
|
||||
length: 0
|
||||
|
||||
init: ->
|
||||
@set('parts', Ember.ArrayProxy.create(content: []))
|
||||
@fetch()
|
||||
|
||||
fetch: ->
|
||||
console.log 'fetch'
|
||||
handlers =
|
||||
json: (json) => @loadParts(json['log']['parts'])
|
||||
text: (text) => @loadText(text)
|
||||
Travis.Log.Request.create(id: id, handlers: handlers).run() if id = @get('job.id')
|
||||
|
||||
clear: ->
|
||||
@set('parts', Ember.ArrayProxy.create(content: []))
|
||||
@incrementProperty('version')
|
||||
|
||||
append: (part) ->
|
||||
@get('parts').pushObject(part)
|
||||
|
||||
loadParts: (parts) ->
|
||||
@append(part) for part in parts
|
||||
@set('isLoaded', true)
|
||||
|
||||
loadText: (text) ->
|
||||
number = -1
|
||||
@append(number: 0, content: text)
|
||||
@set('isLoaded', true)
|
||||
|
||||
Travis.Log.Request = Em.Object.extend
|
||||
HEADERS:
|
||||
accept: 'application/vnd.travis-ci.2+json; chunked=true; version=2, text/plain; version=2'
|
||||
|
||||
run: ->
|
||||
Travis.ajax.ajax "/jobs/#{@id}/log?cors_hax=true", 'GET',
|
||||
dataType: 'text'
|
||||
headers: @HEADERS
|
||||
success: (body, status, xhr) => @handle(body, status, xhr)
|
||||
|
||||
handle: (body, status, xhr) ->
|
||||
if xhr.status == 204
|
||||
$.ajax(url: @redirectTo(xhr), type: 'GET', success: @handlers.text)
|
||||
else if @isJson(xhr, body)
|
||||
@handlers.json(JSON.parse(body))
|
||||
else
|
||||
@handlers.text(body)
|
||||
|
||||
redirectTo: (xhr) ->
|
||||
# Firefox can't see the Location header on the xhr response due to the wrong
|
||||
# status code 204. Should be some redirect code but that doesn't work with CORS.
|
||||
xhr.getResponseHeader('Location') || @s3Url()
|
||||
|
||||
s3Url: ->
|
||||
endpoint = Travis.config.api_endpoint
|
||||
staging = if endpoint.match(/-staging/) then '-staging' else ''
|
||||
host = endpoint.replace(/^https?:\/\//, '').split('.').slice(-2).join('.')
|
||||
"https://s3.amazonaws.com/archive#{staging}.#{host}#{path}/jobs/#{@id}/log.txt"
|
||||
|
||||
isJson: (xhr, body) ->
|
||||
# Firefox can't see the Content-Type header on the xhr response due to the wrong
|
||||
# status code 204. Should be some redirect code but that doesn't work with CORS.
|
||||
type = xhr.getResponseHeader('Content-Type') || ''
|
||||
type.indexOf('json') > -1 || body.slice(0, 8) == '{"log":{'
|
|
@ -52,7 +52,7 @@ Travis.Store = DS.Store.extend
|
|||
!!@typeMapFor(type).idToCid[id]
|
||||
|
||||
receive: (event, data) ->
|
||||
console.log event, data
|
||||
#console.log event, data
|
||||
[name, type] = event.split(':')
|
||||
|
||||
mappings = @adapter.get('mappings')
|
||||
|
@ -79,8 +79,9 @@ Travis.Store = DS.Store.extend
|
|||
|
||||
|
||||
if event == 'job:log'
|
||||
if job = @find(Travis.Job, data['job']['id'])
|
||||
job.appendLog(data['job']['_log'])
|
||||
data = data.job
|
||||
job = @find(Travis.Job, data.id)
|
||||
job.appendLog(number: parseInt(data.number), content: data._log)
|
||||
else if data[type.singularName()]
|
||||
@_loadOne(this, type, data)
|
||||
else if data[type.pluralName()]
|
||||
|
|
|
@ -46,6 +46,6 @@
|
|||
{{view Travis.JobsView jobsBinding="build.requiredJobs" required="true"}}
|
||||
{{view Travis.JobsView jobsBinding="build.allowedFailureJobs"}}
|
||||
{{else}}
|
||||
{{view Travis.LogView contextBinding="build.jobs.firstObject"}}
|
||||
{{view Travis.LogView jobBinding="build.jobs.firstObject"}}
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
|
|
@ -1,13 +1,7 @@
|
|||
{{view.logSubscriber}}
|
||||
{{#if view.log.isLoaded}}
|
||||
{{view Travis.PreView logBinding="view.log" logUrlBinding="view.logUrl"}}
|
||||
|
||||
{{#if view.job.log.isLoaded}}
|
||||
{{! this #with + #if is needed because I want to rerender 'pre' when log changes to properly clean it up,
|
||||
this should probably be refactored to use container view}}
|
||||
{{#with view.job.log}}
|
||||
{{view Travis.PreView logBinding="view.context.log" logUrlBinding="view.logUrl"}}
|
||||
{{/with}}
|
||||
|
||||
{{#if sponsor.name}}
|
||||
{{#if view.job.sponsor.name}}
|
||||
<p class="sponsor">
|
||||
{{t builds.messages.sponsored_by}}
|
||||
<a {{bindAttr href="sponsor.url"}}>{{sponsor.name}}</a>
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
<dd class="config">{{formatConfig job.config}}</dd>
|
||||
</dl>
|
||||
|
||||
{{view Travis.LogView contextBinding="job"}}}
|
||||
{{view Travis.LogView jobBinding="job"}}
|
||||
</div>
|
||||
{{else}}
|
||||
<div id="job" class="loading">
|
||||
|
|
|
@ -21,6 +21,7 @@ require 'views/build'
|
|||
require 'views/events'
|
||||
require 'views/flash'
|
||||
require 'views/job'
|
||||
require 'views/log'
|
||||
require 'views/repo'
|
||||
require 'views/profile'
|
||||
require 'views/sidebar'
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
@Travis.reopen
|
||||
Travis.reopen
|
||||
JobsView: Travis.View.extend
|
||||
templateName: 'jobs/list'
|
||||
buildBinding: 'controller.build'
|
||||
|
@ -37,204 +37,3 @@
|
|||
urlCommitter: (->
|
||||
Travis.Urls.email(@get('commit.committerEmail'))
|
||||
).property('commit.committerEmail')
|
||||
|
||||
LogView: Travis.View.extend
|
||||
templateName: 'jobs/log'
|
||||
logBinding: 'job.log'
|
||||
|
||||
plainTextLogUrl: (->
|
||||
if id = @get('job.log.id')
|
||||
Travis.Urls.plainTextLog(id)
|
||||
).property('job.log')
|
||||
|
||||
didInsertElement: ->
|
||||
@_super.apply this, arguments
|
||||
@tryScrollingToHashLineNumber()
|
||||
|
||||
scrollTo: (hash) ->
|
||||
# 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 $(hash).offset().top
|
||||
|
||||
@set 'controller.lineNumberHash', null
|
||||
|
||||
lineNumberHashDidChange: (->
|
||||
@tryScrollingToHashLineNumber()
|
||||
).observes('controller.lineNumberHash')
|
||||
|
||||
tryScrollingToHashLineNumber: ->
|
||||
if hash = @get 'controller.lineNumberHash'
|
||||
self = this
|
||||
|
||||
checker = ->
|
||||
return if self.get('isDestroyed')
|
||||
|
||||
if $(hash).length
|
||||
self.scrollTo(hash)
|
||||
else
|
||||
setTimeout checker, 100
|
||||
|
||||
checker()
|
||||
|
||||
click: ->
|
||||
target = $(event.target)
|
||||
|
||||
target.closest('.fold').toggleClass('open')
|
||||
|
||||
if target.is('a') && target.attr('id') && target.attr('id').match(/^L\d+$/)
|
||||
path = target.attr 'href'
|
||||
Travis.get('router').route(path)
|
||||
event.stopPropagation()
|
||||
return false
|
||||
|
||||
toTop: () ->
|
||||
$(window).scrollTop(0)
|
||||
|
||||
jobBinding: 'context'
|
||||
|
||||
logSubscriber: (->
|
||||
# for some reason observing context does not work,
|
||||
# TODO: find out why
|
||||
job = @get('job')
|
||||
job.subscribe() if job && !job.get('isFinished')
|
||||
null
|
||||
).property('job', 'job.state')
|
||||
|
||||
logUrl: (->
|
||||
repo = @get('job.repo')
|
||||
item = @get('parentView.currentItem')
|
||||
|
||||
if repo && item
|
||||
event = if item.constructor == Travis.Build
|
||||
'showBuild'
|
||||
else
|
||||
'showJob'
|
||||
|
||||
#Travis.get('router').urlForEvent(event, repo, item)
|
||||
).property('job.repo', 'parentView.currentItem')
|
||||
|
||||
PreView: Em.View.extend
|
||||
templateName: 'jobs/pre'
|
||||
init: ->
|
||||
@_super.apply this, arguments
|
||||
@set 'logManager', Travis.Log.create(target: this)
|
||||
|
||||
toggleTailing: ->
|
||||
Travis.tailing.toggle()
|
||||
event.preventDefault()
|
||||
|
||||
didInsertElement: ->
|
||||
@_super.apply this, arguments
|
||||
|
||||
Ember.run.next this, ->
|
||||
if @get 'log.isInitialized'
|
||||
@logDidChange()
|
||||
|
||||
willDestroy: ->
|
||||
@get('logManager').destroy()
|
||||
@get('log.parts').removeArrayObserver this,
|
||||
didChange: 'logContentsDidChange'
|
||||
willChange: 'logContentsWillChange'
|
||||
|
||||
version: (->
|
||||
@rerender()
|
||||
@set 'logManager', Travis.Log.create(target: this)
|
||||
).observes('log.version')
|
||||
|
||||
logDidChange: (->
|
||||
if @get('log.isInitialized') && @state == 'inDOM'
|
||||
@attachLogObservers()
|
||||
).observes('log', 'log.isInitialized')
|
||||
|
||||
attachLogObservers: ->
|
||||
return if @get('logPartsObserversAttached') == Ember.guidFor(@get('log'))
|
||||
@set 'logPartsObserversAttached', Ember.guidFor(@get('log'))
|
||||
|
||||
Ember.run.next this, ->
|
||||
@get('logManager').append @get('log.parts')
|
||||
|
||||
@get('log.parts').addArrayObserver this,
|
||||
didChange: 'logContentsDidChange'
|
||||
willChange: 'logContentsWillChange'
|
||||
|
||||
logContentsDidChange: (lines, index, removedCount, addedCount) ->
|
||||
addedLines = lines.slice(index, index + addedCount)
|
||||
@get('logManager').append addedLines
|
||||
|
||||
logContentsWillChange: (-> )
|
||||
|
||||
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}\">#{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>")
|
||||
|
||||
|
|
190
assets/scripts/app/views/log.coffee
Normal file
190
assets/scripts/app/views/log.coffee
Normal file
|
@ -0,0 +1,190 @@
|
|||
require 'log'
|
||||
|
||||
Travis.reopen
|
||||
LogView: Travis.View.extend
|
||||
templateName: 'jobs/log'
|
||||
logBinding: 'job.log'
|
||||
contextBinding: 'job'
|
||||
|
||||
init: ->
|
||||
@_super.apply this, arguments
|
||||
|
||||
plainTextLogUrl: (->
|
||||
if id = @get('job.log.id')
|
||||
Travis.Urls.plainTextLog(id)
|
||||
).property('log')
|
||||
|
||||
didInsertElement: ->
|
||||
job = @get('job')
|
||||
job.subscribe() if job && !job.get('isFinished')
|
||||
|
||||
@_super.apply this, arguments
|
||||
#@tryScrollingToHashLineNumber()
|
||||
|
||||
scrollTo: (hash) ->
|
||||
# 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 $(hash).offset().top
|
||||
|
||||
@set 'controller.lineNumberHash', null
|
||||
|
||||
lineNumberHashDidChange: (->
|
||||
@tryScrollingToHashLineNumber()
|
||||
).observes('controller.lineNumberHash')
|
||||
|
||||
tryScrollingToHashLineNumber: ->
|
||||
if hash = @get 'controller.lineNumberHash'
|
||||
self = this
|
||||
|
||||
checker = ->
|
||||
return if self.get('isDestroyed')
|
||||
|
||||
if $(hash).length
|
||||
self.scrollTo(hash)
|
||||
else
|
||||
setTimeout checker, 100
|
||||
|
||||
checker()
|
||||
|
||||
click: ->
|
||||
target = $(event.target)
|
||||
|
||||
target.closest('.fold').toggleClass('open')
|
||||
|
||||
if target.is('a') && target.attr('id') && target.attr('id').match(/^L\d+$/)
|
||||
path = target.attr 'href'
|
||||
Travis.get('router').route(path)
|
||||
event.stopPropagation()
|
||||
return false
|
||||
|
||||
toTop: () ->
|
||||
$(window).scrollTop(0)
|
||||
|
||||
logUrl: (->
|
||||
repo = @get('job.repo')
|
||||
item = @get('parentView.currentItem')
|
||||
|
||||
if repo && item
|
||||
event = if item.constructor == Travis.Build
|
||||
'showBuild'
|
||||
else
|
||||
'showJob'
|
||||
|
||||
#Travis.get('router').urlForEvent(event, repo, item)
|
||||
).property('job.repo', 'parentView.currentItem')
|
||||
|
||||
PreView: Em.View.extend
|
||||
templateName: 'jobs/pre'
|
||||
|
||||
createEngine: ->
|
||||
limit = new Log.Limit
|
||||
scroll = new Log.Scroll
|
||||
engine = Log.create(listeners: [limit, new Log.FragmentRenderer, new Log.Folds, scroll])
|
||||
|
||||
@set('scroll', scroll)
|
||||
@set('limit', limit)
|
||||
@set('engine', engine)
|
||||
|
||||
@observeParts()
|
||||
|
||||
observeParts: ->
|
||||
parts = @get('log.parts')
|
||||
parts.addArrayObserver(@, didChange: 'partsDidChange', willChange: 'partsWillChange')
|
||||
@partsDidChange(parts.slice(0))
|
||||
|
||||
didInsertElement: ->
|
||||
@_super.apply this, arguments
|
||||
@createEngine()
|
||||
|
||||
willDestroyElement: ->
|
||||
parts = @get('log.parts')
|
||||
parts.removeArrayObserver(@, didChange: 'partsDidChange', willChange: 'partsWillChange')
|
||||
|
||||
partsWillChange: ->
|
||||
|
||||
partsDidChange: (parts, start, _, added) ->
|
||||
unless @get('isLimited')
|
||||
start ||= 0
|
||||
added ||= parts.length
|
||||
@get('engine').set(part.number, part.content) for part, i in parts.slice(start, start + added)
|
||||
#@propertyDidChange('isLimited')
|
||||
|
||||
versionDidChange: (->
|
||||
@rerender() if @get('inDOM')
|
||||
).observes('log.version')
|
||||
|
||||
logDidChange: (->
|
||||
@rerender() if @get('inDOM')
|
||||
).observes('log')
|
||||
|
||||
isLimited: (->
|
||||
@limit && @limit.isLimited()
|
||||
).property()
|
||||
|
||||
plainTextLogUrl: (->
|
||||
Travis.Urls.plainTextLog(id) if id = @get('log.job.id')
|
||||
).property('job.log.id')
|
||||
|
||||
toggleTailing: (event) ->
|
||||
Travis.app.tailing.toggle()
|
||||
event.preventDefault()
|
||||
|
||||
lineNumbers: ->
|
||||
$('#log').on 'mouseenter', 'a', ->
|
||||
$(this).attr('href', '#L' + ($(this.parentNode).prevAll('p').length + 1))
|
||||
|
||||
folds: ->
|
||||
$('#log').on 'click', '.fold', ->
|
||||
$(this).toggleClass('open')
|
||||
|
||||
click: (event) ->
|
||||
target = $(event.target)
|
||||
target.closest('.fold').toggleClass('open')
|
||||
if target.is('a') && matches = target.attr('href')?.match(/#L(\d+)$/)
|
||||
Travis.app.get('router.location').setURL(target.attr('href'))
|
||||
@set('controller.lineNumber', matches[1])
|
||||
event.stopPropagation()
|
||||
return false
|
||||
|
||||
lineNumberObserver: (->
|
||||
@scroll.set(number) if !@get('isDestroyed') && number = @get('controller.lineNumber')
|
||||
).observes('controller.lineNumber')
|
||||
|
||||
Log.Scroll = ->
|
||||
Log.Scroll.prototype = $.extend new Log.Listener,
|
||||
set: (number) ->
|
||||
return unless number
|
||||
@number = number
|
||||
@tryScroll()
|
||||
|
||||
insert: (log, after, data) ->
|
||||
@tryScroll() if @number
|
||||
|
||||
tryScroll: ->
|
||||
if element = $("#log p:nth-child(#{@number})")
|
||||
$('#main').scrollTop(0)
|
||||
$('html, body').scrollTop(element.offset()?.top) # weird, html works in chrome, body in firefox
|
||||
@highlight(element)
|
||||
@number = undefined
|
||||
|
||||
highlight: (element) ->
|
||||
$('#log p.highlight').removeClass('highlight')
|
||||
$(element).addClass('highlight')
|
||||
|
||||
Log.Limit = ->
|
||||
Log.Limit.prototype = $.extend new Log.Listener,
|
||||
MAX_LINES: 5000
|
||||
count: 0
|
||||
|
||||
insert: (log, after, lines) ->
|
||||
@count += lines.length
|
||||
lines.length = @MAX_LINES if lines.length > @MAX_LINES
|
||||
|
||||
isLimited: ->
|
||||
@count > @MAX_LINES
|
|
@ -1,152 +0,0 @@
|
|||
# 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.Log = 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