xcskarel 0.11.0 → 0.14.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/xcskarel +46 -25
- data/lib/xcskarel/application.rb +54 -0
- data/lib/xcskarel/filter.rb +27 -10
- data/lib/xcskarel/server.rb +22 -5
- data/lib/xcskarel/version.rb +1 -1
- data/spec/application_spec.rb +39 -0
- data/spec/filter_spec.rb +46 -3
- metadata +3 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f69158b3f6c2220edec0c74f13e59c1dc1785ae5
|
4
|
+
data.tar.gz: 2584d85e540450a8c65e52ea9f2987a23b93e8e3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2ecc1b825c615c8be675b732a52639553bcdbd34d10200499d70de79dd78bdf7ef82c70d794f5f7b2125cdd7a07d96e09b838de32a6e8e8551aa5cdf73e39833
|
7
|
+
data.tar.gz: 7136da6e5c5d838eb575d54aad6ffbcd060dce1265b0a87b76d9f4adba2e8b41354f306c7c544497c37237892381a1c6852770fb52bc3b1e98b3f8746d2e6b77
|
data/bin/xcskarel
CHANGED
@@ -12,18 +12,25 @@ class XCSKarelApplication
|
|
12
12
|
include Commander::Methods
|
13
13
|
|
14
14
|
def add_xcs_options(c)
|
15
|
-
c.option '--host Hostname', '
|
16
|
-
c.option '--user Username', 'Xcode Server username'
|
17
|
-
c.option '--pass Password', 'Xcode Server password'
|
15
|
+
c.option '--host Hostname', 'Xcode Server\'s hostname or IP address (default: localhost) (also can be specified as the environment variable XCSKAREL_HOST)'
|
16
|
+
c.option '--user Username', 'Xcode Server username (also can be specified as the environment variable XCSKAREL_USER)'
|
17
|
+
c.option '--pass Password', 'Xcode Server password (also can be specified as the environment variable XCSKAREL_PASS)'
|
18
18
|
end
|
19
19
|
|
20
20
|
def add_bot_options(c)
|
21
|
-
c.option '--bot
|
21
|
+
c.option '--bot BOT_ID_OR_NAME', '(required) Bot identifier or name'
|
22
|
+
end
|
23
|
+
|
24
|
+
def add_bot_or_integration_options(c)
|
25
|
+
c.option '--bot BOT_ID_OR_NAME', 'Bot identifier or name'
|
26
|
+
c.option '--integration INTEGRATION_ID', 'Integration identifier'
|
22
27
|
end
|
23
28
|
|
24
29
|
def create_server_from_options(options)
|
25
|
-
host = options.host || "localhost"
|
26
|
-
|
30
|
+
host = options.host || ENV['XCSKAREL_HOST'] || "localhost"
|
31
|
+
user = options.user || ENV['XCSKAREL_USER']
|
32
|
+
pass = options.pass || ENV['XCSKAREL_PASS']
|
33
|
+
XCSKarel::Server.new(host, user, pass)
|
27
34
|
end
|
28
35
|
|
29
36
|
def run
|
@@ -92,14 +99,7 @@ class XCSKarelApplication
|
|
92
99
|
c.action do |args, options|
|
93
100
|
server = create_server_from_options(options)
|
94
101
|
all_bots = server.get_bots
|
95
|
-
|
96
|
-
# bot_objs = all_bots.map { |json| XCSKarel::Bot.new(json) }
|
97
|
-
# bot_files = bot_objs.map { |bot| name = "./xcskarel/#{bot.json['_id']}.json"; bot.to_file(name); name }
|
98
|
-
unless options.no_filter
|
99
|
-
all_bots = XCSKarel::Filter.filter_key_paths(all_bots, ['name', '_id'])
|
100
|
-
end
|
101
|
-
out = options.no_pretty ? JSON.generate(all_bots) : JSON.pretty_generate(all_bots)
|
102
|
-
puts out
|
102
|
+
puts XCSKarel::Application.format(all_bots, options, ['name', '_id'])
|
103
103
|
end
|
104
104
|
end
|
105
105
|
|
@@ -112,12 +112,37 @@ class XCSKarelApplication
|
|
112
112
|
c.action do |args, options|
|
113
113
|
raise "No Bot id was specified, please see `xcskarel integrations --help`" unless options.bot
|
114
114
|
server = create_server_from_options(options)
|
115
|
-
all_integrations =
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
115
|
+
all_integrations = XCSKarel::Application.integrations(server, options.bot)
|
116
|
+
puts XCSKarel::Application.format(all_integrations, options, ['number', '_id', 'currentStep', 'result'])
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
command :issues do |c|
|
121
|
+
c.syntax = 'xcskarel issues [options]'
|
122
|
+
c.description = 'Fetches issues of an integration or a Bot\'s last integration'
|
123
|
+
c.example 'fetches issues for a given integration on localhost', 'xcskarel issues --integration 448946985304230369392c2e6b05a821'
|
124
|
+
c.example 'fetches issues for a given bot\'s last integration', 'xcskarel issues --bot "My Bot" --host 192.168.1.64'
|
125
|
+
add_xcs_options(c)
|
126
|
+
add_bot_or_integration_options(c)
|
127
|
+
c.action do |args, options|
|
128
|
+
server = create_server_from_options(options)
|
129
|
+
bot = options.bot
|
130
|
+
integration = options.integration
|
131
|
+
raise "Exactly one of Bot or Integration must be specified, please see `xcskarel issues --help`" unless bot || integration
|
132
|
+
all_issues = XCSKarel::Application.issues(server, bot, integration)
|
133
|
+
key_paths = [
|
134
|
+
'buildServiceErrors.*',
|
135
|
+
'buildServiceWarnings.message',
|
136
|
+
'triggerErrors.*',
|
137
|
+
'analyzerWarnings.*',
|
138
|
+
'errors.*.message',
|
139
|
+
'testFailures.*.testCase',
|
140
|
+
'testFailures.*.documentFilePath',
|
141
|
+
'testFailures.*.message',
|
142
|
+
'warnings.*.message',
|
143
|
+
'warnings.*.documentFilePath'
|
144
|
+
]
|
145
|
+
puts XCSKarel::Application.format(all_issues, options, key_paths, false)
|
121
146
|
end
|
122
147
|
end
|
123
148
|
|
@@ -144,11 +169,7 @@ class XCSKarelApplication
|
|
144
169
|
c.action do |args, options|
|
145
170
|
server = create_server_from_options(options)
|
146
171
|
all_health = server.get_health
|
147
|
-
|
148
|
-
all_health = XCSKarel::Filter.filter_key_paths(all_health, ['uptime'])
|
149
|
-
end
|
150
|
-
out = options.no_pretty ? JSON.generate(all_health) : JSON.pretty_generate(all_health)
|
151
|
-
puts out
|
172
|
+
puts XCSKarel::Application.format(all_health, options, ['uptime'])
|
152
173
|
end
|
153
174
|
end
|
154
175
|
|
data/lib/xcskarel/application.rb
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
require 'xcskarel/filter'
|
2
|
+
require 'json'
|
3
|
+
|
1
4
|
module XCSKarel
|
2
5
|
module Application
|
3
6
|
def self.choose_bot(server)
|
@@ -91,6 +94,12 @@ module XCSKarel
|
|
91
94
|
puts table.to_s
|
92
95
|
end
|
93
96
|
|
97
|
+
def self.integrations(server, bot_id_or_name)
|
98
|
+
bot = server.find_bot_by_id_or_name(bot_id_or_name)
|
99
|
+
XCSKarel.log.debug "Found Bot #{bot['name']} with id #{bot['_id']}".yellow
|
100
|
+
server.get_integrations(bot['_id'])
|
101
|
+
end
|
102
|
+
|
94
103
|
def self.integrate(server, bot_id_or_name)
|
95
104
|
|
96
105
|
# find bot by id or name
|
@@ -104,5 +113,50 @@ module XCSKarel
|
|
104
113
|
self.print_status(server)
|
105
114
|
end
|
106
115
|
|
116
|
+
def self.issues(server, bot_id_or_name, integration_id)
|
117
|
+
integration = nil
|
118
|
+
if bot_id_or_name
|
119
|
+
bot = server.find_bot_by_id_or_name(bot_id_or_name)
|
120
|
+
# fetch last integration
|
121
|
+
integration = (server.get_integrations(bot['_id']).first || {})['_id']
|
122
|
+
raise "No Integration found for Bot \"#{bot['name']}\"".red unless integration
|
123
|
+
else
|
124
|
+
integration = server.get_integration(integration_id)['_id']
|
125
|
+
raise "No Integration found for id #{integration_id}".red unless integration
|
126
|
+
end
|
127
|
+
|
128
|
+
# fetch issues
|
129
|
+
issues = server.get_issues(integration)
|
130
|
+
return issues
|
131
|
+
end
|
132
|
+
|
133
|
+
def self.format(object, options, allowed_key_paths, allow_empty_container_leaves=true)
|
134
|
+
unless options.no_filter
|
135
|
+
|
136
|
+
extra_filters = []
|
137
|
+
|
138
|
+
unless allow_empty_container_leaves
|
139
|
+
# optionally add an override to filter out empty containers as leaves
|
140
|
+
empty_leaves = lambda do |k,v|
|
141
|
+
return true unless v.is_a?(Array) || v.is_a?(Hash)
|
142
|
+
return v.count > 0
|
143
|
+
end
|
144
|
+
extra_filters << empty_leaves
|
145
|
+
end
|
146
|
+
|
147
|
+
# create a super-filter composed from all the gathered filters
|
148
|
+
custom_filters = lambda do |k,v|
|
149
|
+
extra_filters.each do |filter|
|
150
|
+
return false unless filter.call(k,v)
|
151
|
+
end
|
152
|
+
return true
|
153
|
+
end
|
154
|
+
|
155
|
+
object = XCSKarel::Filter.filter_key_paths(object, allowed_key_paths, custom_filters)
|
156
|
+
end
|
157
|
+
out = options.no_pretty ? JSON.generate(object) : JSON.pretty_generate(object)
|
158
|
+
return out
|
159
|
+
end
|
160
|
+
|
107
161
|
end
|
108
162
|
end
|
data/lib/xcskarel/filter.rb
CHANGED
@@ -6,11 +6,11 @@ module XCSKarel
|
|
6
6
|
# returns a copy of the passed-in hash with only the provided keypaths kept
|
7
7
|
# e.g. "name" will keep the key "name" at the top level.
|
8
8
|
# supports even nested keypaths
|
9
|
-
def self.filter_key_paths(object, key_paths)
|
9
|
+
def self.filter_key_paths(object, key_paths, custom_block=nil)
|
10
10
|
|
11
11
|
# array?
|
12
12
|
if object.is_a?(Array)
|
13
|
-
return self.filter_array(object, key_paths)
|
13
|
+
return self.filter_array(object, key_paths, custom_block)
|
14
14
|
end
|
15
15
|
|
16
16
|
# hash?
|
@@ -19,14 +19,31 @@ module XCSKarel
|
|
19
19
|
unique_key_paths = Set.new(key_paths)
|
20
20
|
object.each do |k,v|
|
21
21
|
|
22
|
+
next if !custom_block.nil? && !custom_block.call(k, v)
|
23
|
+
|
22
24
|
keys = k.split('.') # key-paths must be separated by a period
|
23
|
-
|
24
|
-
key.split('.')
|
25
|
-
|
26
|
-
|
27
|
-
|
25
|
+
matches = unique_key_paths.select do |key|
|
26
|
+
kp = key.split('.')
|
27
|
+
kp.first == keys.first || kp.first == "*"
|
28
|
+
end
|
29
|
+
if matches.count > 0
|
30
|
+
|
31
|
+
child_key_paths = []
|
32
|
+
split_matches = matches.map { |match| match.split('.') }
|
33
|
+
|
34
|
+
# universal wildcard
|
35
|
+
if matches.index("*") != nil
|
36
|
+
child_key_paths = child_key_paths << "*"
|
37
|
+
else
|
38
|
+
# normal key-path (including key-pathed wildcard)
|
39
|
+
child_key_paths = child_key_paths + split_matches.map { |split_match| split_match.drop(1).join('.') }
|
40
|
+
end
|
41
|
+
|
28
42
|
# if there are no more key paths, we just take everything (whitelisted by default)
|
29
|
-
|
43
|
+
new_k = keys.first
|
44
|
+
new_v = filter_key_paths(v, child_key_paths, custom_block)
|
45
|
+
next if !custom_block.nil? && !custom_block.call(new_k, new_v)
|
46
|
+
new_hash[new_k] = new_v
|
30
47
|
end
|
31
48
|
end
|
32
49
|
return new_hash
|
@@ -39,11 +56,11 @@ module XCSKarel
|
|
39
56
|
private
|
40
57
|
|
41
58
|
# filters each element
|
42
|
-
def self.filter_array(array, key_paths)
|
59
|
+
def self.filter_array(array, key_paths, custom_block=nil)
|
43
60
|
new_array = Array.new
|
44
61
|
keys = Set.new(key_paths)
|
45
62
|
array.each do |i|
|
46
|
-
new_array << filter_key_paths(i, key_paths)
|
63
|
+
new_array << filter_key_paths(i, key_paths, custom_block)
|
47
64
|
end
|
48
65
|
return new_array
|
49
66
|
end
|
data/lib/xcskarel/server.rb
CHANGED
@@ -31,12 +31,29 @@ module XCSKarel
|
|
31
31
|
bots.sort_by { |bot| bot['name'] }
|
32
32
|
end
|
33
33
|
|
34
|
-
def get_integrations(bot_id)
|
35
|
-
|
34
|
+
def get_integrations(bot_id, limit=nil)
|
35
|
+
raise "No Bot id provided".red if !bot_id || bot_id.empty?
|
36
|
+
limit_s = limit ? "?last=#{limit}" : ""
|
37
|
+
response = get_endpoint("/bots/#{bot_id}/integrations#{limit_s}")
|
36
38
|
raise "Failed to fetch Integrations for Bot #{bot_id} from Xcode Server at #{@host}, response: #{response.status}: #{response.body}".red if response.status != 200
|
37
39
|
JSON.parse(response.body)['results']
|
38
40
|
end
|
39
41
|
|
42
|
+
def get_integration(integration_id)
|
43
|
+
raise "No Integration id provided".red if !integration_id || integration_id.empty?
|
44
|
+
response = get_endpoint("/integrations/#{integration_id}")
|
45
|
+
raise "Failed to fetch an Integrations with id #{integration_id} from Xcode Server at #{@host}, response: #{response.status}: #{response.body}".red if response.status != 200
|
46
|
+
JSON.parse(response.body)
|
47
|
+
end
|
48
|
+
|
49
|
+
def get_issues(integration_id)
|
50
|
+
raise "No Integration id provided".red if !integration_id || integration_id.empty?
|
51
|
+
response = get_endpoint("/integrations/#{integration_id}/issues")
|
52
|
+
raise "Failed to fetch issues for an Integrations with id #{integration_id} from Xcode Server at #{@host}, response: #{response.status}: #{response.body}".red if response.status != 200
|
53
|
+
body = JSON.parse(response.body)
|
54
|
+
return body['results'] || body # support both xcode 6 and 7
|
55
|
+
end
|
56
|
+
|
40
57
|
def get_health
|
41
58
|
response = get_endpoint("/health")
|
42
59
|
raise "Failed to get Health of #{@host}" if response.status != 200
|
@@ -52,7 +69,7 @@ module XCSKarel
|
|
52
69
|
status['name'] = bot['name']
|
53
70
|
status['id'] = bot['_id']
|
54
71
|
status['branch'] = XCSKarel::Config.new(bot, nil, nil).branch
|
55
|
-
last_integration = self.get_integrations(bot['_id']).first # sorted from newest to oldest
|
72
|
+
last_integration = self.get_integrations(bot['_id'], 1).first # sorted from newest to oldest
|
56
73
|
if last_integration
|
57
74
|
status['current_step'] = last_integration['currentStep']
|
58
75
|
status['result'] = last_integration['result']
|
@@ -78,10 +95,10 @@ module XCSKarel
|
|
78
95
|
return integration
|
79
96
|
end
|
80
97
|
|
81
|
-
def find_bot_by_id_or_name(id_or_name)
|
98
|
+
def find_bot_by_id_or_name(id_or_name, raise_if_none_found=true)
|
82
99
|
bots = self.get_bots
|
83
100
|
found_bots = bots.select { |bot| [bot['name'], bot['_id']].index(id_or_name) != nil }
|
84
|
-
raise "No Bot found for \"#{id_or_name}\"".red if found_bots.count == 0
|
101
|
+
raise "No Bot found for \"#{id_or_name}\"".red if raise_if_none_found && found_bots.count == 0
|
85
102
|
XCSKarel.log.warn "More than one Bot found for \"#{id_or_name}\", taking the first one (you shouldn't have more Bots with the same name!)".red if found_bots.count > 1
|
86
103
|
return found_bots.first
|
87
104
|
end
|
data/lib/xcskarel/version.rb
CHANGED
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'xcskarel/application'
|
2
|
+
|
3
|
+
class Opts
|
4
|
+
attr_accessor :no_pretty
|
5
|
+
attr_accessor :no_filter
|
6
|
+
end
|
7
|
+
|
8
|
+
describe XCSKarel do
|
9
|
+
describe XCSKarel::Application do
|
10
|
+
describe "format" do
|
11
|
+
|
12
|
+
def default_options
|
13
|
+
opts = Opts.new
|
14
|
+
opts.no_filter = false
|
15
|
+
opts.no_pretty = false
|
16
|
+
return opts
|
17
|
+
end
|
18
|
+
|
19
|
+
it "filters correctly empty leaf arrays" do
|
20
|
+
object = [{
|
21
|
+
"errors" => {
|
22
|
+
"unresolvedIssues" => [],
|
23
|
+
"resolvedIssues" => []
|
24
|
+
},
|
25
|
+
"testFailures" => {
|
26
|
+
"unresolvedIssues" => ["one"],
|
27
|
+
"resolvedIssues" => []
|
28
|
+
}
|
29
|
+
}]
|
30
|
+
exp = JSON.pretty_generate([{
|
31
|
+
"testFailures" => {
|
32
|
+
"unresolvedIssues" => ["one"]
|
33
|
+
}
|
34
|
+
}])
|
35
|
+
expect(XCSKarel::Application.format(object, default_options, ["errors.*", "testFailures.*"], false)).to eq(exp)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/spec/filter_spec.rb
CHANGED
@@ -3,8 +3,8 @@ require 'xcskarel/filter'
|
|
3
3
|
describe XCSKarel do
|
4
4
|
describe XCSKarel::Filter do
|
5
5
|
|
6
|
-
def test(inObj, key_paths)
|
7
|
-
XCSKarel::Filter.filter_key_paths(inObj, key_paths)
|
6
|
+
def test(inObj, key_paths, custom_block=nil)
|
7
|
+
XCSKarel::Filter.filter_key_paths(inObj, key_paths, custom_block)
|
8
8
|
end
|
9
9
|
|
10
10
|
it "handles empty hash" do
|
@@ -59,7 +59,7 @@ describe XCSKarel do
|
|
59
59
|
"old" => -2
|
60
60
|
}
|
61
61
|
}
|
62
|
-
expect(test(obj, ["oranges", "apples"])).to eq(exp)
|
62
|
+
expect(test(obj, ["oranges.*", "apples"])).to eq(exp)
|
63
63
|
end
|
64
64
|
|
65
65
|
it "handles basic key path with an array without popping the key path" do
|
@@ -78,5 +78,48 @@ describe XCSKarel do
|
|
78
78
|
]
|
79
79
|
expect(test(obj, ["new"])).to eq(exp)
|
80
80
|
end
|
81
|
+
|
82
|
+
it "custom block can override and filter out based on keys" do
|
83
|
+
obj = [
|
84
|
+
"apples",
|
85
|
+
{
|
86
|
+
"new" => ["one", "two"],
|
87
|
+
"old" => []
|
88
|
+
}
|
89
|
+
]
|
90
|
+
exp = [
|
91
|
+
"apples",
|
92
|
+
{
|
93
|
+
"new" => ["one", "two"]
|
94
|
+
}
|
95
|
+
]
|
96
|
+
custom_block = lambda do |k,v|
|
97
|
+
return true unless v.is_a?(Array)
|
98
|
+
return v.count > 0
|
99
|
+
end
|
100
|
+
expect(test(obj, ["new", "old"], custom_block)).to eq(exp)
|
101
|
+
end
|
102
|
+
|
103
|
+
it "handles the wildcard symbol in the middle of a keypath" do
|
104
|
+
obj = {
|
105
|
+
"errors" => {
|
106
|
+
"status" => 0,
|
107
|
+
"data" => "1234abcd"
|
108
|
+
},
|
109
|
+
"warnings" => {
|
110
|
+
"status" => 1,
|
111
|
+
"data" => "abcd1234"
|
112
|
+
}
|
113
|
+
}
|
114
|
+
exp = {
|
115
|
+
"errors" => {
|
116
|
+
"status" => 0
|
117
|
+
},
|
118
|
+
"warnings" => {
|
119
|
+
"status" => 1
|
120
|
+
}
|
121
|
+
}
|
122
|
+
expect(test(obj, ["*.status"])).to eq(exp)
|
123
|
+
end
|
81
124
|
end
|
82
125
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: xcskarel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.14.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Honza Dvorsky
|
@@ -196,6 +196,7 @@ files:
|
|
196
196
|
- lib/xcskarel/server.rb
|
197
197
|
- lib/xcskarel/version.rb
|
198
198
|
- lib/xcskarel/xcsfile.rb
|
199
|
+
- spec/application_spec.rb
|
199
200
|
- spec/default_spec.rb
|
200
201
|
- spec/filter_spec.rb
|
201
202
|
homepage: http://github.com/czechboy0/xcskarel
|
@@ -223,6 +224,7 @@ signing_key:
|
|
223
224
|
specification_version: 4
|
224
225
|
summary: Manage your Xcode Server & Bots from the command line
|
225
226
|
test_files:
|
227
|
+
- spec/application_spec.rb
|
226
228
|
- spec/default_spec.rb
|
227
229
|
- spec/filter_spec.rb
|
228
230
|
has_rdoc:
|