fir-cli 0.2.0 → 0.2.1.a
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/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
|