xcskarel 0.11.0 → 0.14.0
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 +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:
|