fir-cli 0.2.0 → 0.2.1.a
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +2 -1
- data/README.md +89 -106
- data/fir-cli.gemspec +1 -0
- data/fir-cli.rb +5 -0
- data/lib/fir-cli/version.rb +1 -1
- data/lib/fir-cli-commands/00-login.rb +2 -2
- data/lib/fir-cli-commands/00-profile.rb +2 -2
- data/lib/fir-cli-commands/01-config.rb +2 -13
- data/lib/fir-cli-commands/100-resign_codesign.rb +54 -0
- data/lib/fir-cli-commands/{10-resign.rb → 100-resign_tapbeta.rb} +11 -11
- data/lib/fir-cli-commands/101-resign.rb +26 -0
- data/lib/fir-cli-commands/11-publish.rb +3 -3
- data/lib/fir-cli-commands/12-build_ipa.rb +14 -17
- data/lib/fir-cli.chk.rb +33 -0
- data/lib/fir-cli.core.rb +96 -0
- data/lib/{fir-cli.core.ext.rb → fir-cli.fir.rb} +16 -9
- data/lib/fir-cli.opt.rb +33 -0
- data/lib/fir-cli.output.rb +55 -0
- data/lib/fir-cli.utils.rb +105 -0
- data/lib/lagunitas.ext.rb +2 -2
- data/lib/lagunitas.patch.rb +2 -3
- metadata +26 -6
- data/lib/fir-cli.utils.ext.rb +0 -247
data/lib/fir-cli.chk.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
module Fir
|
3
|
+
class Cli
|
4
|
+
private
|
5
|
+
def _chk_login!(prompt = true)
|
6
|
+
if !_opt_token && prompt == true
|
7
|
+
token = _prompt_secret('请输入用户 token:')
|
8
|
+
@token = token if token.length > 0
|
9
|
+
end
|
10
|
+
if !@token
|
11
|
+
_puts_require_token
|
12
|
+
_exit
|
13
|
+
elsif !_user(@token)
|
14
|
+
_puts_invalid_token
|
15
|
+
_exit
|
16
|
+
end
|
17
|
+
end
|
18
|
+
def _chk_os!(os)
|
19
|
+
if _os != os
|
20
|
+
_puts "! #{Paint["该指令不支持在非 #{os} 系统执行", :red]}"
|
21
|
+
_exit
|
22
|
+
end
|
23
|
+
end
|
24
|
+
def _chk_opt!(*opts)
|
25
|
+
opts.each do |_opt|
|
26
|
+
if !_opt? _opt.to_sym
|
27
|
+
_puts "! #{Paint["缺少参数 #{_opt}", :red]}"
|
28
|
+
_exit
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/fir-cli.core.rb
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
module Fir
|
3
|
+
class Cli
|
4
|
+
def initialize(*args)
|
5
|
+
super
|
6
|
+
@tmpfiles = []
|
7
|
+
_init_config
|
8
|
+
_load_config
|
9
|
+
_puts_welcome
|
10
|
+
end
|
11
|
+
private
|
12
|
+
def _info(path, more = false)
|
13
|
+
if _is_ipa? path
|
14
|
+
_ipa_info path, more
|
15
|
+
elsif _is_apk? path
|
16
|
+
_apk_info path, more
|
17
|
+
else
|
18
|
+
_puts "! #{Paint['只能支持后缀为 ipa 和 apk 的文件', :red]}"
|
19
|
+
_exit
|
20
|
+
end
|
21
|
+
end
|
22
|
+
def _apk_info(path, more = false)
|
23
|
+
path = _path path
|
24
|
+
apk = Android::Apk.new path
|
25
|
+
info = {
|
26
|
+
:type => 'android',
|
27
|
+
:identifier => apk.manifest.package_name,
|
28
|
+
:name => apk.label,
|
29
|
+
:version => apk.manifest.version_code,
|
30
|
+
:short_version => apk.manifest.version_name
|
31
|
+
}
|
32
|
+
if more
|
33
|
+
info[:icons] = apk.icon.map do |name, data|
|
34
|
+
tfile = Tempfile.new ["icon-#{SecureRandom.hex}", '.png']
|
35
|
+
tfile.write data
|
36
|
+
@tmpfiles.push tfile
|
37
|
+
{
|
38
|
+
:name => File.basename(name),
|
39
|
+
:path => tfile.path
|
40
|
+
}
|
41
|
+
end
|
42
|
+
end
|
43
|
+
info
|
44
|
+
end
|
45
|
+
def _ipa_info(path, more = false)
|
46
|
+
path = _path path
|
47
|
+
ipa = Lagunitas::IPA.new path
|
48
|
+
_puts '> 正在解析 ipa 文件...'
|
49
|
+
app = ipa.app
|
50
|
+
info = {
|
51
|
+
:type => 'ios',
|
52
|
+
:identifier => app.identifier,
|
53
|
+
:name => app.name,
|
54
|
+
:display_name => app.display_name,
|
55
|
+
:version => app.version,
|
56
|
+
:short_version => app.short_version,
|
57
|
+
:devices => app.devices,
|
58
|
+
:release_type => app.release_type,
|
59
|
+
:distribution_name => app.distribution_name
|
60
|
+
}
|
61
|
+
if more
|
62
|
+
if app.icons
|
63
|
+
info[:icons] = app.icons.sort { |a,b| -(a[:width] <=> b[:width]) }.map do |icon|
|
64
|
+
tfile = Tempfile.new ["icon-#{SecureRandom.hex}", '.png']
|
65
|
+
@tmpfiles.push tfile
|
66
|
+
FileUtils.cp icon[:path], tfile.path
|
67
|
+
{
|
68
|
+
:name => File.basename(icon[:path]),
|
69
|
+
:path => tfile.path
|
70
|
+
}
|
71
|
+
end
|
72
|
+
end
|
73
|
+
info[:plist] = app.info
|
74
|
+
app.mobileprovision.delete 'DeveloperCertificates' if app.mobileprovision
|
75
|
+
info[:mobileprovision] = app.mobileprovision
|
76
|
+
end
|
77
|
+
ipa.cleanup
|
78
|
+
info
|
79
|
+
end
|
80
|
+
def _batch_publish(*dirs)
|
81
|
+
_puts "! #{ Paint['至少需要提供一个文件夹', :red] }" if dirs.length == 0
|
82
|
+
dirs.each do |dir|
|
83
|
+
Dir.foreach(dir) do |_f|
|
84
|
+
if _is_ipa?(_f) || _is_apk?(_f)
|
85
|
+
_puts "> 正在发布 #{ _f }"
|
86
|
+
begin
|
87
|
+
publish File.join dir, _f
|
88
|
+
# rescue Exception => e
|
89
|
+
# _puts "! #{ _f } 失败:#{ e.to_s }"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -2,34 +2,41 @@
|
|
2
2
|
module Fir
|
3
3
|
class Cli
|
4
4
|
private
|
5
|
+
def _fir_url
|
6
|
+
@base_path ||= begin
|
7
|
+
path = @config['base_path'] || 'http://fir.im'
|
8
|
+
path = path.strip
|
9
|
+
path = path.end_with?('/') ? path.chop : path
|
10
|
+
end
|
11
|
+
end
|
5
12
|
def _fir_info(identifier, type = 'ios')
|
6
13
|
_puts "> 正在获取 #{identifier}@FIR.im 的应用信息..."
|
7
|
-
_chk_login
|
14
|
+
_chk_login!
|
8
15
|
body = { :token => @token, :type => type }
|
9
|
-
res = RestClient.get "#{
|
16
|
+
res = RestClient.get "#{_fir_url}/api/v2/app/info/#{identifier}?#{URI.encode_www_form body}"
|
10
17
|
JSON.parse res.body, :symbolize_names => true
|
11
18
|
end
|
12
19
|
def _fir_put(id, body)
|
13
|
-
_chk_login
|
20
|
+
_chk_login!
|
14
21
|
body[:token] = @token
|
15
22
|
_puts '> 正在更新 fir 的应用信息...'
|
16
|
-
RestClient.put "#{
|
23
|
+
RestClient.put "#{_fir_url}/api/v2/app/#{id}?#{URI.encode_www_form body}", body
|
17
24
|
_puts '> 更新成功'
|
18
25
|
end
|
19
26
|
def _fir_vput_complete(id, body)
|
20
|
-
_chk_login
|
27
|
+
_chk_login!
|
21
28
|
body[:token] = @token
|
22
29
|
_puts '> 正在更新 fir 的应用版本信息...'
|
23
|
-
RestClient.put "#{
|
30
|
+
RestClient.put "#{_fir_url}/api/v2/appVersion/#{id}/complete?#{URI.encode_www_form body}", body
|
24
31
|
_puts '> 更新成功'
|
25
32
|
end
|
26
33
|
def _fir_vput(id, body)
|
27
|
-
_chk_login
|
34
|
+
_chk_login!
|
28
35
|
body[:token] = @token
|
29
|
-
RestClient.put "#{
|
36
|
+
RestClient.put "#{_fir_url}/api/v2/appVersion/#{id}?#{URI.encode_www_form body}", body
|
30
37
|
end
|
31
38
|
def _user(token)
|
32
|
-
RestClient.get "#{
|
39
|
+
RestClient.get "#{_fir_url}/api/v2/user/me?token=#{token}" do |res|
|
33
40
|
case res.code
|
34
41
|
when 200
|
35
42
|
JSON.parse res.body, :symbolize_names => true
|
data/lib/fir-cli.opt.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
module Fir
|
3
|
+
class Cli
|
4
|
+
no_commands do
|
5
|
+
%w(token email verbose origin branch mobile_provision sign entitlements).each do |_m|
|
6
|
+
define_method "_opt_#{_m}" do
|
7
|
+
unless instance_variable_get("@#{_m}")
|
8
|
+
instance_variable_set("@#{_m}", options[_m.to_sym] || @config[_m] )
|
9
|
+
end
|
10
|
+
instance_variable_get("@#{_m}")
|
11
|
+
end
|
12
|
+
private "_opt_#{_m}".to_sym
|
13
|
+
end
|
14
|
+
%w(publish resign quiet color trim).each do |_m|
|
15
|
+
define_method "_opt_#{_m}" do
|
16
|
+
return false if options[_m.to_sym] == false
|
17
|
+
unless instance_variable_get("@#{_m}")
|
18
|
+
instance_variable_set("@#{_m}", options[_m.to_sym] || @config[_m] )
|
19
|
+
end
|
20
|
+
instance_variable_get("@#{_m}")
|
21
|
+
end
|
22
|
+
private "_opt_#{_m}".to_sym
|
23
|
+
end
|
24
|
+
end
|
25
|
+
private
|
26
|
+
def _opt (*opts)
|
27
|
+
opts.map { |_opt| method("_opt_#{_opt}").call }
|
28
|
+
end
|
29
|
+
def _opt? (*opts)
|
30
|
+
opts.each { |_opt| return false if !method("_opt_#{_opt}").call }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
module Fir
|
3
|
+
class Cli
|
4
|
+
def self.output_options
|
5
|
+
option :verbose,
|
6
|
+
:desc => '设置输出辅助信息的详细程度',
|
7
|
+
:type => :string,
|
8
|
+
:enum => ['v', 'vv', 'vvv']
|
9
|
+
option :quiet,
|
10
|
+
:aliases => '-q',
|
11
|
+
:desc => '安静模式,不输出任何辅助信息',
|
12
|
+
:type => 'boolean'
|
13
|
+
option :color,
|
14
|
+
:desc => '设置输出带有颜色的信息',
|
15
|
+
:type => 'boolean'
|
16
|
+
end
|
17
|
+
private
|
18
|
+
def _puts(text)
|
19
|
+
return puts _format text if !/^[->!] /.match text
|
20
|
+
return if _opt_quiet
|
21
|
+
case _opt_verbose || 'vv' # If nothing about log is set, use the default option - vv
|
22
|
+
when 'v'
|
23
|
+
puts _format text if text.start_with?('!')
|
24
|
+
when 'vv'
|
25
|
+
puts _format text if text.start_with?('!') || text.start_with?('>')
|
26
|
+
when 'vvv'
|
27
|
+
puts _format text if text.start_with?('!') || text.start_with?('>') || text.start_with?('-')
|
28
|
+
end
|
29
|
+
end
|
30
|
+
def _format(text)
|
31
|
+
return text.gsub /\e\[\d+(?:;\d+)*m/, '' if _opt_color == false
|
32
|
+
text
|
33
|
+
end
|
34
|
+
def _puts_welcome
|
35
|
+
_puts "> #{Paint['欢迎使用 FIR.im 命令行工具,如需帮助请输入:', :green]} fir help"
|
36
|
+
end
|
37
|
+
def _puts_require_token
|
38
|
+
_puts "! #{Paint['用户 token 不能为空', :red]}"
|
39
|
+
end
|
40
|
+
def _puts_invalid_token
|
41
|
+
_puts "! #{Paint['输入的用户 token 不合法', :red]}"
|
42
|
+
end
|
43
|
+
def _puts_invalid_email
|
44
|
+
_puts "! #{Paint['输入的邮件地址不合法', :red]}"
|
45
|
+
end
|
46
|
+
def _prompt_secret(prompt)
|
47
|
+
prompt = '' if _opt_quiet
|
48
|
+
ask(prompt) { |_q| _q.echo = false }
|
49
|
+
end
|
50
|
+
def _prompt(prompt)
|
51
|
+
prompt = '' if _opt_quiet
|
52
|
+
ask(prompt) { |_q| _q }
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
module Fir
|
3
|
+
class Cli
|
4
|
+
def initialize(*args)
|
5
|
+
super
|
6
|
+
@tmpfiles = []
|
7
|
+
_init_config
|
8
|
+
_load_config
|
9
|
+
_puts_welcome
|
10
|
+
end
|
11
|
+
def self.find_extends
|
12
|
+
`gem list --local`
|
13
|
+
.each_line("\n")
|
14
|
+
.map { |gem| /^[^\s]+/.match(gem)[0] }
|
15
|
+
.select { |gem| true if gem.start_with? 'fir-cli-' }
|
16
|
+
end
|
17
|
+
private
|
18
|
+
def _init_config
|
19
|
+
@uconfig = UserConfig.new '.fir'
|
20
|
+
@global_config = @uconfig['global.yaml']
|
21
|
+
end
|
22
|
+
def _load_config
|
23
|
+
@config = @uconfig[_profile]
|
24
|
+
end
|
25
|
+
def _set_config(configs)
|
26
|
+
if configs.length > 0
|
27
|
+
configs.each do |option|
|
28
|
+
_puts "> #{Paint[option[0].to_s.rjust(10), :blue]} : #{@config[option[0].to_s]} => #{option[1]}"
|
29
|
+
@config[option[0].to_s] = option[1]
|
30
|
+
end
|
31
|
+
if @config['token'] && !_user(@config['token'])
|
32
|
+
_puts_invalid_token
|
33
|
+
_exit
|
34
|
+
end
|
35
|
+
@config.save
|
36
|
+
end
|
37
|
+
end
|
38
|
+
def _profile
|
39
|
+
@global_config['profile'] || 'default.yaml'
|
40
|
+
end
|
41
|
+
def _path(path)
|
42
|
+
path = Pathname.new(Dir.pwd).join(path).cleanpath
|
43
|
+
end
|
44
|
+
def _is_ipa?(path)
|
45
|
+
path.end_with? '.ipa'
|
46
|
+
end
|
47
|
+
def _is_apk?(path)
|
48
|
+
path.end_with? '.apk'
|
49
|
+
end
|
50
|
+
def _is_workspace?(path)
|
51
|
+
path.end_with? '.xcworkspace'
|
52
|
+
end
|
53
|
+
def _is_xcodeproject?(path)
|
54
|
+
path.end_with? '.xcodeproj'
|
55
|
+
end
|
56
|
+
def _is_identifier?(str)
|
57
|
+
/^(?:(?:[a-zA-Z]|[a-zA-Z][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*(?:[A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])$/.match str
|
58
|
+
end
|
59
|
+
def _is_email?(str)
|
60
|
+
/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.(?:[A-Z]{2}|com|org|net|edu|gov|mil|biz|info|mobi|name|aero|asia|jobs|museum)$/i.match str
|
61
|
+
end
|
62
|
+
def _os
|
63
|
+
return 'mac' if /darwin/ =~ RUBY_PLATFORM
|
64
|
+
return 'windows' if /cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM
|
65
|
+
return 'linux'
|
66
|
+
end
|
67
|
+
def _exit
|
68
|
+
exit 1
|
69
|
+
end
|
70
|
+
def _exec(cmd)
|
71
|
+
output = `#{cmd}`
|
72
|
+
if $?.exitstatus != 0
|
73
|
+
puts output
|
74
|
+
_exit
|
75
|
+
end
|
76
|
+
end
|
77
|
+
def _convert_settings(*settings)
|
78
|
+
settings.reduce({}) do |hash, setting|
|
79
|
+
key,val = setting.split('=', 2).map(&:strip)
|
80
|
+
hash[key.to_sym] = val
|
81
|
+
hash
|
82
|
+
end
|
83
|
+
end
|
84
|
+
def _edit(ipath, opath=nil)
|
85
|
+
ipath = _path ipath
|
86
|
+
if !opath then opath = ipath else opath = _path opath end
|
87
|
+
extname = File.extname opath
|
88
|
+
Dir.mktmpdir do |_d|
|
89
|
+
Dir.chdir(_d) do
|
90
|
+
|
91
|
+
Zip::ZipFile.open(ipath) do |_z|
|
92
|
+
_z.each do |_entry|
|
93
|
+
entrypath = File.join _d, _entry.name
|
94
|
+
FileUtils.mkdir_p File.dirname entrypath
|
95
|
+
_z.extract _entry, entrypath unless File.file? entrypath
|
96
|
+
end
|
97
|
+
end
|
98
|
+
yield _d if block_given?
|
99
|
+
File.unlink opath if File.file? opath
|
100
|
+
_exec "zip -qr #{opath} Payload"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
data/lib/lagunitas.ext.rb
CHANGED
@@ -15,7 +15,7 @@ module Lagunitas
|
|
15
15
|
end
|
16
16
|
def mobileprovision?
|
17
17
|
return true if @mobileprovision
|
18
|
-
File.
|
18
|
+
File.file? File.join @path, 'embedded.mobileprovision'
|
19
19
|
end
|
20
20
|
def devices
|
21
21
|
mobileprovision['ProvisionedDevices'] if mobileprovision
|
@@ -30,7 +30,7 @@ module Lagunitas
|
|
30
30
|
end
|
31
31
|
def metadata?
|
32
32
|
return true if @metadata
|
33
|
-
File.
|
33
|
+
File.file? File.join @root, 'iTunesMetadata.plist'
|
34
34
|
end
|
35
35
|
def release_type
|
36
36
|
@release_type ||= begin
|
data/lib/lagunitas.patch.rb
CHANGED
@@ -19,8 +19,7 @@ module Lagunitas
|
|
19
19
|
end
|
20
20
|
def get_image(name)
|
21
21
|
path = File.join @path, name
|
22
|
-
return nil unless File.
|
23
|
-
|
22
|
+
return nil unless File.file? path
|
24
23
|
dimensions = Pngdefry.dimensions(path)
|
25
24
|
{
|
26
25
|
path: path,
|
@@ -42,7 +41,7 @@ module Lagunitas
|
|
42
41
|
zip_file.each do |f|
|
43
42
|
f_path = File.join tmp_path, f.name
|
44
43
|
FileUtils.mkdir_p File.dirname f_path
|
45
|
-
zip_file.extract f, f_path unless File.
|
44
|
+
zip_file.extract f, f_path unless File.file? f_path
|
46
45
|
end
|
47
46
|
end
|
48
47
|
tmp_path
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fir-cli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.1.a
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- FIR.im
|
@@ -122,6 +122,20 @@ dependencies:
|
|
122
122
|
- - "~>"
|
123
123
|
- !ruby/object:Gem::Version
|
124
124
|
version: '1.6'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: rubyzip
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: 0.9.9
|
132
|
+
type: :runtime
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: 0.9.9
|
125
139
|
- !ruby/object:Gem::Dependency
|
126
140
|
name: bundler
|
127
141
|
requirement: !ruby/object:Gem::Requirement
|
@@ -171,11 +185,17 @@ files:
|
|
171
185
|
- lib/fir-cli-commands/00-upgrade.rb
|
172
186
|
- lib/fir-cli-commands/00-version.rb
|
173
187
|
- lib/fir-cli-commands/01-config.rb
|
174
|
-
- lib/fir-cli-commands/
|
188
|
+
- lib/fir-cli-commands/100-resign_codesign.rb
|
189
|
+
- lib/fir-cli-commands/100-resign_tapbeta.rb
|
190
|
+
- lib/fir-cli-commands/101-resign.rb
|
175
191
|
- lib/fir-cli-commands/11-publish.rb
|
176
192
|
- lib/fir-cli-commands/12-build_ipa.rb
|
177
|
-
- lib/fir-cli.
|
178
|
-
- lib/fir-cli.
|
193
|
+
- lib/fir-cli.chk.rb
|
194
|
+
- lib/fir-cli.core.rb
|
195
|
+
- lib/fir-cli.fir.rb
|
196
|
+
- lib/fir-cli.opt.rb
|
197
|
+
- lib/fir-cli.output.rb
|
198
|
+
- lib/fir-cli.utils.rb
|
179
199
|
- lib/fir-cli/version.rb
|
180
200
|
- lib/lagunitas.ext.rb
|
181
201
|
- lib/lagunitas.patch.rb
|
@@ -196,9 +216,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
196
216
|
version: '0'
|
197
217
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
198
218
|
requirements:
|
199
|
-
- - "
|
219
|
+
- - ">"
|
200
220
|
- !ruby/object:Gem::Version
|
201
|
-
version:
|
221
|
+
version: 1.3.1
|
202
222
|
requirements: []
|
203
223
|
rubyforge_project:
|
204
224
|
rubygems_version: 2.4.4
|