flow-cli 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/flow-cli.gemspec +7 -3
- data/lib/flow/cli/cmd_manager.rb +7 -2
- data/lib/flow/cli/commands/remote.rb +102 -0
- data/lib/flow/cli/constant.rb +1 -0
- data/lib/flow/cli/utils/all.rb +4 -0
- data/lib/flow/cli/utils/api/flow_api_manager.rb +106 -0
- data/lib/flow/cli/utils/db_manager.rb +45 -0
- data/lib/flow/cli/utils/flow_api_rest.rb +21 -0
- data/lib/flow/cli/utils/local_service_rest.rb +108 -0
- data/lib/flow/cli/vendors/hash.rb +110 -30
- data/lib/flow/cli/version.rb +1 -1
- data/lib/flow/cli.rb +2 -0
- metadata +45 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a2fa6031aa449ee4371cfa829a9935792127aa99
|
4
|
+
data.tar.gz: 4ae2ae1777e0a4b18f3b37e4534a561bcbf831d2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d0ecd916f4dbb0a6c459d8270f0137de90d41e6f171edaa1aa33c14c59b9d62e31e1b02ab568eb943c3de5818ec814286d5c198e9cd9e944b4d4c0b3b6c3e983
|
7
|
+
data.tar.gz: 5de248002899ab72f88af68ba13fda40a2ded7250ccfcc3bd7091d75ff15db68b31a56bf322e4b2532a62fbbfa434cedf1a67aa3a7acf83408b09add58b3a0d3
|
data/flow-cli.gemspec
CHANGED
@@ -32,6 +32,8 @@ Gem::Specification.new do |spec|
|
|
32
32
|
****************************************************
|
33
33
|
这是 flow.ci CLI 的早期版本,暂时只支持 ios 项目
|
34
34
|
|
35
|
+
0.0.3 版本新增 flow-cli remote 系列指令,支持传证书,传provisions 文件
|
36
|
+
|
35
37
|
)
|
36
38
|
|
37
39
|
spec.add_development_dependency "bundler", "~> 1.14"
|
@@ -41,7 +43,9 @@ Gem::Specification.new do |spec|
|
|
41
43
|
spec.add_development_dependency "rubocop"
|
42
44
|
spec.add_development_dependency "byebug"
|
43
45
|
|
44
|
-
spec.add_dependency "thor", "
|
45
|
-
spec.add_dependency "tty", "
|
46
|
-
spec.add_dependency "fastlane", "
|
46
|
+
spec.add_dependency "thor", "~> 0.18"
|
47
|
+
spec.add_dependency "tty", "~> 0.7"
|
48
|
+
spec.add_dependency "fastlane", "~> 2.28"
|
49
|
+
spec.add_dependency "oj", "~> 2"
|
50
|
+
spec.add_dependency "rest-client"
|
47
51
|
end
|
data/lib/flow/cli/cmd_manager.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'yaml'
|
2
2
|
require 'tty'
|
3
3
|
require 'thor'
|
4
|
+
require_relative './commands/remote'
|
4
5
|
|
5
6
|
module Flow::Cli
|
6
7
|
class CmdManager < Thor
|
@@ -10,12 +11,16 @@ module Flow::Cli
|
|
10
11
|
@pastel = Pastel.new
|
11
12
|
@error = @pastel.red.bold.detach
|
12
13
|
@warning = @pastel.yellow.detach
|
14
|
+
@db_manager = Utils::DbManager
|
15
|
+
@api_manager = Utils::FlowApiManager.load_from_db
|
13
16
|
end
|
14
17
|
|
18
|
+
desc "remote ...ARGS", "manage flow ci"
|
19
|
+
subcommand "remote", Commands::Remote
|
20
|
+
|
15
21
|
desc "build_yaml_file", "build flow ci project yaml"
|
16
22
|
def build_yaml_file
|
17
23
|
config = ProjectAnalytics.new.config
|
18
|
-
|
19
24
|
# 用来交互
|
20
25
|
# TODO: 优化点,以后放到其他地方
|
21
26
|
config[:gym_config] = ask_gym_build_options if config[:flow_language] == "objc" && ENV["FLOW_CLI_TEST"] != "TRUE"
|
@@ -43,7 +48,7 @@ module Flow::Cli
|
|
43
48
|
print_line
|
44
49
|
end
|
45
50
|
|
46
|
-
desc "version", "show flow cli version"
|
51
|
+
desc "version", "show flow cli version #{VERSION}"
|
47
52
|
map ['v', '-v', '--version'] => :version
|
48
53
|
def version
|
49
54
|
puts VERSION
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'tty'
|
3
|
+
require 'thor'
|
4
|
+
|
5
|
+
module Flow::Cli
|
6
|
+
module Commands
|
7
|
+
class Remote < Thor
|
8
|
+
def initialize(*args)
|
9
|
+
super(*args)
|
10
|
+
@prompt = TTY::Prompt.new
|
11
|
+
@pastel = Pastel.new
|
12
|
+
@error = @pastel.red.bold.detach
|
13
|
+
@warning = @pastel.yellow.detach
|
14
|
+
@db_manager = Utils::DbManager
|
15
|
+
@api_manager = Utils::FlowApiManager.load_from_db
|
16
|
+
end
|
17
|
+
|
18
|
+
desc "login", "bind flow ci account to flow cli."
|
19
|
+
def login
|
20
|
+
email = @prompt.ask("email?")
|
21
|
+
password = @prompt.mask("password?")
|
22
|
+
Utils::FlowApiManager.login(email, password)
|
23
|
+
puts "login success"
|
24
|
+
end
|
25
|
+
|
26
|
+
desc "project_init", "set a project from flow ci to operation"
|
27
|
+
def project_init
|
28
|
+
projects = @api_manager.fetch_projects
|
29
|
+
begin
|
30
|
+
file_origin = `git remote -v`.to_s.match("git.*.git").first
|
31
|
+
rescue
|
32
|
+
puts @warning.call "read git origin fail..."
|
33
|
+
end
|
34
|
+
|
35
|
+
dict = {}
|
36
|
+
dict = Hash[projects.map { |p| [p[:name].to_s, p[:id]] }]
|
37
|
+
|
38
|
+
current_project_id = @prompt.select("Choose your project?", dict)
|
39
|
+
|
40
|
+
@db_manager.save_attribute(:current_project_id, current_project_id)
|
41
|
+
|
42
|
+
flows = @api_manager.fetch_flows(current_project_id)
|
43
|
+
|
44
|
+
current_flow_id = if flows.count == 1
|
45
|
+
flows.first[:id]
|
46
|
+
else
|
47
|
+
dict = {}
|
48
|
+
flows.each { |p| dict[(p[:name]).to_s] = p[:id] }
|
49
|
+
@prompt.select("Choose your flow?", dict)
|
50
|
+
end
|
51
|
+
@db_manager.save_attribute(:current_flow_id, current_flow_id)
|
52
|
+
puts "project_id = #{current_project_id}, flow_id = #{current_flow_id}. saved this info..."
|
53
|
+
end
|
54
|
+
|
55
|
+
desc "upload_p12 FILE_PATH [p12 password]", "upload_p12"
|
56
|
+
def upload_p12(file_path, password = nil)
|
57
|
+
basename = File.basename file_path
|
58
|
+
project_init unless @db_manager.read_attribute(:current_flow_id)
|
59
|
+
|
60
|
+
api_p12s = @api_manager.load_p12s(@db_manager.read_attribute(:current_flow_id))
|
61
|
+
old_p12 = api_p12s.find { |p12| p12[:filename] == basename }
|
62
|
+
unless old_p12.nil?
|
63
|
+
if @prompt.yes? "found a same name file, override?"
|
64
|
+
@api_manager.delete_p12(old_p12[:id], @db_manager.read_attribute(:current_flow_id))
|
65
|
+
else
|
66
|
+
return puts "canceled.."
|
67
|
+
end
|
68
|
+
end
|
69
|
+
@api_manager.upload_p12(@db_manager.read_attribute(:current_flow_id), file_path, password)
|
70
|
+
puts "uploaded."
|
71
|
+
end
|
72
|
+
|
73
|
+
desc "list_p12s", "list_p12s"
|
74
|
+
def list_p12s
|
75
|
+
puts @api_manager.load_p12s(@db_manager.read_attribute(:current_flow_id))
|
76
|
+
end
|
77
|
+
|
78
|
+
desc "upload_provision", "upload_provision"
|
79
|
+
def upload_provision(file_path)
|
80
|
+
basename = File.basename file_path
|
81
|
+
project_init unless @db_manager.read_attribute(:current_flow_id)
|
82
|
+
|
83
|
+
api_provisions = @api_manager.load_provisions(@db_manager.read_attribute(:current_flow_id))
|
84
|
+
old_provision = api_provisions.find { |provision| provision[:filename] == basename }
|
85
|
+
unless old_provision.nil?
|
86
|
+
if @prompt.yes? "found a same name file, override?"
|
87
|
+
@api_manager.delete_provision(old_provision[:id], @db_manager.read_attribute(:current_flow_id))
|
88
|
+
else
|
89
|
+
return puts "canceled.."
|
90
|
+
end
|
91
|
+
end
|
92
|
+
@api_manager.upload_provision(@db_manager.read_attribute(:current_flow_id), file_path)
|
93
|
+
puts "uploaded."
|
94
|
+
end
|
95
|
+
|
96
|
+
desc "list_provisions", "list provisions"
|
97
|
+
def list_provisions
|
98
|
+
puts @api_manager.load_provisions(@db_manager.read_attribute(:current_flow_id))
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
data/lib/flow/cli/constant.rb
CHANGED
@@ -0,0 +1,106 @@
|
|
1
|
+
require_relative "../flow_api_rest"
|
2
|
+
module Flow::Cli
|
3
|
+
module Utils
|
4
|
+
class FlowApiManager
|
5
|
+
attr_accessor :email, :password, :user_access_token, :current_org_id,
|
6
|
+
:current_project_id, :current_flow_id,
|
7
|
+
:current_project_name
|
8
|
+
|
9
|
+
def initialize(hash = {})
|
10
|
+
%i[email password user_access_token].each do |item|
|
11
|
+
send "#{item}=", hash[item.to_s]
|
12
|
+
end
|
13
|
+
yield self if block_given?
|
14
|
+
init_access_token if user_access_token.nil?
|
15
|
+
end
|
16
|
+
|
17
|
+
def fetch_orgs
|
18
|
+
raw_orgs = FlowApiRest.get("/orgs", access_token: user_access_token)
|
19
|
+
raw_orgs.map do |org|
|
20
|
+
org.slice(:id, :name)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def fetch_projects(specify_org_id = nil)
|
25
|
+
org_id = specify_org_id || current_org_id
|
26
|
+
send_to_api(:get, "/projects", { org_id: org_id }, %i[id name git_url source])
|
27
|
+
end
|
28
|
+
|
29
|
+
def fetch_project(project_id)
|
30
|
+
send_to_api(:get, "/projects/#{project_id}")
|
31
|
+
end
|
32
|
+
|
33
|
+
def fetch_flows(project_id)
|
34
|
+
send_to_api(:get, "/projects/#{project_id}/flows")
|
35
|
+
end
|
36
|
+
|
37
|
+
# 5909e8c4ef2cb07bcefc3dbd
|
38
|
+
def upload_p12(flow_id, file, password = nil)
|
39
|
+
send_to_api(:post, "/flows/#{flow_id}/certificates",
|
40
|
+
file: standard_file(file),
|
41
|
+
type: "ios",
|
42
|
+
password: password)
|
43
|
+
end
|
44
|
+
|
45
|
+
def load_p12s(flow_id)
|
46
|
+
send_to_api(:get, "/flows/#{flow_id}/certificates")
|
47
|
+
end
|
48
|
+
|
49
|
+
def delete_p12(p12_id, flow_id)
|
50
|
+
send_to_api(:delete, "/certificates/#{p12_id}", flow_id: flow_id)
|
51
|
+
end
|
52
|
+
|
53
|
+
def upload_provision(flow_id, file)
|
54
|
+
send_to_api(:post, "/flows/#{flow_id}/mobileprovisions",
|
55
|
+
file: standard_file(file),
|
56
|
+
flow_id: flow_id)
|
57
|
+
end
|
58
|
+
|
59
|
+
def load_provisions(flow_id)
|
60
|
+
send_to_api(:get, "/flows/#{flow_id}/mobileprovisions")
|
61
|
+
end
|
62
|
+
|
63
|
+
def delete_provision(mobileprovisions_id,flow_id)
|
64
|
+
send_to_api(:delete, "/mobileprovisions/#{mobileprovisions_id}", flow_id: flow_id)
|
65
|
+
end
|
66
|
+
|
67
|
+
def fetch_flow(flow_id, project_id)
|
68
|
+
send_to_api(:get, "/flows/#{flow_id}", project_id: project_id)
|
69
|
+
end
|
70
|
+
|
71
|
+
def send_to_api(action, url, params = {}, slice_items = nil, need_access_token = true)
|
72
|
+
params[:access_token] = user_access_token if need_access_token
|
73
|
+
params.compact!
|
74
|
+
raw_answer = FlowApiRest.send(action, url, params)
|
75
|
+
|
76
|
+
return raw_answer if slice_items.nil?
|
77
|
+
raise "slice need be a array with symbols" unless slice_items.is_a? Array
|
78
|
+
|
79
|
+
return raw_answer.map { |item| item.slice(*slice_items) } if raw_answer.is_a? Array
|
80
|
+
raw_answer.slice(*slice_items)
|
81
|
+
end
|
82
|
+
|
83
|
+
def init_access_token
|
84
|
+
answer = self.class.login(email, password)
|
85
|
+
self.user_access_token = answer[:access_token]
|
86
|
+
end
|
87
|
+
|
88
|
+
def standard_file(file)
|
89
|
+
return File.open(file) if file.is_a?(String)
|
90
|
+
file
|
91
|
+
end
|
92
|
+
|
93
|
+
class << self
|
94
|
+
def login(email, password)
|
95
|
+
dict = FlowApiRest.post("/login", login: email, password: password)
|
96
|
+
DbManager.save(email: email, password: password)
|
97
|
+
dict
|
98
|
+
end
|
99
|
+
|
100
|
+
def load_from_db
|
101
|
+
new(DbManager.read)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module Flow::Cli
|
4
|
+
module Utils
|
5
|
+
class DbManager
|
6
|
+
class << self
|
7
|
+
FLOW_CLI_CONFIG = "#{ENV['HOME']}/.flow_cli_config.yml".freeze
|
8
|
+
|
9
|
+
def overide_save(hash)
|
10
|
+
File.open(FLOW_CLI_CONFIG, "w") do |file|
|
11
|
+
file << hash.to_yaml
|
12
|
+
end
|
13
|
+
hash
|
14
|
+
end
|
15
|
+
|
16
|
+
def save(settings)
|
17
|
+
old = read
|
18
|
+
settings = old.merge(settings).compact.stringify_keys
|
19
|
+
yaml = settings.to_yaml
|
20
|
+
File.open(FLOW_CLI_CONFIG, "w") do |file|
|
21
|
+
file << yaml
|
22
|
+
end
|
23
|
+
settings
|
24
|
+
end
|
25
|
+
|
26
|
+
def save_attribute(key, val)
|
27
|
+
dict = read
|
28
|
+
dict[key.to_s] = val
|
29
|
+
save(dict)
|
30
|
+
end
|
31
|
+
|
32
|
+
def read
|
33
|
+
return {} unless File.file?(FLOW_CLI_CONFIG)
|
34
|
+
config = YAML.safe_load(File.read(FLOW_CLI_CONFIG))
|
35
|
+
raise "yaml load is not a hash #{config.class}" unless config.is_a? Hash
|
36
|
+
config
|
37
|
+
end
|
38
|
+
|
39
|
+
def read_attribute(key)
|
40
|
+
read[key.to_s]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require_relative './local_service_rest'
|
2
|
+
module Flow::Cli
|
3
|
+
module Utils
|
4
|
+
class FlowApiRest < LocalServiceRest
|
5
|
+
class << self
|
6
|
+
def basic_url
|
7
|
+
FLOW_API_URL # 子类中复写
|
8
|
+
end
|
9
|
+
|
10
|
+
%i[get delete head post patch put].each do |method|
|
11
|
+
alias_method "#{method}_old", method
|
12
|
+
define_method method do |*args, &blk|
|
13
|
+
ret = __send__ "#{method}_old", *args, &blk
|
14
|
+
raise "response_body = #{ret[:response_body]}" if ret.is_a?(Hash) && ret[:status] == false
|
15
|
+
ret
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'oj'
|
2
|
+
require 'json'
|
3
|
+
require 'rest-client'
|
4
|
+
|
5
|
+
module Flow::Cli
|
6
|
+
module Utils
|
7
|
+
class LocalServiceRest
|
8
|
+
class Error < StandardError; end
|
9
|
+
class JSONParseError < Error; end
|
10
|
+
class << self
|
11
|
+
%w[get delete head].each do |word|
|
12
|
+
define_method word.to_sym do |url, params = {}, timeout = 10, retry_times = 5|
|
13
|
+
params.merge!(add_those_to_params) # 将默认参数添加到params 里
|
14
|
+
url = basic_url + url
|
15
|
+
exception = nil
|
16
|
+
retry_times.times do
|
17
|
+
begin
|
18
|
+
headers = json_content_type
|
19
|
+
headers[:params] = if headers[:params]
|
20
|
+
headers[:params].merge(params)
|
21
|
+
else
|
22
|
+
params
|
23
|
+
end
|
24
|
+
response = ::RestClient::Request.execute(
|
25
|
+
method: word,
|
26
|
+
url: url,
|
27
|
+
headers: headers,
|
28
|
+
timeout: timeout
|
29
|
+
)
|
30
|
+
return ::Oj.load(response.body, symbol_keys: true)
|
31
|
+
rescue ::Oj::Error
|
32
|
+
raise ::LocalServiceRest::JSONParseError('返回的内容JSON解析失败')
|
33
|
+
rescue ::RestClient::ExceptionWithResponse => ex # 有返回的错误
|
34
|
+
raise ex unless ex.http_code.to_i < 500 && ex.http_code.to_i >= 300 # 这里主要是服务器返回了500 了,没必要解析body
|
35
|
+
return { # 这里是服务器有返回的错误,都应该是json 格式的
|
36
|
+
status: false,
|
37
|
+
json: ::Oj.load(ex.response.body, symbol_keys: true),
|
38
|
+
response_body: ex.response.body,
|
39
|
+
message: ex.message
|
40
|
+
}
|
41
|
+
rescue RestClient::Exception => e
|
42
|
+
exception = e
|
43
|
+
next
|
44
|
+
end
|
45
|
+
end
|
46
|
+
raise exception
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
%w[post patch put].each do |word|
|
51
|
+
define_method word.to_sym do |url, payload, timeout = 5, retry_times = 5|
|
52
|
+
payload = add_something_to_payload(payload)
|
53
|
+
url = build_valid_url(url)
|
54
|
+
exception = nil
|
55
|
+
retry_times.times do
|
56
|
+
begin
|
57
|
+
request = ::RestClient::Request.execute(
|
58
|
+
method: word,
|
59
|
+
url: url,
|
60
|
+
payload: payload,
|
61
|
+
headers: json_content_type,
|
62
|
+
timeout: timeout
|
63
|
+
)
|
64
|
+
return ::Oj.load(request.body, symbol_keys: true)
|
65
|
+
rescue ::Oj::Error
|
66
|
+
raise ::LocalServiceRest::JSONParseError('返回的内容JSON解析失败')
|
67
|
+
rescue ::RestClient::ExceptionWithResponse => ex # 有返回的错误
|
68
|
+
raise ex unless ex.http_code < 500 && ex.http_code >= 300
|
69
|
+
return {
|
70
|
+
status: false,
|
71
|
+
json: ::Oj.load(ex.response.body, symbol_keys: true),
|
72
|
+
response_body: ex.response.body,
|
73
|
+
message: ex.message
|
74
|
+
}
|
75
|
+
rescue RestClient::Exception => e
|
76
|
+
exception = e
|
77
|
+
next
|
78
|
+
end
|
79
|
+
end
|
80
|
+
raise exception
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def build_valid_url(url)
|
85
|
+
add_params = URI.escape(add_those_to_params.collect { |k, v| "#{k}=#{v}" }.join('&'))
|
86
|
+
tmp = url.include?('?') ? '&' : '?'
|
87
|
+
basic_url + url + tmp + add_params
|
88
|
+
end
|
89
|
+
|
90
|
+
def add_something_to_payload(payload)
|
91
|
+
payload.merge(add_those_to_params)
|
92
|
+
end
|
93
|
+
|
94
|
+
def json_content_type
|
95
|
+
{ accept: :json }
|
96
|
+
end
|
97
|
+
|
98
|
+
def basic_url
|
99
|
+
'http://www.example.com' # 子类中复写
|
100
|
+
end
|
101
|
+
|
102
|
+
def add_those_to_params
|
103
|
+
{} # 子类中复写
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -1,4 +1,59 @@
|
|
1
1
|
class Hash
|
2
|
+
# Slice a hash to include only the given keys. Returns a hash containing
|
3
|
+
# the given keys.
|
4
|
+
#
|
5
|
+
# { a: 1, b: 2, c: 3, d: 4 }.slice(:a, :b)
|
6
|
+
# # => {:a=>1, :b=>2}
|
7
|
+
#
|
8
|
+
# This is useful for limiting an options hash to valid keys before
|
9
|
+
# passing to a method:
|
10
|
+
#
|
11
|
+
# def search(criteria = {})
|
12
|
+
# criteria.assert_valid_keys(:mass, :velocity, :time)
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# search(options.slice(:mass, :velocity, :time))
|
16
|
+
#
|
17
|
+
# If you have an array of keys you want to limit to, you should splat them:
|
18
|
+
#
|
19
|
+
# valid_keys = [:mass, :velocity, :time]
|
20
|
+
# search(options.slice(*valid_keys))
|
21
|
+
def slice(*keys)
|
22
|
+
keys.map! { |key| convert_key(key) } if respond_to?(:convert_key, true)
|
23
|
+
keys.each_with_object(self.class.new) { |k, hash| hash[k] = self[k] if key?(k) }
|
24
|
+
end
|
25
|
+
|
26
|
+
# Replaces the hash with only the given keys.
|
27
|
+
# Returns a hash containing the removed key/value pairs.
|
28
|
+
#
|
29
|
+
# { a: 1, b: 2, c: 3, d: 4 }.slice!(:a, :b)
|
30
|
+
# # => {:c=>3, :d=>4}
|
31
|
+
def slice!(*keys)
|
32
|
+
keys.map! { |key| convert_key(key) } if respond_to?(:convert_key, true)
|
33
|
+
omit = slice(*self.keys - keys)
|
34
|
+
hash = slice(*keys)
|
35
|
+
hash.default = default
|
36
|
+
hash.default_proc = default_proc if default_proc
|
37
|
+
replace(hash)
|
38
|
+
omit
|
39
|
+
end
|
40
|
+
|
41
|
+
# Removes and returns the key/value pairs matching the given keys.
|
42
|
+
#
|
43
|
+
# { a: 1, b: 2, c: 3, d: 4 }.extract!(:a, :b) # => {:a=>1, :b=>2}
|
44
|
+
# { a: 1, b: 2 }.extract!(:a, :x) # => {:a=>1}
|
45
|
+
def extract!(*keys)
|
46
|
+
keys.each_with_object(self.class.new) { |key, result| result[key] = delete(key) if key?(key) }
|
47
|
+
end
|
48
|
+
|
49
|
+
def compact
|
50
|
+
reject { |_, value| value.nil? }
|
51
|
+
end
|
52
|
+
|
53
|
+
def compact!
|
54
|
+
reject! { |_, value| value.nil? }
|
55
|
+
end
|
56
|
+
|
2
57
|
# Returns a new hash with all keys converted using the +block+ operation.
|
3
58
|
#
|
4
59
|
# hash = { name: 'Rob', age: '28' }
|
@@ -52,16 +107,28 @@ class Hash
|
|
52
107
|
# hash.symbolize_keys
|
53
108
|
# # => {:name=>"Rob", :age=>"28"}
|
54
109
|
def symbolize_keys
|
55
|
-
transform_keys
|
110
|
+
transform_keys do |key|
|
111
|
+
begin
|
112
|
+
key.to_sym
|
113
|
+
rescue
|
114
|
+
key
|
115
|
+
end
|
116
|
+
end
|
56
117
|
end
|
57
|
-
|
118
|
+
alias to_options symbolize_keys
|
58
119
|
|
59
120
|
# Destructively converts all keys to symbols, as long as they respond
|
60
121
|
# to +to_sym+. Same as +symbolize_keys+, but modifies +self+.
|
61
122
|
def symbolize_keys!
|
62
|
-
transform_keys!
|
123
|
+
transform_keys! do |key|
|
124
|
+
begin
|
125
|
+
key.to_sym
|
126
|
+
rescue
|
127
|
+
key
|
128
|
+
end
|
129
|
+
end
|
63
130
|
end
|
64
|
-
|
131
|
+
alias to_options! symbolize_keys!
|
65
132
|
|
66
133
|
# Validates all keys in a hash match <tt>*valid_keys</tt>, raising
|
67
134
|
# +ArgumentError+ on a mismatch.
|
@@ -76,7 +143,7 @@ class Hash
|
|
76
143
|
valid_keys.flatten!
|
77
144
|
each_key do |k|
|
78
145
|
unless valid_keys.include?(k)
|
79
|
-
raise ArgumentError
|
146
|
+
raise ArgumentError, "Unknown key: #{k.inspect}. Valid keys are: #{valid_keys.map(&:inspect).join(', ')}"
|
80
147
|
end
|
81
148
|
end
|
82
149
|
end
|
@@ -128,43 +195,56 @@ class Hash
|
|
128
195
|
# hash.deep_symbolize_keys
|
129
196
|
# # => {:person=>{:name=>"Rob", :age=>"28"}}
|
130
197
|
def deep_symbolize_keys
|
131
|
-
deep_transform_keys
|
198
|
+
deep_transform_keys do |key|
|
199
|
+
begin
|
200
|
+
key.to_sym
|
201
|
+
rescue
|
202
|
+
key
|
203
|
+
end
|
204
|
+
end
|
132
205
|
end
|
133
206
|
|
134
207
|
# Destructively converts all keys to symbols, as long as they respond
|
135
208
|
# to +to_sym+. This includes the keys from the root hash and from all
|
136
209
|
# nested hashes and arrays.
|
137
210
|
def deep_symbolize_keys!
|
138
|
-
deep_transform_keys!
|
211
|
+
deep_transform_keys! do |key|
|
212
|
+
begin
|
213
|
+
key.to_sym
|
214
|
+
rescue
|
215
|
+
key
|
216
|
+
end
|
217
|
+
end
|
139
218
|
end
|
140
219
|
|
141
220
|
private
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
when Array
|
150
|
-
object.map { |e| _deep_transform_keys_in_object(e, &block) }
|
151
|
-
else
|
152
|
-
object
|
221
|
+
|
222
|
+
# support methods for deep transforming nested hashes and arrays
|
223
|
+
def _deep_transform_keys_in_object(object, &block)
|
224
|
+
case object
|
225
|
+
when Hash
|
226
|
+
object.each_with_object({}) do |(key, value), result|
|
227
|
+
result[yield(key)] = _deep_transform_keys_in_object(value, &block)
|
153
228
|
end
|
229
|
+
when Array
|
230
|
+
object.map { |e| _deep_transform_keys_in_object(e, &block) }
|
231
|
+
else
|
232
|
+
object
|
154
233
|
end
|
234
|
+
end
|
155
235
|
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
end
|
163
|
-
object
|
164
|
-
when Array
|
165
|
-
object.map! { |e| _deep_transform_keys_in_object!(e, &block) }
|
166
|
-
else
|
167
|
-
object
|
236
|
+
def _deep_transform_keys_in_object!(object, &block)
|
237
|
+
case object
|
238
|
+
when Hash
|
239
|
+
object.keys.each do |key|
|
240
|
+
value = object.delete(key)
|
241
|
+
object[yield(key)] = _deep_transform_keys_in_object!(value, &block)
|
168
242
|
end
|
243
|
+
object
|
244
|
+
when Array
|
245
|
+
object.map! { |e| _deep_transform_keys_in_object!(e, &block) }
|
246
|
+
else
|
247
|
+
object
|
169
248
|
end
|
249
|
+
end
|
170
250
|
end
|
data/lib/flow/cli/version.rb
CHANGED
data/lib/flow/cli.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
require_relative "./cli/version"
|
2
2
|
require_relative "./cli/constant"
|
3
3
|
require_relative "./cli/vendors/all"
|
4
|
+
require_relative "./cli/utils/all"
|
4
5
|
require_relative "./cli/exception"
|
6
|
+
|
5
7
|
require_relative "./cli/project_analytics"
|
6
8
|
require_relative "./cli/flow_yaml_builder"
|
7
9
|
require_relative "./cli/ios_build_step_generator"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: flow-cli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- atpking
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-05-
|
11
|
+
date: 2017-05-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -98,44 +98,72 @@ dependencies:
|
|
98
98
|
name: thor
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
|
-
- - "
|
101
|
+
- - "~>"
|
102
102
|
- !ruby/object:Gem::Version
|
103
|
-
version: '0.
|
103
|
+
version: '0.18'
|
104
104
|
type: :runtime
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
|
-
- - "
|
108
|
+
- - "~>"
|
109
109
|
- !ruby/object:Gem::Version
|
110
|
-
version: '0.
|
110
|
+
version: '0.18'
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
112
|
name: tty
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
114
114
|
requirements:
|
115
|
-
- - "
|
115
|
+
- - "~>"
|
116
116
|
- !ruby/object:Gem::Version
|
117
117
|
version: '0.7'
|
118
118
|
type: :runtime
|
119
119
|
prerelease: false
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
121
121
|
requirements:
|
122
|
-
- - "
|
122
|
+
- - "~>"
|
123
123
|
- !ruby/object:Gem::Version
|
124
124
|
version: '0.7'
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
126
|
name: fastlane
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
128
128
|
requirements:
|
129
|
-
- - "
|
129
|
+
- - "~>"
|
130
130
|
- !ruby/object:Gem::Version
|
131
131
|
version: '2.28'
|
132
132
|
type: :runtime
|
133
133
|
prerelease: false
|
134
134
|
version_requirements: !ruby/object:Gem::Requirement
|
135
135
|
requirements:
|
136
|
-
- - "
|
136
|
+
- - "~>"
|
137
137
|
- !ruby/object:Gem::Version
|
138
138
|
version: '2.28'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: oj
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - "~>"
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '2'
|
146
|
+
type: :runtime
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - "~>"
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '2'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: rest-client
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - ">="
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
type: :runtime
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
139
167
|
description: Flow CI CLI, used to build yaml, run ci yaml locally.
|
140
168
|
email:
|
141
169
|
- atpking@gmail.com
|
@@ -159,11 +187,17 @@ files:
|
|
159
187
|
- flow-cli.gemspec
|
160
188
|
- lib/flow/cli.rb
|
161
189
|
- lib/flow/cli/cmd_manager.rb
|
190
|
+
- lib/flow/cli/commands/remote.rb
|
162
191
|
- lib/flow/cli/constant.rb
|
163
192
|
- lib/flow/cli/exception.rb
|
164
193
|
- lib/flow/cli/flow_yaml_builder.rb
|
165
194
|
- lib/flow/cli/ios_build_step_generator.rb
|
166
195
|
- lib/flow/cli/project_analytics.rb
|
196
|
+
- lib/flow/cli/utils/all.rb
|
197
|
+
- lib/flow/cli/utils/api/flow_api_manager.rb
|
198
|
+
- lib/flow/cli/utils/db_manager.rb
|
199
|
+
- lib/flow/cli/utils/flow_api_rest.rb
|
200
|
+
- lib/flow/cli/utils/local_service_rest.rb
|
167
201
|
- lib/flow/cli/vendors/all.rb
|
168
202
|
- lib/flow/cli/vendors/hash.rb
|
169
203
|
- lib/flow/cli/version.rb
|
@@ -176,7 +210,7 @@ post_install_message: "\n _____ _ _____ ______ ___ ____ _ ___
|
|
176
210
|
\\ /\\ / / | | | | | | | | |\n | _| | |__| |_| |\\ V V /| |___ | | |
|
177
211
|
|___| |___ | |\n |_| |_____\\___/ \\_/\\_/(_)____|___| \\____|_____|___|\n\n
|
178
212
|
****************************************************\n 这是 flow.ci CLI 的早期版本,暂时只支持
|
179
|
-
ios 项目\n\n "
|
213
|
+
ios 项目\n\n0.0.3 版本新增 flow-cli remote 系列指令,支持传证书,传provisions 文件\n\n "
|
180
214
|
rdoc_options: []
|
181
215
|
require_paths:
|
182
216
|
- lib
|