shipit-engine 0.8.9 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/assets/javascripts/shipit_bs.js.coffee +2 -0
- data/app/assets/javascripts/task.js.coffee +14 -5
- data/app/assets/javascripts/task/search_bar.js.coffee +52 -0
- data/app/assets/javascripts/task/stream.js.coffee +9 -2
- data/app/assets/javascripts/task/tty.js.coffee +75 -28
- data/app/assets/stylesheets/_base/_forms.scss +5 -0
- data/app/assets/stylesheets/_pages/_commits.scss +1 -1
- data/app/assets/stylesheets/_pages/_deploy.scss +56 -24
- data/app/assets/stylesheets/_pages/_settings.scss +5 -0
- data/app/assets/stylesheets/_structure/_layout.scss +1 -0
- data/app/assets/stylesheets/_structure/_main.scss +4 -0
- data/app/assets/stylesheets/shipit.scss +2 -0
- data/app/assets/stylesheets/shipit_bs.scss +22 -0
- data/app/controllers/shipit/deploys_controller.rb +5 -1
- data/app/controllers/shipit/shipit_controller.rb +10 -3
- data/app/controllers/shipit/stacks_controller.rb +12 -3
- data/app/controllers/shipit/tasks_controller.rb +4 -0
- data/app/helpers/shipit/shipit_helper.rb +18 -0
- data/app/helpers/shipit/stacks_helper.rb +1 -1
- data/app/jobs/shipit/cache_deploy_spec_job.rb +2 -0
- data/app/jobs/shipit/fetch_deployed_revision_job.rb +1 -0
- data/app/jobs/shipit/git_mirror_update_job.rb +2 -0
- data/app/jobs/shipit/perform_task_job.rb +1 -0
- data/app/models/shipit/commit.rb +2 -2
- data/app/models/shipit/deploy.rb +1 -1
- data/app/models/shipit/deploy_spec/bundler_discovery.rb +1 -1
- data/app/models/shipit/duration.rb +28 -0
- data/app/models/shipit/stack.rb +33 -11
- data/app/models/shipit/task.rb +26 -3
- data/app/serializers/shipit/task_serializer.rb +14 -1
- data/app/views/bootstrap/shipit/missing_settings.html.erb +97 -0
- data/app/views/bootstrap/shipit/stacks/new.html.erb +44 -0
- data/app/views/layouts/shipit.html.erb +1 -1
- data/app/views/layouts/shipit_bootstrap.html.erb +44 -0
- data/app/views/shipit/commits/_commit.html.erb +3 -2
- data/app/views/shipit/deploys/_deploy.html.erb +11 -2
- data/app/views/shipit/deploys/show.html.erb +1 -1
- data/app/views/shipit/stacks/new.html.erb +12 -12
- data/app/views/shipit/stacks/settings.html.erb +5 -0
- data/app/views/shipit/stacks/show.html.erb +1 -1
- data/app/views/shipit/tasks/_task.html.erb +10 -2
- data/app/views/shipit/tasks/_task_output.html.erb +11 -1
- data/app/views/shipit/tasks/show.html.erb +1 -1
- data/config/routes.rb +1 -0
- data/db/migrate/20160324155046_add_started_at_and_ended_at_on_tasks.rb +25 -0
- data/lib/shipit.rb +13 -0
- data/lib/shipit/command.rb +13 -9
- data/lib/shipit/engine.rb +8 -0
- data/lib/shipit/template_renderer_extension.rb +16 -0
- data/lib/shipit/version.rb +1 -1
- data/test/controllers/deploys_controller_test.rb +11 -0
- data/test/controllers/stacks_controller_test.rb +5 -0
- data/test/controllers/tasks_controller_test.rb +6 -0
- data/test/dummy/config/secrets.example.yml +4 -0
- data/test/dummy/config/secrets.yml +2 -0
- data/test/dummy/data/stacks/byroot/junk/production/deploys/83/bar.txt +2 -0
- data/test/dummy/data/stacks/byroot/junk/production/deploys/83/dkfdsf +0 -0
- data/test/dummy/data/stacks/byroot/junk/production/deploys/83/dskjfsd +0 -0
- data/test/dummy/data/stacks/byroot/junk/production/deploys/83/dslkjfjsdf +0 -0
- data/test/dummy/data/stacks/byroot/junk/production/deploys/83/plopfizz +0 -0
- data/test/dummy/data/stacks/byroot/junk/production/deploys/83/sd +0 -0
- data/test/dummy/data/stacks/byroot/junk/production/deploys/83/sdkfjsdf +1 -0
- data/test/dummy/data/stacks/byroot/junk/production/deploys/83/sdlfjsdfdsfj +0 -0
- data/test/dummy/data/stacks/byroot/junk/production/deploys/83/sdlkfjsdlkfjsdlkfjdsfsdfksdfjsldkfjsdlkfjsdf +0 -0
- data/test/dummy/data/stacks/byroot/junk/production/deploys/83/shipit.yml +32 -0
- data/test/dummy/data/stacks/byroot/junk/production/deploys/83/toto.txt +2 -0
- data/test/dummy/data/stacks/byroot/junk/production/git/bar.txt +1 -0
- data/test/dummy/data/stacks/byroot/junk/production/git/shipit.yml +6 -1
- data/test/dummy/data/stacks/byroot/test/production/git/README.md +1 -0
- data/test/dummy/db/development.sqlite3 +0 -0
- data/test/dummy/db/schema.rb +3 -1
- data/test/dummy/db/seeds.rb +6 -0
- data/test/dummy/db/test.sqlite3 +0 -0
- data/test/dummy/db/test.sqlite3-journal +0 -0
- data/test/fixtures/shipit/tasks.yml +11 -0
- data/test/models/commits_test.rb +1 -1
- data/test/models/deploys_test.rb +40 -0
- data/test/models/duration_test.rb +13 -0
- data/test/models/stacks_test.rb +3 -4
- data/test/unit/command_test.rb +14 -0
- data/vendor/assets/javascripts/clusterize.js +327 -0
- data/vendor/assets/javascripts/mousetrap-global-bind.js +43 -0
- data/vendor/assets/javascripts/mousetrap.js +1021 -0
- data/vendor/assets/javascripts/string_includes.js +14 -0
- data/vendor/assets/stylesheets/clusterize.css +27 -0
- metadata +100 -3
- data/app/assets/javascripts/task/sticky_element.js.coffee +0 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 22eb132c6bbb6e9af2fdfca03481022aa47ccbeb
|
4
|
+
data.tar.gz: 6415095a2772724f3b2ad364411ad3d5956039f4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2d5f6b17d2f1b9f4a52b4ed208524dd13f4ddddd8d178e0235eaeecd318c872ee29b480b87f5f905bfc9990274e82e1addca61feefa98aa373b09f03d245176a
|
7
|
+
data.tar.gz: d17ad59449f3a8a801689d991ff4092efe67c6c88ea83f51de1c70257fdf3c919ea0181d1d206099fea76550824602c65b85432cdf88e879c770d99954a5a709
|
@@ -1,9 +1,20 @@
|
|
1
|
+
#= require string_includes
|
2
|
+
#= require mousetrap
|
3
|
+
#= require mousetrap-global-bind
|
4
|
+
#= require lodash
|
5
|
+
#= require clusterize
|
1
6
|
#= require_tree ./task
|
2
7
|
#= require_self
|
3
8
|
|
4
9
|
@OutputStream = new Stream
|
5
10
|
|
6
11
|
jQuery ->
|
12
|
+
$code = $('code')
|
13
|
+
initialOutput = $code.attr('data-output')
|
14
|
+
$code.removeAttr('data-output')
|
15
|
+
|
16
|
+
search = new SearchBar($('.search-bar'))
|
17
|
+
|
7
18
|
OutputStream.addEventListener 'status', (status, response) ->
|
8
19
|
$('[data-status]').attr('data-status', status)
|
9
20
|
|
@@ -11,16 +22,14 @@ jQuery ->
|
|
11
22
|
window.location = response.rollback_url
|
12
23
|
|
13
24
|
tty = new TTY($('body'))
|
25
|
+
search.addEventListener('query', tty.filterOutput)
|
26
|
+
search.immediateBroadcastQueryChange()
|
14
27
|
OutputStream.addEventListener('chunk', tty.appendChunk)
|
15
28
|
|
16
29
|
if task = $('[data-task]').data('task')
|
17
30
|
Notifications.init(OutputStream, task)
|
18
31
|
|
19
|
-
$code = $('code')
|
20
32
|
OutputStream.init
|
21
33
|
status: $code.closest('[data-status]').data('status')
|
22
34
|
url: $code.data('next-chunks-url')
|
23
|
-
text:
|
24
|
-
|
25
|
-
StickyElement.init('.deploy-banner')
|
26
|
-
StickyElement.init('.sidebar')
|
35
|
+
text: initialOutput
|
@@ -0,0 +1,52 @@
|
|
1
|
+
class @SearchBar
|
2
|
+
DEBOUNCE = 300
|
3
|
+
|
4
|
+
constructor: (@$bar) ->
|
5
|
+
@eventListeners = {}
|
6
|
+
@query = window.location.hash.replace(/^#/, '')
|
7
|
+
@$input = @$bar.find('.search-input')
|
8
|
+
@$input.on('blur', @closeIfEmpty)
|
9
|
+
@$input.on('input', @updateQuery)
|
10
|
+
@broadcastQueryChange = _.debounce(@immediateBroadcastQueryChange, DEBOUNCE)
|
11
|
+
Mousetrap.bindGlobal(['command+f', 'ctrl+f'], @open)
|
12
|
+
|
13
|
+
if @query
|
14
|
+
@open()
|
15
|
+
@setQuery(@query)
|
16
|
+
|
17
|
+
addEventListener: (type, handler) ->
|
18
|
+
@listeners(type).push(handler)
|
19
|
+
|
20
|
+
listeners: (type) ->
|
21
|
+
@eventListeners[type] ||= []
|
22
|
+
|
23
|
+
setQuery: (query) ->
|
24
|
+
@$input.val(query)
|
25
|
+
@updateQuery()
|
26
|
+
|
27
|
+
updateQuery: =>
|
28
|
+
oldQuery = @query
|
29
|
+
@query = @$input.val()
|
30
|
+
@broadcastQueryChange() unless @query == oldQuery
|
31
|
+
|
32
|
+
immediateBroadcastQueryChange: =>
|
33
|
+
@updateHash()
|
34
|
+
for handler in @listeners('query')
|
35
|
+
handler(@query)
|
36
|
+
|
37
|
+
updateHash: ->
|
38
|
+
window.location.hash = "##{@query}"
|
39
|
+
|
40
|
+
open: (event) =>
|
41
|
+
event?.preventDefault()
|
42
|
+
@$bar.removeClass('hidden')
|
43
|
+
@focus()
|
44
|
+
|
45
|
+
focus: ->
|
46
|
+
@$input.focus()[0].select()
|
47
|
+
|
48
|
+
closeIfEmpty: (event) =>
|
49
|
+
@close() unless @query.length
|
50
|
+
|
51
|
+
close: ->
|
52
|
+
@$bar.addClass('hidden')
|
@@ -8,10 +8,15 @@ class Chunk
|
|
8
8
|
@_text ||= AnsiStream.strip(@raw)
|
9
9
|
|
10
10
|
rawLines: ->
|
11
|
-
@_rawLines ||= @raw
|
11
|
+
@_rawLines ||= @splitLines(@raw)
|
12
12
|
|
13
13
|
lines: ->
|
14
|
-
@_lines ||= @text()
|
14
|
+
@_lines ||= @splitLines(@text())
|
15
|
+
|
16
|
+
splitLines: (text) ->
|
17
|
+
lines = text.split(/\r?\n/)
|
18
|
+
lines.pop() unless lines[lines.length - 1]
|
19
|
+
lines
|
15
20
|
|
16
21
|
class @Stream
|
17
22
|
INTERVAL = 1000
|
@@ -49,6 +54,8 @@ class @Stream
|
|
49
54
|
console?.log("Plugin error: #{error}")
|
50
55
|
|
51
56
|
broadcastOutput: (raw, args...) ->
|
57
|
+
return unless raw
|
58
|
+
|
52
59
|
chunk = new Chunk(raw)
|
53
60
|
for handler in @listeners('chunk')
|
54
61
|
try
|
@@ -1,3 +1,47 @@
|
|
1
|
+
class OutputLines
|
2
|
+
constructor: (@screen, @render) ->
|
3
|
+
@query = ''
|
4
|
+
@highlightRegexp = null
|
5
|
+
@raw = []
|
6
|
+
@renderingCache = {}
|
7
|
+
@stripCache = {}
|
8
|
+
|
9
|
+
setFilter: (query) ->
|
10
|
+
if @query = query
|
11
|
+
@screen.options.no_data_text = 'No matches'
|
12
|
+
else
|
13
|
+
@screen.options.no_data_text = 'Loading...'
|
14
|
+
@highlightRegexp = @buildHighlightRegexp(@query)
|
15
|
+
@reset()
|
16
|
+
|
17
|
+
reset: ->
|
18
|
+
@screen.update(@renderLines(@filter(@raw)))
|
19
|
+
|
20
|
+
filter: (lines) ->
|
21
|
+
return lines unless @query
|
22
|
+
line for line in lines when @strip(line).includes(@query)
|
23
|
+
|
24
|
+
strip: (line) ->
|
25
|
+
@stripCache[line] ||= AnsiStream.strip(line)
|
26
|
+
|
27
|
+
append: (lines) ->
|
28
|
+
@raw = @raw.concat(lines)
|
29
|
+
@screen.append(@renderLines(@filter(lines)))
|
30
|
+
|
31
|
+
renderLines: (lines) ->
|
32
|
+
for line in lines
|
33
|
+
@highlight(@renderingCache[line] ||= @render(line))
|
34
|
+
|
35
|
+
buildHighlightRegexp: (query) ->
|
36
|
+
pattern = query.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&').replace(/(\s+)/g, '(<[^>]+>)*$1(<[^>]+>)*')
|
37
|
+
new RegExp("(#{pattern})", 'g')
|
38
|
+
|
39
|
+
highlight: (renderedLine) ->
|
40
|
+
return renderedLine unless @query
|
41
|
+
|
42
|
+
renderedLine.replace(@highlightRegexp, '<mark>$1</mark>').replace(/(<mark>[^<>]*)((<[^>]+>)+)([^<>]*<\/mark>)/, '$1</mark>$2<mark>$4');
|
43
|
+
|
44
|
+
|
1
45
|
class @TTY
|
2
46
|
FORMATTERS = []
|
3
47
|
STICKY_SCROLL_TOLERENCE = 200
|
@@ -9,13 +53,19 @@ class @TTY
|
|
9
53
|
FORMATTERS.unshift(formatter)
|
10
54
|
|
11
55
|
constructor: ($body) ->
|
56
|
+
@outputLines = []
|
12
57
|
@$code = $body.find('code')
|
13
|
-
|
58
|
+
@$container = @$code.closest('.clusterize-scroll')
|
59
|
+
scroller = new Clusterize(
|
60
|
+
no_data_text: 'Loading...'
|
61
|
+
tag: 'div'
|
62
|
+
contentElem: @$code[0]
|
63
|
+
scrollElem: @$container[0]
|
64
|
+
)
|
65
|
+
@output = new OutputLines(scroller, (line) => @createLine(@formatChunks(line)))
|
14
66
|
|
15
|
-
|
16
|
-
output
|
17
|
-
@$code.empty()
|
18
|
-
output
|
67
|
+
filterOutput: (query) =>
|
68
|
+
@output.setFilter(query)
|
19
69
|
|
20
70
|
formatChunks: (chunk) ->
|
21
71
|
for formatter in FORMATTERS
|
@@ -23,32 +73,29 @@ class @TTY
|
|
23
73
|
chunk
|
24
74
|
|
25
75
|
appendChunk: (chunk) =>
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
class Scrolling
|
30
|
-
TOLERENCE = 200
|
76
|
+
lines = chunk.rawLines()
|
77
|
+
return unless lines.length
|
31
78
|
|
32
|
-
|
33
|
-
|
34
|
-
@initialScroll = true
|
79
|
+
@preserveScroll =>
|
80
|
+
@output.append(lines)
|
35
81
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
82
|
+
createLine: (fragment) ->
|
83
|
+
div = document.createElement('div')
|
84
|
+
div.appendChild(fragment)
|
85
|
+
div.className = 'output-line'
|
86
|
+
div.outerHTML
|
41
87
|
|
42
88
|
isScrolledToBottom: ->
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
89
|
+
(@getMaxScroll() - @$container.scrollTop()) < 1
|
90
|
+
|
91
|
+
scrollToBottom: ->
|
92
|
+
@$container.scrollTop(@getMaxScroll())
|
93
|
+
|
94
|
+
getMaxScroll: ->
|
95
|
+
@$code.parent().outerHeight(true) - @$container.outerHeight(true)
|
49
96
|
|
50
|
-
|
51
|
-
|
97
|
+
preserveScroll: (callback) ->
|
98
|
+
wasScrolledToBottom = @isScrolledToBottom()
|
99
|
+
callback()
|
100
|
+
@scrollToBottom() if wasScrolledToBottom
|
52
101
|
|
53
|
-
codeBottomPosition: ->
|
54
|
-
@$code.position().top + @$code.height()
|
@@ -54,17 +54,27 @@
|
|
54
54
|
}
|
55
55
|
}
|
56
56
|
|
57
|
+
.task-output-container {
|
58
|
+
height: calc(100vh - 9rem - 4rem - 1px); // .header and .deploy-banner. -1px is to floor the result
|
59
|
+
}
|
60
|
+
|
61
|
+
.output-line {
|
62
|
+
height: 1.5rem;
|
63
|
+
}
|
64
|
+
|
57
65
|
.deploy-banner {
|
66
|
+
height: 4rem;
|
58
67
|
background-color: #f0f4f7;
|
59
68
|
display: flex;
|
60
69
|
justify-content: center;
|
61
70
|
align-items: center;
|
62
71
|
position: relative;
|
63
72
|
flex-wrap: wrap;
|
73
|
+
overflow-x: hidden;
|
64
74
|
|
65
75
|
.deploy-banner-section {
|
66
76
|
display: inline-block;
|
67
|
-
padding: .
|
77
|
+
padding: .75rem 1.5rem;
|
68
78
|
}
|
69
79
|
|
70
80
|
.stack-link {
|
@@ -75,26 +85,44 @@
|
|
75
85
|
flex: none;
|
76
86
|
}
|
77
87
|
|
78
|
-
|
79
|
-
|
80
|
-
|
88
|
+
.deploy-banner-status {
|
89
|
+
height: 2px;
|
90
|
+
position: absolute;
|
91
|
+
bottom: 0;
|
81
92
|
left: 0;
|
82
|
-
|
93
|
+
}
|
83
94
|
|
84
|
-
|
85
|
-
|
95
|
+
&[data-status="failure"],
|
96
|
+
&[data-status="error"] {
|
97
|
+
.deploy-banner-status {
|
98
|
+
background-color: $bright-red;
|
99
|
+
width: 100%;
|
100
|
+
}
|
101
|
+
}
|
102
|
+
|
103
|
+
&[data-status="aborted"],
|
104
|
+
&[data-status="flapping"] {
|
105
|
+
.deploy-banner-status {
|
106
|
+
background-color: $orange;
|
107
|
+
width: 100%;
|
86
108
|
}
|
87
109
|
}
|
88
110
|
|
89
|
-
&[data-status="
|
90
|
-
|
91
|
-
|
111
|
+
&[data-status="success"] .deploy-banner-status {
|
112
|
+
background-color: $green;
|
113
|
+
width: 100%;
|
114
|
+
}
|
115
|
+
|
116
|
+
&[data-status="running"] .deploy-banner-status {
|
92
117
|
background-color: $blue;
|
93
|
-
bottom: 0;
|
94
|
-
left: 0;
|
95
118
|
width: 0%;
|
96
|
-
|
97
|
-
|
119
|
+
z-index: 9999;
|
120
|
+
-webkit-animation: loading-slide 1.2s linear infinite;
|
121
|
+
}
|
122
|
+
|
123
|
+
&[data-status="aborting"] .deploy-banner-status {
|
124
|
+
background-color: $orange;
|
125
|
+
width: 0%;
|
98
126
|
z-index: 9999;
|
99
127
|
-webkit-animation: loading-slide 1.2s linear infinite;
|
100
128
|
}
|
@@ -112,6 +140,19 @@
|
|
112
140
|
}
|
113
141
|
}
|
114
142
|
|
143
|
+
.search-bar {
|
144
|
+
background-color: #f0f4f7;
|
145
|
+
border: 1px solid #f0f4f7;
|
146
|
+
position: absolute;
|
147
|
+
right: 0px;
|
148
|
+
padding: .125rem;
|
149
|
+
border-bottom-left-radius: .25rem;
|
150
|
+
|
151
|
+
input[type="search"] {
|
152
|
+
width: 300px;
|
153
|
+
}
|
154
|
+
}
|
155
|
+
|
115
156
|
@include keyframes(loading-slide) {
|
116
157
|
0% { width: 0%; left: 0%; }
|
117
158
|
30% { left: 0%; }
|
@@ -124,27 +165,18 @@
|
|
124
165
|
|
125
166
|
.sidebar.enabled + .deploy-main {
|
126
167
|
margin-left: 300px;
|
127
|
-
|
128
|
-
.deploy-banner.sticky {
|
129
|
-
box-sizing: border-box;
|
130
|
-
margin-left: 300px;
|
131
|
-
}
|
132
168
|
}
|
133
169
|
|
134
170
|
.sidebar {
|
135
171
|
background-color: $slate;
|
136
172
|
color: white;
|
137
173
|
overflow: hidden;
|
138
|
-
height:
|
174
|
+
height: calc(100vh - 9rem - 1px); // .header. -1px is to floor the result
|
139
175
|
position: absolute;
|
140
176
|
width: 0px;
|
141
177
|
&.enabled {
|
142
178
|
width: 300px;
|
143
179
|
}
|
144
|
-
&.sticky {
|
145
|
-
top: 0;
|
146
|
-
position: fixed;
|
147
|
-
}
|
148
180
|
}
|
149
181
|
|
150
182
|
.sidebar-plugins {
|
@@ -1,6 +1,9 @@
|
|
1
1
|
.main {
|
2
2
|
@include flex(1);
|
3
3
|
padding-bottom: 3rem;
|
4
|
+
&.no-footer {
|
5
|
+
padding-bottom: 0;
|
6
|
+
}
|
4
7
|
}
|
5
8
|
|
6
9
|
section {
|
@@ -47,6 +50,7 @@
|
|
47
50
|
|
48
51
|
pre {
|
49
52
|
background-color: $terminal-black;
|
53
|
+
min-height: 100%;
|
50
54
|
overflow-x: auto;
|
51
55
|
color: #fff;
|
52
56
|
padding: 1.5rem;
|