Add @svenfuchs' log reimplementation
This commit is contained in:
parent
78dc69b61e
commit
553495c56c
|
@ -1,4 +1,3 @@
|
||||||
require 'travis/log'
|
|
||||||
require 'config/emoij'
|
require 'config/emoij'
|
||||||
|
|
||||||
@Travis.Helpers =
|
@Travis.Helpers =
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
require 'models/extensions'
|
require 'models/extensions'
|
||||||
require 'models/account'
|
require 'models/account'
|
||||||
require 'models/artifact'
|
|
||||||
require 'models/broadcast'
|
require 'models/broadcast'
|
||||||
require 'models/branch'
|
require 'models/branch'
|
||||||
require 'models/build'
|
require 'models/build'
|
||||||
|
@ -8,6 +7,7 @@ require 'models/commit'
|
||||||
require 'models/event'
|
require 'models/event'
|
||||||
require 'models/hook'
|
require 'models/hook'
|
||||||
require 'models/job'
|
require 'models/job'
|
||||||
|
require 'models/log'
|
||||||
require 'models/repo'
|
require 'models/repo'
|
||||||
require 'models/sponsor'
|
require 'models/sponsor'
|
||||||
require 'models/user'
|
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')
|
build: DS.belongsTo('Travis.Build')
|
||||||
commit: DS.belongsTo('Travis.Commit')
|
commit: DS.belongsTo('Travis.Commit')
|
||||||
commits: DS.belongsTo('Travis.Commit')
|
commits: DS.belongsTo('Travis.Commit')
|
||||||
|
|
||||||
log: ( ->
|
log: ( ->
|
||||||
Travis.Artifact.create(job: this)
|
Travis.Log.create(job: this)
|
||||||
).property()
|
).property()
|
||||||
|
|
||||||
repoSlug: (->
|
repoSlug: (->
|
||||||
|
@ -39,7 +40,7 @@ require 'travis/model'
|
||||||
).property('state')
|
).property('state')
|
||||||
|
|
||||||
clearLog: ->
|
clearLog: ->
|
||||||
@get('log').clear() if @get('log.isLoaded')
|
@get('log').clear()
|
||||||
|
|
||||||
sponsor: (->
|
sponsor: (->
|
||||||
worker = @get('log.workerName')
|
worker = @get('log.workerName')
|
||||||
|
@ -71,13 +72,13 @@ require 'travis/model'
|
||||||
requeue: ->
|
requeue: ->
|
||||||
Travis.ajax.post '/requests', job_id: @get('id')
|
Travis.ajax.post '/requests', job_id: @get('id')
|
||||||
|
|
||||||
appendLog: (text) ->
|
appendLog: (part) ->
|
||||||
if log = @get('log')
|
@get('log').append part
|
||||||
log.append(text)
|
|
||||||
|
|
||||||
subscribe: ->
|
subscribe: ->
|
||||||
if id = @get('id')
|
return if @get('subscribed')
|
||||||
Travis.pusher.subscribe "job-#{id}"
|
@set('subscribed', true)
|
||||||
|
Travis.pusher.subscribe "job-#{@get('id')}"
|
||||||
|
|
||||||
onStateChange: (->
|
onStateChange: (->
|
||||||
if @get('state') == 'finished' && Travis.pusher
|
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]
|
!!@typeMapFor(type).idToCid[id]
|
||||||
|
|
||||||
receive: (event, data) ->
|
receive: (event, data) ->
|
||||||
console.log event, data
|
#console.log event, data
|
||||||
[name, type] = event.split(':')
|
[name, type] = event.split(':')
|
||||||
|
|
||||||
mappings = @adapter.get('mappings')
|
mappings = @adapter.get('mappings')
|
||||||
|
@ -79,8 +79,9 @@ Travis.Store = DS.Store.extend
|
||||||
|
|
||||||
|
|
||||||
if event == 'job:log'
|
if event == 'job:log'
|
||||||
if job = @find(Travis.Job, data['job']['id'])
|
data = data.job
|
||||||
job.appendLog(data['job']['_log'])
|
job = @find(Travis.Job, data.id)
|
||||||
|
job.appendLog(number: parseInt(data.number), content: data._log)
|
||||||
else if data[type.singularName()]
|
else if data[type.singularName()]
|
||||||
@_loadOne(this, type, data)
|
@_loadOne(this, type, data)
|
||||||
else if data[type.pluralName()]
|
else if data[type.pluralName()]
|
||||||
|
|
|
@ -46,6 +46,6 @@
|
||||||
{{view Travis.JobsView jobsBinding="build.requiredJobs" required="true"}}
|
{{view Travis.JobsView jobsBinding="build.requiredJobs" required="true"}}
|
||||||
{{view Travis.JobsView jobsBinding="build.allowedFailureJobs"}}
|
{{view Travis.JobsView jobsBinding="build.allowedFailureJobs"}}
|
||||||
{{else}}
|
{{else}}
|
||||||
{{view Travis.LogView contextBinding="build.jobs.firstObject"}}
|
{{view Travis.LogView jobBinding="build.jobs.firstObject"}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/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}}
|
{{#if view.job.sponsor.name}}
|
||||||
{{! 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}}
|
|
||||||
<p class="sponsor">
|
<p class="sponsor">
|
||||||
{{t builds.messages.sponsored_by}}
|
{{t builds.messages.sponsored_by}}
|
||||||
<a {{bindAttr href="sponsor.url"}}>{{sponsor.name}}</a>
|
<a {{bindAttr href="sponsor.url"}}>{{sponsor.name}}</a>
|
||||||
|
|
|
@ -42,7 +42,7 @@
|
||||||
<dd class="config">{{formatConfig job.config}}</dd>
|
<dd class="config">{{formatConfig job.config}}</dd>
|
||||||
</dl>
|
</dl>
|
||||||
|
|
||||||
{{view Travis.LogView contextBinding="job"}}}
|
{{view Travis.LogView jobBinding="job"}}
|
||||||
</div>
|
</div>
|
||||||
{{else}}
|
{{else}}
|
||||||
<div id="job" class="loading">
|
<div id="job" class="loading">
|
||||||
|
|
|
@ -21,6 +21,7 @@ require 'views/build'
|
||||||
require 'views/events'
|
require 'views/events'
|
||||||
require 'views/flash'
|
require 'views/flash'
|
||||||
require 'views/job'
|
require 'views/job'
|
||||||
|
require 'views/log'
|
||||||
require 'views/repo'
|
require 'views/repo'
|
||||||
require 'views/profile'
|
require 'views/profile'
|
||||||
require 'views/sidebar'
|
require 'views/sidebar'
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
@Travis.reopen
|
Travis.reopen
|
||||||
JobsView: Travis.View.extend
|
JobsView: Travis.View.extend
|
||||||
templateName: 'jobs/list'
|
templateName: 'jobs/list'
|
||||||
buildBinding: 'controller.build'
|
buildBinding: 'controller.build'
|
||||||
|
@ -37,204 +37,3 @@
|
||||||
urlCommitter: (->
|
urlCommitter: (->
|
||||||
Travis.Urls.email(@get('commit.committerEmail'))
|
Travis.Urls.email(@get('commit.committerEmail'))
|
||||||
).property('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