gitlab-build-output 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.rspec +1 -0
- data/.rubocop.yml +12 -0
- data/.ruby-gemset +1 -0
- data/.rvm-gemset +1 -0
- data/.travis.yml +5 -0
- data/Gemfile +6 -0
- data/README.md +36 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/exe/gitlab-build-output +6 -0
- data/gitlab-build-output.gemspec +46 -0
- data/lib/gitlab/build/output.rb +1 -0
- data/lib/gitlab_build_output/ansi2html.rb +345 -0
- data/lib/gitlab_build_output/application.rb +156 -0
- data/lib/gitlab_build_output/core_ext/hash/to_json.rb +7 -0
- data/lib/gitlab_build_output/core_ext/hash.rb +1 -0
- data/lib/gitlab_build_output/core_ext/object/blank.rb +143 -0
- data/lib/gitlab_build_output/core_ext/object.rb +1 -0
- data/lib/gitlab_build_output/core_ext/regexp.rb +9 -0
- data/lib/gitlab_build_output/core_ext.rb +3 -0
- data/lib/gitlab_build_output/gitlab_api.rb +48 -0
- data/lib/gitlab_build_output/html_outputter.rb +11 -0
- data/lib/gitlab_build_output/job_tracer.rb +72 -0
- data/lib/gitlab_build_output/loop_runner.rb +11 -0
- data/lib/gitlab_build_output/outputter.rb +7 -0
- data/lib/gitlab_build_output/single_runner.rb +21 -0
- data/lib/gitlab_build_output/tracer.rb +28 -0
- data/lib/gitlab_build_output/version.rb +3 -0
- data/lib/gitlab_build_output.rb +29 -0
- metadata +216 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a9865235d35cdd14d7ae16dd29f567e5fb3ba500
|
4
|
+
data.tar.gz: 69bd5296f6b8180bb4afaf95253763b743bd3a24
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6d2d5528476051550754782fb7dbd477c34730935e6cbfa7b458f25f77d1bcad670daae1eaf96087a13fc8fd2b8cbab47244866c1a1305ddb594c06f84692482
|
7
|
+
data.tar.gz: bd93842b93f28ef57c7d724accde9f1858e0250c62a53435b7d1c38216632431fb163e56d6aea847c9a528a22aee506d516f4d7b154c1a36fc9139f1834dce1b
|
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/.rubocop.yml
ADDED
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
gitlab-build-output
|
data/.rvm-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
gitlab-build-output
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# Gitlab::Build::Output
|
2
|
+
|
3
|
+
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/gitlab/build/output`. To experiment with that code, run `bin/console` for an interactive prompt.
|
4
|
+
|
5
|
+
TODO: Delete this and the text above, and describe your gem
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem 'gitlab-build-output'
|
13
|
+
```
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
$ bundle
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
$ gem install gitlab-build-output
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
TODO: Write usage instructions here
|
26
|
+
|
27
|
+
## Development
|
28
|
+
|
29
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
30
|
+
|
31
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
32
|
+
|
33
|
+
## Contributing
|
34
|
+
|
35
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/gitlab-build-output.
|
36
|
+
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'gitlab/build/output'
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require 'irb'
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
lib = File.expand_path('../lib', __FILE__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'gitlab_build_output/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = 'gitlab-build-output'
|
9
|
+
spec.version = GitLabBuildOutput::VERSION
|
10
|
+
spec.authors = ['Jacob Carlborg']
|
11
|
+
spec.email = ['doob@me.com']
|
12
|
+
|
13
|
+
spec.summary = 'Prints the output of a GitLab CI job.'
|
14
|
+
spec.description = 'Prints the output of a GitLab CI job.'
|
15
|
+
spec.homepage = 'https://github.com/jacob-carlborg/gitlab-build-output'
|
16
|
+
|
17
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the
|
18
|
+
# 'allowed_push_host' to allow pushing to a single host or delete this section
|
19
|
+
# to allow pushing to any host.
|
20
|
+
if spec.respond_to?(:metadata)
|
21
|
+
spec.metadata['allowed_push_host'] = "https://rubygems.org"
|
22
|
+
else
|
23
|
+
raise 'RubyGems 2.0 or newer is required to protect against ' \
|
24
|
+
'public gem pushes.'
|
25
|
+
end
|
26
|
+
|
27
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
28
|
+
f.match(%r{^(test|spec|features)/})
|
29
|
+
end
|
30
|
+
spec.bindir = 'exe'
|
31
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
32
|
+
spec.require_paths = ['lib']
|
33
|
+
|
34
|
+
spec.add_dependency 'git', '~> 1.3'
|
35
|
+
spec.add_dependency 'gitlab', '~> 4.0'
|
36
|
+
spec.add_dependency 'git_clone_url', '~> 2.0'
|
37
|
+
|
38
|
+
spec.add_development_dependency 'bundler', '~> 1.14'
|
39
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
40
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
41
|
+
spec.add_development_dependency 'rubocop', '0.48.1'
|
42
|
+
spec.add_development_dependency 'pry', '~> 0.11'
|
43
|
+
# spec.add_development_dependency 'pry-byebug', '~> 3.5'
|
44
|
+
spec.add_development_dependency 'pry-rescue', '~> 1.4'
|
45
|
+
spec.add_development_dependency 'pry-stack_explorer', '~> 0.4.9'
|
46
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require_relative '../../gitlab_build_output'
|
@@ -0,0 +1,345 @@
|
|
1
|
+
# Stolen from GitLab https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/gitlab/ci/ansi2html.rb
|
2
|
+
# ANSI color library
|
3
|
+
#
|
4
|
+
# Implementation per http://en.wikipedia.org/wiki/ANSI_escape_code
|
5
|
+
module GitLabBuildOutput
|
6
|
+
module Ansi2html
|
7
|
+
TRACE_SECTION_REGEX = /section_((?:start)|(?:end)):(\d+):([^\r]+)\r\033\[0K/.freeze
|
8
|
+
|
9
|
+
# keys represent the trailing digit in color changing command (30-37, 40-47, 90-97. 100-107)
|
10
|
+
COLOR = {
|
11
|
+
0 => 'black', # not that this is gray in the intense color table
|
12
|
+
1 => 'red',
|
13
|
+
2 => 'green',
|
14
|
+
3 => 'yellow',
|
15
|
+
4 => 'blue',
|
16
|
+
5 => 'magenta',
|
17
|
+
6 => 'cyan',
|
18
|
+
7 => 'white', # not that this is gray in the dark (aka default) color table
|
19
|
+
}.freeze
|
20
|
+
|
21
|
+
STYLE_SWITCHES = {
|
22
|
+
bold: 0x01,
|
23
|
+
italic: 0x02,
|
24
|
+
underline: 0x04,
|
25
|
+
conceal: 0x08,
|
26
|
+
cross: 0x10
|
27
|
+
}.freeze
|
28
|
+
|
29
|
+
def self.convert(ansi, state = nil)
|
30
|
+
Converter.new.convert(ansi, state)
|
31
|
+
end
|
32
|
+
|
33
|
+
class Converter
|
34
|
+
def on_0(s) reset() end
|
35
|
+
|
36
|
+
def on_1(s) enable(STYLE_SWITCHES[:bold]) end
|
37
|
+
|
38
|
+
def on_3(s) enable(STYLE_SWITCHES[:italic]) end
|
39
|
+
|
40
|
+
def on_4(s) enable(STYLE_SWITCHES[:underline]) end
|
41
|
+
|
42
|
+
def on_8(s) enable(STYLE_SWITCHES[:conceal]) end
|
43
|
+
|
44
|
+
def on_9(s) enable(STYLE_SWITCHES[:cross]) end
|
45
|
+
|
46
|
+
def on_21(s) disable(STYLE_SWITCHES[:bold]) end
|
47
|
+
|
48
|
+
def on_22(s) disable(STYLE_SWITCHES[:bold]) end
|
49
|
+
|
50
|
+
def on_23(s) disable(STYLE_SWITCHES[:italic]) end
|
51
|
+
|
52
|
+
def on_24(s) disable(STYLE_SWITCHES[:underline]) end
|
53
|
+
|
54
|
+
def on_28(s) disable(STYLE_SWITCHES[:conceal]) end
|
55
|
+
|
56
|
+
def on_29(s) disable(STYLE_SWITCHES[:cross]) end
|
57
|
+
|
58
|
+
def on_30(s) set_fg_color(0) end
|
59
|
+
|
60
|
+
def on_31(s) set_fg_color(1) end
|
61
|
+
|
62
|
+
def on_32(s) set_fg_color(2) end
|
63
|
+
|
64
|
+
def on_33(s) set_fg_color(3) end
|
65
|
+
|
66
|
+
def on_34(s) set_fg_color(4) end
|
67
|
+
|
68
|
+
def on_35(s) set_fg_color(5) end
|
69
|
+
|
70
|
+
def on_36(s) set_fg_color(6) end
|
71
|
+
|
72
|
+
def on_37(s) set_fg_color(7) end
|
73
|
+
|
74
|
+
def on_38(s) set_fg_color_256(s) end
|
75
|
+
|
76
|
+
def on_39(s) set_fg_color(9) end
|
77
|
+
|
78
|
+
def on_40(s) set_bg_color(0) end
|
79
|
+
|
80
|
+
def on_41(s) set_bg_color(1) end
|
81
|
+
|
82
|
+
def on_42(s) set_bg_color(2) end
|
83
|
+
|
84
|
+
def on_43(s) set_bg_color(3) end
|
85
|
+
|
86
|
+
def on_44(s) set_bg_color(4) end
|
87
|
+
|
88
|
+
def on_45(s) set_bg_color(5) end
|
89
|
+
|
90
|
+
def on_46(s) set_bg_color(6) end
|
91
|
+
|
92
|
+
def on_47(s) set_bg_color(7) end
|
93
|
+
|
94
|
+
def on_48(s) set_bg_color_256(s) end
|
95
|
+
|
96
|
+
def on_49(s) set_bg_color(9) end
|
97
|
+
|
98
|
+
def on_90(s) set_fg_color(0, 'l') end
|
99
|
+
|
100
|
+
def on_91(s) set_fg_color(1, 'l') end
|
101
|
+
|
102
|
+
def on_92(s) set_fg_color(2, 'l') end
|
103
|
+
|
104
|
+
def on_93(s) set_fg_color(3, 'l') end
|
105
|
+
|
106
|
+
def on_94(s) set_fg_color(4, 'l') end
|
107
|
+
|
108
|
+
def on_95(s) set_fg_color(5, 'l') end
|
109
|
+
|
110
|
+
def on_96(s) set_fg_color(6, 'l') end
|
111
|
+
|
112
|
+
def on_97(s) set_fg_color(7, 'l') end
|
113
|
+
|
114
|
+
def on_99(s) set_fg_color(9, 'l') end
|
115
|
+
|
116
|
+
def on_100(s) set_bg_color(0, 'l') end
|
117
|
+
|
118
|
+
def on_101(s) set_bg_color(1, 'l') end
|
119
|
+
|
120
|
+
def on_102(s) set_bg_color(2, 'l') end
|
121
|
+
|
122
|
+
def on_103(s) set_bg_color(3, 'l') end
|
123
|
+
|
124
|
+
def on_104(s) set_bg_color(4, 'l') end
|
125
|
+
|
126
|
+
def on_105(s) set_bg_color(5, 'l') end
|
127
|
+
|
128
|
+
def on_106(s) set_bg_color(6, 'l') end
|
129
|
+
|
130
|
+
def on_107(s) set_bg_color(7, 'l') end
|
131
|
+
|
132
|
+
def on_109(s) set_bg_color(9, 'l') end
|
133
|
+
|
134
|
+
attr_accessor :offset, :n_open_tags, :fg_color, :bg_color, :style_mask
|
135
|
+
|
136
|
+
STATE_PARAMS = [:offset, :n_open_tags, :fg_color, :bg_color, :style_mask].freeze
|
137
|
+
|
138
|
+
def convert(stream, new_state)
|
139
|
+
reset_state
|
140
|
+
restore_state(new_state, stream) if new_state.present?
|
141
|
+
|
142
|
+
append = false
|
143
|
+
truncated = false
|
144
|
+
|
145
|
+
cur_offset = stream.tell
|
146
|
+
if cur_offset > @offset
|
147
|
+
@offset = cur_offset
|
148
|
+
truncated = true
|
149
|
+
else
|
150
|
+
stream.seek(@offset)
|
151
|
+
append = @offset > 0
|
152
|
+
end
|
153
|
+
start_offset = @offset
|
154
|
+
|
155
|
+
open_new_tag
|
156
|
+
|
157
|
+
stream.each_line do |line|
|
158
|
+
s = StringScanner.new(line)
|
159
|
+
until s.eos?
|
160
|
+
if s.scan(TRACE_SECTION_REGEX)
|
161
|
+
handle_section(s)
|
162
|
+
elsif s.scan(/\e([@-_])(.*?)([@-~])/)
|
163
|
+
handle_sequence(s)
|
164
|
+
elsif s.scan(/\e(([@-_])(.*?)?)?$/)
|
165
|
+
break
|
166
|
+
elsif s.scan(/</)
|
167
|
+
@out << '<'
|
168
|
+
elsif s.scan(/\r?\n/)
|
169
|
+
@out << '<br>'
|
170
|
+
else
|
171
|
+
@out << s.scan(/./m)
|
172
|
+
end
|
173
|
+
@offset += s.matched_size
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
close_open_tags()
|
178
|
+
|
179
|
+
OpenStruct.new(
|
180
|
+
html: @out.force_encoding(Encoding.default_external),
|
181
|
+
state: state,
|
182
|
+
append: append,
|
183
|
+
truncated: truncated,
|
184
|
+
offset: start_offset,
|
185
|
+
size: stream.tell - start_offset,
|
186
|
+
total: stream.size
|
187
|
+
)
|
188
|
+
end
|
189
|
+
|
190
|
+
def handle_section(s)
|
191
|
+
action = s[1]
|
192
|
+
timestamp = s[2]
|
193
|
+
section = s[3]
|
194
|
+
line = s.matched()[0...-5] # strips \r\033[0K
|
195
|
+
|
196
|
+
@out << %{<div class="hidden" data-action="#{action}" data-timestamp="#{timestamp}" data-section="#{section}">#{line}</div>}
|
197
|
+
end
|
198
|
+
|
199
|
+
def handle_sequence(s)
|
200
|
+
indicator = s[1]
|
201
|
+
commands = s[2].split ';'
|
202
|
+
terminator = s[3]
|
203
|
+
|
204
|
+
# We are only interested in color and text style changes - triggered by
|
205
|
+
# sequences starting with '\e[' and ending with 'm'. Any other control
|
206
|
+
# sequence gets stripped (including stuff like "delete last line")
|
207
|
+
return unless indicator == '[' && terminator == 'm'
|
208
|
+
|
209
|
+
close_open_tags()
|
210
|
+
|
211
|
+
if commands.empty?()
|
212
|
+
reset()
|
213
|
+
return
|
214
|
+
end
|
215
|
+
|
216
|
+
evaluate_command_stack(commands)
|
217
|
+
|
218
|
+
open_new_tag
|
219
|
+
end
|
220
|
+
|
221
|
+
def evaluate_command_stack(stack)
|
222
|
+
return unless command = stack.shift()
|
223
|
+
|
224
|
+
if self.respond_to?("on_#{command}", true)
|
225
|
+
self.__send__("on_#{command}", stack) # rubocop:disable GitlabSecurity/PublicSend
|
226
|
+
end
|
227
|
+
|
228
|
+
evaluate_command_stack(stack)
|
229
|
+
end
|
230
|
+
|
231
|
+
def open_new_tag
|
232
|
+
css_classes = []
|
233
|
+
|
234
|
+
unless @fg_color.nil?
|
235
|
+
fg_color = @fg_color
|
236
|
+
# Most terminals show bold colored text in the light color variant
|
237
|
+
# Let's mimic that here
|
238
|
+
if @style_mask & STYLE_SWITCHES[:bold] != 0
|
239
|
+
fg_color.sub!(/fg-(\w{2,}+)/, 'fg-l-\1')
|
240
|
+
end
|
241
|
+
css_classes << fg_color
|
242
|
+
end
|
243
|
+
css_classes << @bg_color unless @bg_color.nil?
|
244
|
+
|
245
|
+
STYLE_SWITCHES.each do |css_class, flag|
|
246
|
+
css_classes << "term-#{css_class}" if @style_mask & flag != 0
|
247
|
+
end
|
248
|
+
|
249
|
+
return if css_classes.empty?
|
250
|
+
|
251
|
+
@out << %{<span class="#{css_classes.join(' ')}">}
|
252
|
+
@n_open_tags += 1
|
253
|
+
end
|
254
|
+
|
255
|
+
def close_open_tags
|
256
|
+
while @n_open_tags > 0
|
257
|
+
@out << %{</span>}
|
258
|
+
@n_open_tags -= 1
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
def reset_state
|
263
|
+
@offset = 0
|
264
|
+
@n_open_tags = 0
|
265
|
+
@out = ''
|
266
|
+
reset
|
267
|
+
end
|
268
|
+
|
269
|
+
def state
|
270
|
+
state = STATE_PARAMS.inject({}) do |h, param|
|
271
|
+
h[param] = send(param) # rubocop:disable GitlabSecurity/PublicSend
|
272
|
+
h
|
273
|
+
end
|
274
|
+
Base64.urlsafe_encode64(state.to_json)
|
275
|
+
end
|
276
|
+
|
277
|
+
def restore_state(new_state, stream)
|
278
|
+
state = Base64.urlsafe_decode64(new_state)
|
279
|
+
state = JSON.parse(state, symbolize_names: true)
|
280
|
+
return if state[:offset].to_i > stream.size
|
281
|
+
|
282
|
+
STATE_PARAMS.each do |param|
|
283
|
+
send("#{param}=".to_sym, state[param]) # rubocop:disable GitlabSecurity/PublicSend
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
def reset
|
288
|
+
@fg_color = nil
|
289
|
+
@bg_color = nil
|
290
|
+
@style_mask = 0
|
291
|
+
end
|
292
|
+
|
293
|
+
def enable(flag)
|
294
|
+
@style_mask |= flag
|
295
|
+
end
|
296
|
+
|
297
|
+
def disable(flag)
|
298
|
+
@style_mask &= ~flag
|
299
|
+
end
|
300
|
+
|
301
|
+
def set_fg_color(color_index, prefix = nil)
|
302
|
+
@fg_color = get_term_color_class(color_index, ["fg", prefix])
|
303
|
+
end
|
304
|
+
|
305
|
+
def set_bg_color(color_index, prefix = nil)
|
306
|
+
@bg_color = get_term_color_class(color_index, ["bg", prefix])
|
307
|
+
end
|
308
|
+
|
309
|
+
def get_term_color_class(color_index, prefix)
|
310
|
+
color_name = COLOR[color_index]
|
311
|
+
return nil if color_name.nil?
|
312
|
+
|
313
|
+
get_color_class(["term", prefix, color_name])
|
314
|
+
end
|
315
|
+
|
316
|
+
def set_fg_color_256(command_stack)
|
317
|
+
css_class = get_xterm_color_class(command_stack, "fg")
|
318
|
+
@fg_color = css_class unless css_class.nil?
|
319
|
+
end
|
320
|
+
|
321
|
+
def set_bg_color_256(command_stack)
|
322
|
+
css_class = get_xterm_color_class(command_stack, "bg")
|
323
|
+
@bg_color = css_class unless css_class.nil?
|
324
|
+
end
|
325
|
+
|
326
|
+
def get_xterm_color_class(command_stack, prefix)
|
327
|
+
# the 38 and 48 commands have to be followed by "5" and the color index
|
328
|
+
return unless command_stack.length >= 2
|
329
|
+
return unless command_stack[0] == "5"
|
330
|
+
|
331
|
+
command_stack.shift() # ignore the "5" command
|
332
|
+
color_index = command_stack.shift().to_i
|
333
|
+
|
334
|
+
return unless color_index >= 0
|
335
|
+
return unless color_index <= 255
|
336
|
+
|
337
|
+
get_color_class(["xterm", prefix, color_index])
|
338
|
+
end
|
339
|
+
|
340
|
+
def get_color_class(segments)
|
341
|
+
[segments].flatten.compact.join('-')
|
342
|
+
end
|
343
|
+
end
|
344
|
+
end
|
345
|
+
end
|
@@ -0,0 +1,156 @@
|
|
1
|
+
module GitLabBuildOutput
|
2
|
+
# rubocop:disable Metrics/ClassLength
|
3
|
+
class Application
|
4
|
+
attr_reader :raw_args
|
5
|
+
attr_reader :args
|
6
|
+
attr_reader :option_parser
|
7
|
+
|
8
|
+
Args = Struct.new(
|
9
|
+
:help, :verbose, :version, :private_token, :endpoint, :loop, :html
|
10
|
+
)
|
11
|
+
|
12
|
+
def initialize(raw_args)
|
13
|
+
@raw_args = raw_args
|
14
|
+
@args = Args.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.start
|
18
|
+
new(ARGV).run
|
19
|
+
end
|
20
|
+
|
21
|
+
def run
|
22
|
+
parse_verbose_argument(raw_args, args)
|
23
|
+
handle_errors do
|
24
|
+
@option_parser = parse_arguments(raw_args, args)
|
25
|
+
exit = handle_arguments(args)
|
26
|
+
return if exit
|
27
|
+
runner.run
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def runner
|
34
|
+
@runner ||=
|
35
|
+
(args.loop ? LoopRunner : SingleRunner).new(job_tracer, outputter, 0.5)
|
36
|
+
end
|
37
|
+
|
38
|
+
def job_tracer
|
39
|
+
@job_tracer ||= JobTracer.new(
|
40
|
+
args.private_token, git_repository, args.endpoint
|
41
|
+
)
|
42
|
+
end
|
43
|
+
|
44
|
+
def outputter
|
45
|
+
@outputter ||= args.html ? HtmlOutputter.new : Outputter.new
|
46
|
+
end
|
47
|
+
|
48
|
+
def git_repository
|
49
|
+
raw_args.first
|
50
|
+
end
|
51
|
+
|
52
|
+
def error_handler
|
53
|
+
args.verbose ? :verbose_error_handler : :default_error_handler
|
54
|
+
end
|
55
|
+
|
56
|
+
def handle_errors(&block)
|
57
|
+
send(error_handler, &block)
|
58
|
+
end
|
59
|
+
|
60
|
+
def default_error_handler(&block)
|
61
|
+
verbose_error_handler(&block)
|
62
|
+
rescue OptionParser::InvalidArgument => e
|
63
|
+
args = e.args.join(' ')
|
64
|
+
puts "Invalid argument: #{args}"
|
65
|
+
exit 1
|
66
|
+
# rubocop:disable Lint/RescueException
|
67
|
+
rescue Exception => e
|
68
|
+
# rubocop:enable Lint/RescueException
|
69
|
+
puts "An unexpected error occurred: #{e.message}"
|
70
|
+
exit 1
|
71
|
+
end
|
72
|
+
|
73
|
+
def verbose_error_handler
|
74
|
+
yield
|
75
|
+
end
|
76
|
+
|
77
|
+
def valid_formats_description
|
78
|
+
@valid_formats_description ||= begin
|
79
|
+
list = VALID_FORMATS.join(', ')
|
80
|
+
"(#{list})"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def parse_verbose_argument(raw_args, args)
|
85
|
+
args.verbose = raw_args.include?('-v') || raw_args.include?('--verbose')
|
86
|
+
end
|
87
|
+
|
88
|
+
# rubocop:disable Metrics/AbcSize
|
89
|
+
# rubocop:disable Metrics/MethodLength
|
90
|
+
def parse_arguments(raw_args, args)
|
91
|
+
opts = OptionParser.new
|
92
|
+
opts.banner = banner
|
93
|
+
opts.separator ''
|
94
|
+
opts.separator 'Options:'
|
95
|
+
|
96
|
+
opts.on('-e', '--endpoint <endpoint>', 'The GitLab endpoint') do |e|
|
97
|
+
args.endpoint = e
|
98
|
+
end
|
99
|
+
|
100
|
+
opts.on('-p', '--private_token <private_token>', 'The private token ' \
|
101
|
+
'used to authenticate to GitLab') do |value|
|
102
|
+
args.private_token = value
|
103
|
+
end
|
104
|
+
|
105
|
+
opts.on('-l', '--[no-]loop', 'Loop until the job is complete') do |value|
|
106
|
+
args.loop = value
|
107
|
+
end
|
108
|
+
|
109
|
+
opts.on('--[no-]html', 'Output the trace with ANSI escape code ' \
|
110
|
+
'converted to HTML') do |value|
|
111
|
+
args.html = value
|
112
|
+
end
|
113
|
+
|
114
|
+
opts.on('-v', '--[no-]verbose', 'Show verbose output') do |value|
|
115
|
+
args.verbose = value
|
116
|
+
end
|
117
|
+
|
118
|
+
opts.on('--version', 'Print version information and exit') do
|
119
|
+
args.version = true
|
120
|
+
puts GitLabBuildOutput::VERSION
|
121
|
+
end
|
122
|
+
|
123
|
+
opts.on('-h', '--help', 'Show this message and exit') do
|
124
|
+
args.help = true
|
125
|
+
print_usage
|
126
|
+
end
|
127
|
+
|
128
|
+
opts.separator ''
|
129
|
+
opts.separator "Use the `-h' flag for help."
|
130
|
+
|
131
|
+
opts.parse!(raw_args)
|
132
|
+
opts
|
133
|
+
end
|
134
|
+
# rubocop:enable Metrics/AbcSize
|
135
|
+
# rubocop:enable Metrics/MethodLength
|
136
|
+
|
137
|
+
def handle_arguments(args)
|
138
|
+
if git_repository.nil?
|
139
|
+
print_usage
|
140
|
+
true
|
141
|
+
else
|
142
|
+
args.help || args.version
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def banner
|
147
|
+
@banner ||= "Usage: gitlab-build-output [options] <git_repository>\n" \
|
148
|
+
"Version: #{GitLabBuildOutput::VERSION}"
|
149
|
+
end
|
150
|
+
|
151
|
+
def print_usage
|
152
|
+
puts option_parser.to_s
|
153
|
+
end
|
154
|
+
end
|
155
|
+
# rubocop:enable Metrics/ClassLength
|
156
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'gitlab_build_output/core_ext/hash/to_json'
|
@@ -0,0 +1,143 @@
|
|
1
|
+
class Object
|
2
|
+
# An object is blank if it's false, empty, or a whitespace string.
|
3
|
+
# For example, +false+, '', ' ', +nil+, [], and {} are all blank.
|
4
|
+
#
|
5
|
+
# This simplifies
|
6
|
+
#
|
7
|
+
# !address || address.empty?
|
8
|
+
#
|
9
|
+
# to
|
10
|
+
#
|
11
|
+
# address.blank?
|
12
|
+
#
|
13
|
+
# @return [true, false]
|
14
|
+
def blank?
|
15
|
+
respond_to?(:empty?) ? !!empty? : !self
|
16
|
+
end
|
17
|
+
|
18
|
+
# An object is present if it's not blank.
|
19
|
+
#
|
20
|
+
# @return [true, false]
|
21
|
+
def present?
|
22
|
+
!blank?
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns the receiver if it's present otherwise returns +nil+.
|
26
|
+
# <tt>object.presence</tt> is equivalent to
|
27
|
+
#
|
28
|
+
# object.present? ? object : nil
|
29
|
+
#
|
30
|
+
# For example, something like
|
31
|
+
#
|
32
|
+
# state = params[:state] if params[:state].present?
|
33
|
+
# country = params[:country] if params[:country].present?
|
34
|
+
# region = state || country || 'US'
|
35
|
+
#
|
36
|
+
# becomes
|
37
|
+
#
|
38
|
+
# region = params[:state].presence || params[:country].presence || 'US'
|
39
|
+
#
|
40
|
+
# @return [Object]
|
41
|
+
def presence
|
42
|
+
self if present?
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class NilClass
|
47
|
+
# +nil+ is blank:
|
48
|
+
#
|
49
|
+
# nil.blank? # => true
|
50
|
+
#
|
51
|
+
# @return [true]
|
52
|
+
def blank?
|
53
|
+
true
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
class FalseClass
|
58
|
+
# +false+ is blank:
|
59
|
+
#
|
60
|
+
# false.blank? # => true
|
61
|
+
#
|
62
|
+
# @return [true]
|
63
|
+
def blank?
|
64
|
+
true
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
class TrueClass
|
69
|
+
# +true+ is not blank:
|
70
|
+
#
|
71
|
+
# true.blank? # => false
|
72
|
+
#
|
73
|
+
# @return [false]
|
74
|
+
def blank?
|
75
|
+
false
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
class Array
|
80
|
+
# An array is blank if it's empty:
|
81
|
+
#
|
82
|
+
# [].blank? # => true
|
83
|
+
# [1,2,3].blank? # => false
|
84
|
+
#
|
85
|
+
# @return [true, false]
|
86
|
+
alias_method :blank?, :empty?
|
87
|
+
end
|
88
|
+
|
89
|
+
class Hash
|
90
|
+
# A hash is blank if it's empty:
|
91
|
+
#
|
92
|
+
# {}.blank? # => true
|
93
|
+
# { key: 'value' }.blank? # => false
|
94
|
+
#
|
95
|
+
# @return [true, false]
|
96
|
+
alias_method :blank?, :empty?
|
97
|
+
end
|
98
|
+
|
99
|
+
class String
|
100
|
+
BLANK_RE = /\A[[:space:]]*\z/
|
101
|
+
|
102
|
+
# A string is blank if it's empty or contains whitespaces only:
|
103
|
+
#
|
104
|
+
# ''.blank? # => true
|
105
|
+
# ' '.blank? # => true
|
106
|
+
# "\t\n\r".blank? # => true
|
107
|
+
# ' blah '.blank? # => false
|
108
|
+
#
|
109
|
+
# Unicode whitespace is supported:
|
110
|
+
#
|
111
|
+
# "\u00a0".blank? # => true
|
112
|
+
#
|
113
|
+
# @return [true, false]
|
114
|
+
def blank?
|
115
|
+
# The regexp that matches blank strings is expensive. For the case of empty
|
116
|
+
# strings we can speed up this method (~3.5x) with an empty? call. The
|
117
|
+
# penalty for the rest of strings is marginal.
|
118
|
+
empty? || BLANK_RE.match?(self)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
class Numeric #:nodoc:
|
123
|
+
# No number is blank:
|
124
|
+
#
|
125
|
+
# 1.blank? # => false
|
126
|
+
# 0.blank? # => false
|
127
|
+
#
|
128
|
+
# @return [false]
|
129
|
+
def blank?
|
130
|
+
false
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
class Time #:nodoc:
|
135
|
+
# No Time is blank:
|
136
|
+
#
|
137
|
+
# Time.now.blank? # => false
|
138
|
+
#
|
139
|
+
# @return [false]
|
140
|
+
def blank?
|
141
|
+
false
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'gitlab_build_output/core_ext/object/blank'
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module GitLabBuildOutput
|
2
|
+
class GitLabApi
|
3
|
+
class Status
|
4
|
+
CREATED = 'created'.freeze
|
5
|
+
PENDING = 'pending'.freeze
|
6
|
+
RUNNING = 'running'.freeze
|
7
|
+
FAILED = 'failed'.freeze
|
8
|
+
SUCCESS = 'success'.freeze
|
9
|
+
CANCELED = 'canceled'.freeze
|
10
|
+
SKIPPED = 'skipped'.freeze
|
11
|
+
MANUAL = 'manual'.freeze
|
12
|
+
|
13
|
+
def self.done?(status)
|
14
|
+
case status
|
15
|
+
when FAILED, SUCCESS, CANCELED, SKIPPED
|
16
|
+
true
|
17
|
+
else
|
18
|
+
false
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(endpoint, private_token)
|
24
|
+
@client ||=
|
25
|
+
Gitlab.client(endpoint: endpoint, private_token: private_token)
|
26
|
+
end
|
27
|
+
|
28
|
+
def method_missing(method, *args, &block)
|
29
|
+
if @client.respond_to?(method)
|
30
|
+
@client.public_send(method, *args, &block)
|
31
|
+
else
|
32
|
+
super
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def respond_to_missing?(*args)
|
37
|
+
@client.respond_to_missing(*args)
|
38
|
+
end
|
39
|
+
|
40
|
+
# def job_trace(project, job_id)
|
41
|
+
# options = {}
|
42
|
+
# @client.send(:set_authorization_header, options)
|
43
|
+
# url = @client.endpoint +
|
44
|
+
# "/projects/#{@client.url_encode(project)}/builds/#{id}/trace"
|
45
|
+
# @client.validate HTTParty.get(url, options)
|
46
|
+
# end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module GitLabBuildOutput
|
2
|
+
class JobTracer
|
3
|
+
def initialize(private_token, git_repository, endpoint = nil, https: false)
|
4
|
+
@https = https
|
5
|
+
@tracer = Tracer.new
|
6
|
+
@git = Git.open(git_repository)
|
7
|
+
self.endpoint = endpoint
|
8
|
+
@gitlab = GitLabApi.new(self.send(:endpoint), private_token)
|
9
|
+
end
|
10
|
+
|
11
|
+
def trace
|
12
|
+
last_job = send(:last_job)
|
13
|
+
trace = tracer.next_trace(job_trace(last_job.id))
|
14
|
+
[trace, last_job.status]
|
15
|
+
end
|
16
|
+
|
17
|
+
def last_job
|
18
|
+
gitlab
|
19
|
+
.commit_builds(project_name, last_commit, per_page: 10, page: 1)
|
20
|
+
.detect do |e|
|
21
|
+
e.status != GitLabApi::Status::CREATED &&
|
22
|
+
e.status != GitLabApi::Status::MANUAL
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def last_commit
|
27
|
+
@last_commit ||= git.log[0].sha
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
attr_reader :git
|
33
|
+
attr_reader :gitlab
|
34
|
+
attr_reader :tracer
|
35
|
+
attr_reader :https
|
36
|
+
attr_reader :endpoint
|
37
|
+
|
38
|
+
def endpoint=(endpoint)
|
39
|
+
return @endpoint = endpoint if endpoint.present?
|
40
|
+
uri = URI.parse(parsed_url.host)
|
41
|
+
uri.path = '/api/v3'
|
42
|
+
uri.host = parsed_url.host
|
43
|
+
uri.scheme ||= parsed_url.scheme || scheme
|
44
|
+
uri.scheme = scheme if uri.scheme == 'ssh'
|
45
|
+
|
46
|
+
@endpoint = uri.to_s
|
47
|
+
end
|
48
|
+
|
49
|
+
def scheme
|
50
|
+
@scheme ||= https ? 'https' : 'http'
|
51
|
+
end
|
52
|
+
|
53
|
+
def project_name
|
54
|
+
@project_name ||= begin
|
55
|
+
path = parsed_url.path.gsub(/\A\/+/, '')
|
56
|
+
File.join(File.dirname(path), File.basename(path, File.extname(path)))
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def git_url
|
61
|
+
@git_url ||= git.remote.url
|
62
|
+
end
|
63
|
+
|
64
|
+
def parsed_url
|
65
|
+
@parsed_url ||= GitCloneUrl.parse(git_url)
|
66
|
+
end
|
67
|
+
|
68
|
+
def job_trace(job_id)
|
69
|
+
gitlab.job_trace(project_name, job_id)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module GitLabBuildOutput
|
2
|
+
class SingleRunner
|
3
|
+
def initialize(job_tracer, outputter, interval)
|
4
|
+
@job_tracer = job_tracer
|
5
|
+
@outputter = outputter
|
6
|
+
@interval = interval
|
7
|
+
end
|
8
|
+
|
9
|
+
def run
|
10
|
+
trace, status = job_tracer.trace
|
11
|
+
outputter.output(trace)
|
12
|
+
status
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
attr_reader :job_tracer
|
18
|
+
attr_reader :outputter
|
19
|
+
attr_reader :interval
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module GitLabBuildOutput
|
2
|
+
class Tracer
|
3
|
+
def initialize
|
4
|
+
@previous_trace = ''
|
5
|
+
end
|
6
|
+
|
7
|
+
def next_trace(trace)
|
8
|
+
new_trace = diff(previous_trace, trace)
|
9
|
+
return '' if new_trace.nil?
|
10
|
+
previous_trace << new_trace
|
11
|
+
new_trace
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
attr_reader :previous_trace
|
17
|
+
|
18
|
+
def diff(s1, s2)
|
19
|
+
s1 ||= ''
|
20
|
+
s2 ||= ''
|
21
|
+
|
22
|
+
return '' if s1 == s2
|
23
|
+
index = s2.chars.zip(s1.chars).index { |a, b| a != b }
|
24
|
+
return nil if index.nil?
|
25
|
+
s2[index..-1]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'erb'
|
3
|
+
require 'json'
|
4
|
+
require 'ostruct'
|
5
|
+
require 'stringio'
|
6
|
+
require 'strscan'
|
7
|
+
|
8
|
+
require 'git'
|
9
|
+
require 'gitlab'
|
10
|
+
require 'git_clone_url'
|
11
|
+
require 'httparty'
|
12
|
+
|
13
|
+
require 'gitlab_build_output/core_ext'
|
14
|
+
|
15
|
+
require 'gitlab_build_output/ansi2html'
|
16
|
+
require 'gitlab_build_output/application'
|
17
|
+
require 'gitlab_build_output/gitlab_api'
|
18
|
+
require 'gitlab_build_output/html_outputter'
|
19
|
+
require 'gitlab_build_output/job_tracer'
|
20
|
+
require 'gitlab_build_output/outputter'
|
21
|
+
require 'gitlab_build_output/tracer'
|
22
|
+
require 'gitlab_build_output/version'
|
23
|
+
|
24
|
+
# the order of these are important
|
25
|
+
require 'gitlab_build_output/single_runner'
|
26
|
+
require 'gitlab_build_output/loop_runner'
|
27
|
+
|
28
|
+
module GitLabBuildOutput
|
29
|
+
end
|
metadata
ADDED
@@ -0,0 +1,216 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gitlab-build-output
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jacob Carlborg
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-01-29 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: git
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.3'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: gitlab
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '4.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '4.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: git_clone_url
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '2.0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '2.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: bundler
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.14'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ~>
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.14'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rake
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ~>
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '10.0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ~>
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '10.0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rspec
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ~>
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '3.0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ~>
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '3.0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rubocop
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - '='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 0.48.1
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - '='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 0.48.1
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: pry
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ~>
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0.11'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ~>
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0.11'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: pry-rescue
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ~>
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '1.4'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ~>
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '1.4'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: pry-stack_explorer
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ~>
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: 0.4.9
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ~>
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: 0.4.9
|
153
|
+
description: Prints the output of a GitLab CI job.
|
154
|
+
email:
|
155
|
+
- doob@me.com
|
156
|
+
executables:
|
157
|
+
- gitlab-build-output
|
158
|
+
extensions: []
|
159
|
+
extra_rdoc_files: []
|
160
|
+
files:
|
161
|
+
- .gitignore
|
162
|
+
- .rspec
|
163
|
+
- .rubocop.yml
|
164
|
+
- .ruby-gemset
|
165
|
+
- .rvm-gemset
|
166
|
+
- .travis.yml
|
167
|
+
- Gemfile
|
168
|
+
- README.md
|
169
|
+
- Rakefile
|
170
|
+
- bin/console
|
171
|
+
- bin/setup
|
172
|
+
- exe/gitlab-build-output
|
173
|
+
- gitlab-build-output.gemspec
|
174
|
+
- lib/gitlab/build/output.rb
|
175
|
+
- lib/gitlab_build_output.rb
|
176
|
+
- lib/gitlab_build_output/ansi2html.rb
|
177
|
+
- lib/gitlab_build_output/application.rb
|
178
|
+
- lib/gitlab_build_output/core_ext.rb
|
179
|
+
- lib/gitlab_build_output/core_ext/hash.rb
|
180
|
+
- lib/gitlab_build_output/core_ext/hash/to_json.rb
|
181
|
+
- lib/gitlab_build_output/core_ext/object.rb
|
182
|
+
- lib/gitlab_build_output/core_ext/object/blank.rb
|
183
|
+
- lib/gitlab_build_output/core_ext/regexp.rb
|
184
|
+
- lib/gitlab_build_output/gitlab_api.rb
|
185
|
+
- lib/gitlab_build_output/html_outputter.rb
|
186
|
+
- lib/gitlab_build_output/job_tracer.rb
|
187
|
+
- lib/gitlab_build_output/loop_runner.rb
|
188
|
+
- lib/gitlab_build_output/outputter.rb
|
189
|
+
- lib/gitlab_build_output/single_runner.rb
|
190
|
+
- lib/gitlab_build_output/tracer.rb
|
191
|
+
- lib/gitlab_build_output/version.rb
|
192
|
+
homepage: https://github.com/jacob-carlborg/gitlab-build-output
|
193
|
+
licenses: []
|
194
|
+
metadata:
|
195
|
+
allowed_push_host: https://rubygems.org
|
196
|
+
post_install_message:
|
197
|
+
rdoc_options: []
|
198
|
+
require_paths:
|
199
|
+
- lib
|
200
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
201
|
+
requirements:
|
202
|
+
- - '>='
|
203
|
+
- !ruby/object:Gem::Version
|
204
|
+
version: '0'
|
205
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
206
|
+
requirements:
|
207
|
+
- - '>='
|
208
|
+
- !ruby/object:Gem::Version
|
209
|
+
version: '0'
|
210
|
+
requirements: []
|
211
|
+
rubyforge_project:
|
212
|
+
rubygems_version: 2.4.8
|
213
|
+
signing_key:
|
214
|
+
specification_version: 4
|
215
|
+
summary: Prints the output of a GitLab CI job.
|
216
|
+
test_files: []
|