flow-cli 0.0.2 → 0.0.3
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/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
|