Skip to content

Commit e091eb0

Browse files
author
Honza Dvorsky
committed
added xcsconfig commands - store bot configurations in git
1 parent 70d0cb6 commit e091eb0

14 files changed

+331
-528
lines changed

bin/xcskarel

+49
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,51 @@ class XCSKarelApplication
3333
global_option('--no_pretty', 'Disables output JSON prettification')
3434
global_option('--no_filter', 'Prints full JSON payload for objects instead of just filtering the important ones')
3535

36+
# Managing local xcsconfig folder
37+
38+
command :'config list' do |c|
39+
c.syntax = 'xcskarel config [options]'
40+
c.description = 'Lists the Xcode Bot configurations found in this folder'
41+
c.action do |args, options|
42+
config_folder = XCSKarel::XCSFile.get_config_folder
43+
return unless config_folder
44+
configs = XCSKarel::XCSFile.load_configs(config_folder)
45+
if configs.count == 0
46+
XCSKarel.log.info "Found no existing configs in #{config_folder}".yellow
47+
else
48+
out = "\n" + configs.map { |c| "#{File.basename(c.path)}".yellow + " - from " + "\"#{c.name}\"".yellow + ", created at #{c.created}" }.join("\n")
49+
XCSKarel.log.info "Found #{configs.count} configs in \"#{config_folder}\":"
50+
XCSKarel.log.info out
51+
end
52+
end
53+
end
54+
55+
command :'config new' do |c|
56+
c.syntax = 'xcskarel config new [options]'
57+
c.description = 'Starts the interactive process of creating a new config from an existing Bot'
58+
add_xcs_options(c)
59+
c.action do |args, options|
60+
61+
# let user chose a bot from the server
62+
server = create_server_from_options(options)
63+
bot = XCSKarel::Application.choose_bot(server)
64+
65+
# get our config folder
66+
config_folder = XCSKarel::XCSFile.get_config_folder
67+
return unless config_folder
68+
69+
# dump the bot into that config folder under a random name
70+
new_config_path = XCSKarel::XCSFile.new_config_name(config_folder)
71+
new_config = XCSKarel::Config.new(bot, server.api_version, new_config_path)
72+
new_config.save
73+
74+
XCSKarel.log.info "Saved Bot \"#{new_config.name}\" configuration to #{new_config_path}.".green
75+
system "open \"#{new_config_path}\""
76+
end
77+
end
78+
79+
# Talking to Xcode Server API
80+
3681
command :bots do |c|
3782
c.syntax = 'xcskarel bots [options]'
3883
c.description = 'Fetches all Bots found on the specified server'
@@ -87,6 +132,8 @@ class XCSKarelApplication
87132
end
88133
end
89134

135+
# Managing a local Xcode Server
136+
90137
command :'server start' do |c|
91138
c.syntax = 'xcskarel server start [options]'
92139
c.description = 'Start local Xcode Server'
@@ -137,6 +184,8 @@ class XCSKarelApplication
137184
end
138185
end
139186

187+
# Managing Xcode
188+
140189
command :'xcode select' do |c|
141190
c.syntax = 'xcskarel xcode select'
142191
c.description = 'Interactive xcode-select'

lib/xcskarel.rb

+3-1
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,6 @@
44
require 'xcskarel/log'
55
require 'xcskarel/filter'
66
require 'xcskarel/control'
7-
require 'xcskarel/bot'
7+
require 'xcskarel/config'
8+
require 'xcskarel/xcsfile'
9+
require 'xcskarel/application'

lib/xcskarel/application.rb

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
module XCSKarel
2+
module Application
3+
def self.choose_bot(server)
4+
all_bots = server.get_bots
5+
bot_names = all_bots.map { |json| "#{json['name']} (#{json['_id']})" }
6+
puts "Which Bot should be used as a template?"
7+
choice = choose(*bot_names)
8+
bot = all_bots[bot_names.index(choice)]
9+
XCSKarel.log.info "Chose Bot \"#{bot['name']}\""
10+
return bot
11+
end
12+
end
13+
end

lib/xcskarel/bot.rb

-23
This file was deleted.

lib/xcskarel/config.rb

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
module XCSKarel
2+
class Config
3+
attr_reader :json
4+
attr_reader :path
5+
attr_reader :api_version
6+
7+
def initialize(json, api_version, path)
8+
@json = json
9+
@path = path
10+
@api_version = api_version || (config_json ? config_json['api_version'] : nil)
11+
end
12+
13+
def name
14+
@json['name'] || config_json['original_bot_name']
15+
end
16+
17+
def created
18+
config_json['time_generated']
19+
end
20+
21+
def config_json
22+
@json['xcsconfig']
23+
end
24+
25+
def key_paths_for_persistance
26+
key_paths_for_xcode_server << "xcsconfig"
27+
end
28+
29+
def key_paths_for_xcode_server
30+
["configuration"]
31+
end
32+
33+
def format_version
34+
1
35+
end
36+
37+
def json_for_persistence
38+
filtered = XCSKarel::Filter.filter_key_paths(@json, key_paths_for_persistance)
39+
40+
# also add xcsconfig metadata
41+
unless filtered["xcsconfig"]
42+
filtered["xcsconfig"] = {
43+
format_version: format_version,
44+
app_version: XCSKarel::VERSION,
45+
time_generated: Time.new.to_s,
46+
original_bot_name: @json['name'],
47+
api_version: @api_version
48+
}
49+
end
50+
return filtered
51+
end
52+
53+
def json_for_xcode_server
54+
XCSKarel::Filter.filter_key_paths(@json, key_paths_for_xcode_server)
55+
end
56+
57+
def self.from_file(file_path)
58+
abs_path = File.absolute_path(file_path)
59+
raise "No file #{abs_path}" unless File.exist?(abs_path)
60+
config = self.new(JSON.parse(File.read(abs_path)), nil, abs_path)
61+
unless config.validate_loaded
62+
XCSKarel.log.warn "Skipping invalid config #{abs_path}".yellow
63+
return nil
64+
end
65+
return config
66+
end
67+
68+
def validate_loaded
69+
return false unless config_json
70+
return true
71+
end
72+
73+
def to_file(file_path)
74+
abs_path = File.absolute_path(file_path)
75+
raise "File #{abs_path} already exists." if File.exist?(abs_path)
76+
FileUtils.mkdir_p(File.dirname(abs_path))
77+
File.open(abs_path, 'w') do |f|
78+
f.puts JSON.pretty_generate(json_for_persistence) + "\n"
79+
end
80+
end
81+
82+
def save
83+
to_file(@path)
84+
end
85+
end
86+
end

lib/xcskarel/filter.rb

+3-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@ def self.filter_key_paths(object, key_paths)
2424
key.split('.').first == keys.first
2525
end.first
2626
if match
27-
new_hash[keys.first] = filter_key_paths(v, match.split('.').drop(1))
27+
child_key_paths = match.split('.').drop(1)
28+
# 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)
2830
end
2931
end
3032
return new_hash

lib/xcskarel/server.rb

+3-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ class Server
1111
attr_reader :user
1212
attr_reader :pass
1313
attr_reader :port
14+
attr_reader :api_version
1415

1516
def initialize(host, user=nil, pass=nil, allow_self_signed=true)
1617
@port = 20343
@@ -88,7 +89,8 @@ def validate_connection(allow_self_signed)
8889
raise "Failed to validate - #{e}.\nPlease make sure your Xcode Server is up and running at #{host}. Run `xcskarel server start` to start a new local Xcode Server instance.".red
8990
else
9091
raise "Failed to validate - Endpoint at \"#{url}\" responded with #{response.data[:status_line]}".red if response.status != 204
91-
XCSKarel.log.debug "Validation of host #{@host} succeeded.".green
92+
@api_version = response.headers['X-XCSAPIVersion'].to_s
93+
XCSKarel.log.debug "Validation of host #{@host} (API version #{@api_version}) succeeded.".green
9294
end
9395
end
9496
end

lib/xcskarel/xcsfile.rb

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
module XCSKarel
2+
module XCSFile
3+
def self.folder_name
4+
"xcsconfig"
5+
end
6+
7+
def self.file_name
8+
"xcsfile.json"
9+
end
10+
11+
def self.find_config_folder_in_current_folder
12+
self.find_config_folder_in_folder(Dir.pwd)
13+
end
14+
15+
def self.find_config_folder_in_folder(folder)
16+
# look for the xcsconfig folder with an xcsfile inside
17+
abs_folder = File.absolute_path(folder)
18+
found_folder = Dir[File.join(abs_folder, "/", "*")].select do |f|
19+
File.basename(f) == self.folder_name
20+
end.first
21+
return found_folder
22+
end
23+
24+
def self.create_config_folder_in_current_folder
25+
self.create_config_folder_in_folder(Dir.pwd)
26+
end
27+
28+
def self.create_config_folder_in_folder(folder)
29+
abs_folder = File.absolute_path(folder)
30+
config_folder = File.join(abs_folder, self.folder_name)
31+
FileUtils.mkdir_p(config_folder)
32+
return config_folder
33+
end
34+
35+
def self.get_config_folder
36+
config_folder = XCSKarel::XCSFile.find_config_folder_in_current_folder
37+
unless config_folder
38+
should_create = agree("There is no xcsconfig folder found, should I create one for you? (y/n)".red)
39+
if should_create
40+
config_folder = XCSKarel::XCSFile.create_config_folder_in_current_folder unless config_folder
41+
XCSKarel.log.debug "Folder #{config_folder} created".yellow
42+
else
43+
return nil
44+
end
45+
end
46+
# we have a config folder
47+
XCSKarel.log.debug "Config folder found: #{config_folder}".green
48+
return config_folder
49+
end
50+
51+
def self.load_configs(folder)
52+
require 'json'
53+
Dir[File.join(folder, "/", "botconfig_*.json")].map do |f|
54+
XCSKarel::Config.from_file(f)
55+
end.select do |c|
56+
c != nil
57+
end
58+
end
59+
60+
def self.random_name
61+
require 'securerandom'
62+
"botconfig_#{SecureRandom.hex(6)}.json"
63+
end
64+
65+
def self.new_config_name(folder)
66+
File.join(folder, self.random_name)
67+
end
68+
69+
end
70+
end

spec/filter_spec.rb

+19
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,25 @@ def test(inObj, key_paths)
4343
expect(test(obj, ["oranges.old"])).to eq(exp)
4444
end
4545

46+
it "keeps full values when hashes and key is already whitelisted" do
47+
obj = {
48+
"apples" => "green",
49+
"blackberries" => 12,
50+
"oranges" => {
51+
"new" => 2,
52+
"old" => -2
53+
}
54+
}
55+
exp = {
56+
"apples" => "green",
57+
"oranges" => {
58+
"new" => 2,
59+
"old" => -2
60+
}
61+
}
62+
expect(test(obj, ["oranges", "apples"])).to eq(exp)
63+
end
64+
4665
it "handles basic key path with an array without popping the key path" do
4766
obj = [
4867
"apples",

0 commit comments

Comments
 (0)