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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cf58b04b0c57a70a57293e17fe3bb66bde17a5f8
4
- data.tar.gz: afb87c39726475e9e85b1c04d74bef54adb7a104
3
+ metadata.gz: f69158b3f6c2220edec0c74f13e59c1dc1785ae5
4
+ data.tar.gz: 2584d85e540450a8c65e52ea9f2987a23b93e8e3
5
5
  SHA512:
6
- metadata.gz: 8f8aaa7c01000090168ef4f12445395135508928271ae0db44bd9a8f57a7e9c13b7fcf6a3b633e4189948044ae25a60b559ebaef8785a3e0779d90d2a6709b39
7
- data.tar.gz: 9e0b9728393772d7ecef24d97162e31c10f023819fa3176fc7c9c16b7eb2b93b79ad12dee1e43630e0febbbbd5bbcfbd7477e38798e6b08c1413585f71beb7ed
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', '(required) Xcode Server\'s hostname or IP address (default: localhost)'
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 BOT_ID', '(required) Bot identifier'
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
- XCSKarel::Server.new(host, options.user, options.pass)
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
- # create Bot instances
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 = server.get_integrations(options.bot)
116
- unless options.no_filter
117
- all_integrations = XCSKarel::Filter.filter_key_paths(all_integrations, ['number', '_id', 'currentStep', 'result'])
118
- end
119
- out = options.no_pretty ? JSON.generate(all_integrations) : JSON.pretty_generate(all_integrations)
120
- puts out
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
- unless options.no_filter
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
 
@@ -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
@@ -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
- match = unique_key_paths.select do |key|
24
- key.split('.').first == keys.first
25
- end.first
26
- if match
27
- child_key_paths = match.split('.').drop(1)
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
- new_hash[keys.first] = child_key_paths.count == 0 ? v : filter_key_paths(v, child_key_paths)
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
@@ -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
- response = get_endpoint("/bots/#{bot_id}/integrations")
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
@@ -1,3 +1,3 @@
1
1
  module XCSKarel
2
- VERSION = '0.11.0'
2
+ VERSION = '0.14.0'
3
3
  end
@@ -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.11.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: