fir-cli 0.2.3.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +13 -82
- data/CHANGELOG +36 -0
- data/Gemfile +3 -1
- data/LICENSE.txt +1 -1
- data/README.md +83 -103
- data/Rakefile +1 -0
- data/bin/fir +9 -11
- data/fir-cli.gemspec +47 -26
- data/lib/fir/api.yml +5 -0
- data/lib/fir/cli.rb +107 -0
- data/lib/fir/patches/bin/pngcrush +0 -0
- data/lib/fir/patches/native_patch.rb +199 -0
- data/lib/fir/patches/os_patch.rb +22 -0
- data/lib/fir/patches/parser_patch.rb +165 -0
- data/lib/fir/patches.rb +5 -0
- data/lib/fir/util/build.rb +158 -0
- data/lib/fir/util/info.rb +79 -0
- data/lib/fir/util/login.rb +17 -0
- data/lib/fir/util/publish.rb +103 -0
- data/lib/fir/util.rb +45 -0
- data/lib/fir/version.rb +5 -0
- data/lib/fir-cli.rb +3 -0
- data/lib/fir.rb +87 -0
- data/lib/fir_cli.rb +3 -0
- metadata +57 -109
- data/Gemfile.lock +0 -45
- data/fir-cli.rb +0 -27
- data/lib/fir-cli/version.rb +0 -5
- data/lib/fir-cli-commands/00-info.rb +0 -17
- data/lib/fir-cli-commands/00-login.rb +0 -30
- data/lib/fir-cli-commands/00-profile.rb +0 -31
- data/lib/fir-cli-commands/00-upgrade.rb +0 -15
- data/lib/fir-cli-commands/00-version.rb +0 -10
- data/lib/fir-cli-commands/01-config.rb +0 -17
- data/lib/fir-cli-commands/100-resign_codesign.rb +0 -59
- data/lib/fir-cli-commands/100-resign_tapbeta.rb +0 -85
- data/lib/fir-cli-commands/101-resign.rb +0 -26
- data/lib/fir-cli-commands/11-publish.rb +0 -73
- data/lib/fir-cli-commands/12-build_ipa.rb +0 -153
- data/lib/fir-cli.chk.rb +0 -33
- data/lib/fir-cli.core.rb +0 -96
- data/lib/fir-cli.fir.rb +0 -49
- data/lib/fir-cli.opt.rb +0 -35
- data/lib/fir-cli.output.rb +0 -55
- data/lib/fir-cli.utils.rb +0 -108
- data/lib/lagunitas.ext.rb +0 -56
- data/lib/lagunitas.patch.rb +0 -51
- data/lib/user_config.patch.rb +0 -10
@@ -0,0 +1,199 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class Object
|
4
|
+
# activesupport/lib/active_support/core_ext/object/blank.rb
|
5
|
+
# An object is blank if it's false, empty, or a whitespace string.
|
6
|
+
# For example, '', ' ', +nil+, [], and {} are all blank.
|
7
|
+
#
|
8
|
+
# This simplifies
|
9
|
+
#
|
10
|
+
# address.nil? || address.empty?
|
11
|
+
#
|
12
|
+
# to
|
13
|
+
#
|
14
|
+
# address.blank?
|
15
|
+
#
|
16
|
+
# @return [true, false]
|
17
|
+
def blank?
|
18
|
+
respond_to?(:empty?) ? !!empty? : !self
|
19
|
+
end
|
20
|
+
|
21
|
+
# An object is present if it's not blank.
|
22
|
+
#
|
23
|
+
# @return [true, false]
|
24
|
+
def present?
|
25
|
+
!blank?
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returns the receiver if it's present otherwise returns +nil+.
|
29
|
+
# <tt>object.presence</tt> is equivalent to
|
30
|
+
#
|
31
|
+
# object.present? ? object : nil
|
32
|
+
#
|
33
|
+
# For example, something like
|
34
|
+
#
|
35
|
+
# state = params[:state] if params[:state].present?
|
36
|
+
# country = params[:country] if params[:country].present?
|
37
|
+
# region = state || country || 'US'
|
38
|
+
#
|
39
|
+
# becomes
|
40
|
+
#
|
41
|
+
# region = params[:state].presence || params[:country].presence || 'US'
|
42
|
+
#
|
43
|
+
# @return [Object]
|
44
|
+
def presence
|
45
|
+
self if present?
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class NilClass
|
50
|
+
# +nil+ is blank:
|
51
|
+
#
|
52
|
+
# nil.blank? # => true
|
53
|
+
#
|
54
|
+
# @return [true]
|
55
|
+
def blank?
|
56
|
+
true
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
class FalseClass
|
61
|
+
# +false+ is blank:
|
62
|
+
#
|
63
|
+
# false.blank? # => true
|
64
|
+
#
|
65
|
+
# @return [true]
|
66
|
+
def blank?
|
67
|
+
true
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
class TrueClass
|
72
|
+
# +true+ is not blank:
|
73
|
+
#
|
74
|
+
# true.blank? # => false
|
75
|
+
#
|
76
|
+
# @return [false]
|
77
|
+
def blank?
|
78
|
+
false
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
class Array
|
83
|
+
# An array is blank if it's empty:
|
84
|
+
#
|
85
|
+
# [].blank? # => true
|
86
|
+
# [1,2,3].blank? # => false
|
87
|
+
#
|
88
|
+
# @return [true, false]
|
89
|
+
alias_method :blank?, :empty?
|
90
|
+
end
|
91
|
+
|
92
|
+
class Hash
|
93
|
+
# A hash is blank if it's empty:
|
94
|
+
#
|
95
|
+
# {}.blank? # => true
|
96
|
+
# { key: 'value' }.blank? # => false
|
97
|
+
#
|
98
|
+
# @return [true, false]
|
99
|
+
alias_method :blank?, :empty?
|
100
|
+
end
|
101
|
+
|
102
|
+
class String
|
103
|
+
BLANK_RE = /\A[[:space:]]*\z/
|
104
|
+
|
105
|
+
# A string is blank if it's empty or contains whitespaces only:
|
106
|
+
#
|
107
|
+
# ''.blank? # => true
|
108
|
+
# ' '.blank? # => true
|
109
|
+
# "\t\n\r".blank? # => true
|
110
|
+
# ' blah '.blank? # => false
|
111
|
+
#
|
112
|
+
# Unicode whitespace is supported:
|
113
|
+
#
|
114
|
+
# "\u00a0".blank? # => true
|
115
|
+
#
|
116
|
+
# @return [true, false]
|
117
|
+
def blank?
|
118
|
+
BLANK_RE === self
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
class Numeric #:nodoc:
|
123
|
+
# No number is blank:
|
124
|
+
#
|
125
|
+
# 1.blank? # => false
|
126
|
+
# 0.blank? # => false
|
127
|
+
#
|
128
|
+
# @return [false]
|
129
|
+
def blank?
|
130
|
+
false
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
class Hash
|
135
|
+
# Returns a new hash with all keys converted using the block operation.
|
136
|
+
#
|
137
|
+
# hash = { name: 'Rob', age: '28' }
|
138
|
+
#
|
139
|
+
# hash.transform_keys{ |key| key.to_s.upcase }
|
140
|
+
# # => {"NAME"=>"Rob", "AGE"=>"28"}
|
141
|
+
def transform_keys
|
142
|
+
return enum_for(:transform_keys) unless block_given?
|
143
|
+
result = self.class.new
|
144
|
+
each_key do |key|
|
145
|
+
result[yield(key)] = self[key]
|
146
|
+
end
|
147
|
+
result
|
148
|
+
end
|
149
|
+
|
150
|
+
# Returns a new hash with all keys converted to symbols, as long as
|
151
|
+
# they respond to +to_sym+.
|
152
|
+
#
|
153
|
+
# hash = { 'name' => 'Rob', 'age' => '28' }
|
154
|
+
#
|
155
|
+
# hash.symbolize_keys
|
156
|
+
# # => {:name=>"Rob", :age=>"28"}
|
157
|
+
def symbolize_keys
|
158
|
+
transform_keys{ |key| key.to_sym rescue key }
|
159
|
+
end
|
160
|
+
|
161
|
+
# Returns a new hash with all keys converted by the block operation.
|
162
|
+
# This includes the keys from the root hash and from all
|
163
|
+
# nested hashes and arrays.
|
164
|
+
#
|
165
|
+
# hash = { person: { name: 'Rob', age: '28' } }
|
166
|
+
#
|
167
|
+
# hash.deep_transform_keys{ |key| key.to_s.upcase }
|
168
|
+
# # => {"PERSON"=>{"NAME"=>"Rob", "AGE"=>"28"}}
|
169
|
+
def deep_transform_keys(&block)
|
170
|
+
_deep_transform_keys_in_object(self, &block)
|
171
|
+
end
|
172
|
+
|
173
|
+
# Returns a new hash with all keys converted to symbols, as long as
|
174
|
+
# they respond to +to_sym+. This includes the keys from the root hash
|
175
|
+
# and from all nested hashes and arrays.
|
176
|
+
#
|
177
|
+
# hash = { 'person' => { 'name' => 'Rob', 'age' => '28' } }
|
178
|
+
#
|
179
|
+
# hash.deep_symbolize_keys
|
180
|
+
# # => {:person=>{:name=>"Rob", :age=>"28"}}
|
181
|
+
def deep_symbolize_keys
|
182
|
+
deep_transform_keys{ |key| key.to_sym rescue key }
|
183
|
+
end
|
184
|
+
|
185
|
+
private
|
186
|
+
# support methods for deep transforming nested hashes and arrays
|
187
|
+
def _deep_transform_keys_in_object(object, &block)
|
188
|
+
case object
|
189
|
+
when Hash
|
190
|
+
object.each_with_object({}) do |(key, value), result|
|
191
|
+
result[yield(key)] = _deep_transform_keys_in_object(value, &block)
|
192
|
+
end
|
193
|
+
when Array
|
194
|
+
object.map {|e| _deep_transform_keys_in_object(e, &block) }
|
195
|
+
else
|
196
|
+
object
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module OS
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def windows?
|
7
|
+
(/cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM) != nil
|
8
|
+
end
|
9
|
+
|
10
|
+
def mac?
|
11
|
+
(/darwin/ =~ RUBY_PLATFORM) != nil
|
12
|
+
end
|
13
|
+
|
14
|
+
def unix?
|
15
|
+
!OS.windows?
|
16
|
+
end
|
17
|
+
|
18
|
+
def linux?
|
19
|
+
OS.unix? && !OS.mac?
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,165 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Parser
|
4
|
+
|
5
|
+
class << self
|
6
|
+
|
7
|
+
def png_bin
|
8
|
+
@png_bin ||= File.expand_path("../bin/pngcrush", __FILE__)
|
9
|
+
end
|
10
|
+
|
11
|
+
def uncrush_icon crushed_icon_path, uncrushed_icon_path
|
12
|
+
system("#{png_bin} -revert-iphone-optimizations #{crushed_icon_path} #{uncrushed_icon_path} &> /dev/null")
|
13
|
+
end
|
14
|
+
|
15
|
+
def crush_icon uncrushed_icon_path, crushed_icon_path
|
16
|
+
system("#{png_bin} -iphone #{uncrushed_icon_path} #{crushed_icon_path} &> /dev/null")
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class IPA
|
21
|
+
|
22
|
+
def initialize(path)
|
23
|
+
@path = path
|
24
|
+
end
|
25
|
+
|
26
|
+
def app
|
27
|
+
@app ||= App.new(app_path)
|
28
|
+
end
|
29
|
+
|
30
|
+
def app_path
|
31
|
+
@app_path ||= Dir.glob(File.join(contents, 'Payload', '*.app')).first
|
32
|
+
end
|
33
|
+
|
34
|
+
def cleanup
|
35
|
+
return unless @contents
|
36
|
+
FileUtils.rm_rf(@contents)
|
37
|
+
@contents = nil
|
38
|
+
end
|
39
|
+
|
40
|
+
def metadata
|
41
|
+
return unless has_metadata?
|
42
|
+
@metadata ||= CFPropertyList.native_types(CFPropertyList::List.new(file: metadata_path).value)
|
43
|
+
end
|
44
|
+
|
45
|
+
def has_metadata?
|
46
|
+
File.file? metadata_path
|
47
|
+
end
|
48
|
+
|
49
|
+
def metadata_path
|
50
|
+
@metadata_path ||= File.join(@contents, 'iTunesMetadata.plist')
|
51
|
+
end
|
52
|
+
|
53
|
+
def release_type
|
54
|
+
has_metadata? ? 'store' : 'adhoc'
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def contents
|
60
|
+
return if @contents
|
61
|
+
@contents = "tmp/ipa_files-#{Time.now.to_i}"
|
62
|
+
|
63
|
+
Zip::File.open(@path) do |zip_file|
|
64
|
+
zip_file.each do |f|
|
65
|
+
f_path = File.join(@contents, f.name)
|
66
|
+
FileUtils.mkdir_p(File.dirname(f_path))
|
67
|
+
zip_file.extract(f, f_path) unless File.exist?(f_path)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
@contents
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class App
|
76
|
+
|
77
|
+
def initialize(path)
|
78
|
+
@path = path
|
79
|
+
end
|
80
|
+
|
81
|
+
def info
|
82
|
+
@info ||= CFPropertyList.native_types(
|
83
|
+
CFPropertyList::List.new(file: File.join(@path, 'Info.plist')).value)
|
84
|
+
end
|
85
|
+
|
86
|
+
def name
|
87
|
+
info['CFBundleName']
|
88
|
+
end
|
89
|
+
|
90
|
+
def identifier
|
91
|
+
info['CFBundleIdentifier']
|
92
|
+
end
|
93
|
+
|
94
|
+
def display_name
|
95
|
+
info['CFBundleDisplayName']
|
96
|
+
end
|
97
|
+
|
98
|
+
def version
|
99
|
+
info['CFBundleVersion']
|
100
|
+
end
|
101
|
+
|
102
|
+
def short_version
|
103
|
+
info['CFBundleShortVersionString']
|
104
|
+
end
|
105
|
+
|
106
|
+
def icons
|
107
|
+
@icons ||= begin
|
108
|
+
icons = []
|
109
|
+
info['CFBundleIcons']['CFBundlePrimaryIcon']['CFBundleIconFiles'].each do |name|
|
110
|
+
icons << get_image(name)
|
111
|
+
icons << get_image("#{name}@2x")
|
112
|
+
end
|
113
|
+
icons.delete_if { |i| !i }
|
114
|
+
rescue NoMethodError
|
115
|
+
[]
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def mobileprovision
|
120
|
+
return unless has_mobileprovision?
|
121
|
+
return @mobileprovision if @mobileprovision
|
122
|
+
|
123
|
+
@mobileprovision = CFPropertyList.native_types(
|
124
|
+
CFPropertyList::List.new(data: `security cms -D -i #{mobileprovision_path}`).value)
|
125
|
+
end
|
126
|
+
|
127
|
+
def has_mobileprovision?
|
128
|
+
File.file? mobileprovision_path
|
129
|
+
end
|
130
|
+
|
131
|
+
def mobileprovision_path
|
132
|
+
@mobileprovision_path ||= File.join(@path, 'embedded.mobileprovision')
|
133
|
+
end
|
134
|
+
|
135
|
+
def hide_developer_certificates
|
136
|
+
mobileprovision.delete('DeveloperCertificates') if has_mobileprovision?
|
137
|
+
end
|
138
|
+
|
139
|
+
def devices
|
140
|
+
mobileprovision['ProvisionedDevices'] if has_mobileprovision?
|
141
|
+
end
|
142
|
+
|
143
|
+
def distribution_name
|
144
|
+
"#{mobileprovision['Name']} - #{mobileprovision['TeamName']}" if has_mobileprovision?
|
145
|
+
end
|
146
|
+
|
147
|
+
def release_type
|
148
|
+
if has_mobileprovision?
|
149
|
+
if devices
|
150
|
+
'adhoc'
|
151
|
+
else
|
152
|
+
'inhouse'
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
private
|
158
|
+
|
159
|
+
def get_image name
|
160
|
+
path = File.join(@path, "#{name}.png")
|
161
|
+
return nil unless File.exist?(path)
|
162
|
+
path
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
data/lib/fir/patches.rb
ADDED
@@ -0,0 +1,158 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module FIR
|
4
|
+
module Build
|
5
|
+
|
6
|
+
def build_ipa *args, options
|
7
|
+
# initialize build options
|
8
|
+
if args.first.blank? || !File.exist?(args.first)
|
9
|
+
build_dir = Dir.pwd
|
10
|
+
else
|
11
|
+
build_dir = File.absolute_path(args.shift.to_s) # pop the first param
|
12
|
+
end
|
13
|
+
|
14
|
+
build_cmd = "xcodebuild build -sdk iphoneos"
|
15
|
+
build_tmp_dir = Dir.mktmpdir
|
16
|
+
custom_settings = parse_custom_settings(args) # convert ['a=1', 'b=2'] => { 'a' => '1', 'b' => '2' }
|
17
|
+
configuration = options[:configuration]
|
18
|
+
target_name = options[:target]
|
19
|
+
scheme_name = options[:scheme]
|
20
|
+
output_path = options[:output].blank? ? "#{build_dir}/build_ipa" : File.absolute_path(options[:output].to_s)
|
21
|
+
|
22
|
+
# check build environment and make build cmd
|
23
|
+
check_osx
|
24
|
+
if options.workspace?
|
25
|
+
workspace = check_and_find_workspace(build_dir)
|
26
|
+
check_scheme(scheme_name)
|
27
|
+
build_cmd += " -workspace '#{workspace}' -scheme '#{scheme_name}'"
|
28
|
+
else
|
29
|
+
project = check_and_find_project(build_dir)
|
30
|
+
build_cmd += " -project '#{project}'"
|
31
|
+
end
|
32
|
+
|
33
|
+
build_cmd += " -configuration '#{configuration}'" unless configuration.blank?
|
34
|
+
build_cmd += " -target '#{target_name}'" unless target_name.blank?
|
35
|
+
|
36
|
+
# convert { "a" => "1", "b" => "2" } => "a='1' b='2'"
|
37
|
+
setting_str = custom_settings.collect { |k, v| "#{k}='#{v}'" }.join(' ')
|
38
|
+
setting_str += " TARGET_BUILD_DIR='#{build_tmp_dir}'" unless custom_settings['TARGET_BUILD_DIR']
|
39
|
+
setting_str += " CONFIGURATION_BUILD_DIR='#{build_tmp_dir}'" unless custom_settings['CONFIGURATION_BUILD_DIR']
|
40
|
+
setting_str += " DWARF_DSYM_FOLDER_PATH='#{output_path}'" unless custom_settings['DWARF_DSYM_FOLDER_PATH']
|
41
|
+
|
42
|
+
build_cmd += " #{setting_str} 2>&1"
|
43
|
+
puts build_cmd if $DEBUG
|
44
|
+
|
45
|
+
logger.info "Building......"
|
46
|
+
logger_info_dividing_line
|
47
|
+
|
48
|
+
logger.info `#{build_cmd}`
|
49
|
+
|
50
|
+
FileUtils.mkdir_p(output_path) unless File.exist?(output_path)
|
51
|
+
Dir.chdir(build_tmp_dir) do
|
52
|
+
apps = Dir["*.app"]
|
53
|
+
if apps.length == 0
|
54
|
+
logger.error "Builded has no output app, Can not be packaged"
|
55
|
+
exit 1
|
56
|
+
end
|
57
|
+
|
58
|
+
apps.each do |app|
|
59
|
+
ipa_path = File.join(output_path, "#{File.basename(app, '.app')}.ipa")
|
60
|
+
zip_app2ipa(File.join(build_tmp_dir, app), ipa_path)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
logger.info "Build Success"
|
65
|
+
|
66
|
+
if options.publish?
|
67
|
+
ipa_path = Dir["#{output_path}/*.ipa"].first
|
68
|
+
publish(ipa_path, short: options[:short], changelog: options[:changelog], token: options[:token])
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def build_apk *args, options
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def parse_custom_settings args
|
78
|
+
hash = {}
|
79
|
+
args.each do |setting|
|
80
|
+
k, v = setting.split('=', 2).map(&:strip)
|
81
|
+
hash[k] = v
|
82
|
+
end
|
83
|
+
hash
|
84
|
+
end
|
85
|
+
|
86
|
+
def check_osx
|
87
|
+
unless OS.mac?
|
88
|
+
logger.error "Unsupported OS type, `build_ipa` only support for OSX"
|
89
|
+
exit 1
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def check_and_find_project path
|
94
|
+
unless File.exist?(path)
|
95
|
+
logger.error "The first param BUILD_DIR must be a xcodeproj directory"
|
96
|
+
exit 1
|
97
|
+
end
|
98
|
+
|
99
|
+
if is_project?(path)
|
100
|
+
project = path
|
101
|
+
else
|
102
|
+
project = Dir["#{path}/*.xcodeproj"].first
|
103
|
+
if project.blank?
|
104
|
+
logger.error "The xcodeproj file is missing, check the BUILD_DIR"
|
105
|
+
exit 1
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
project
|
110
|
+
end
|
111
|
+
|
112
|
+
def check_and_find_workspace path
|
113
|
+
unless File.exist?(path)
|
114
|
+
logger.error "The first param BUILD_DIR must be a xcworkspace directory"
|
115
|
+
exit 1
|
116
|
+
end
|
117
|
+
|
118
|
+
if is_workspace?(path)
|
119
|
+
workspace = path
|
120
|
+
else
|
121
|
+
workspace = Dir["#{path}/*.xcworkspace"].first
|
122
|
+
if workspace.blank?
|
123
|
+
logger.error "The xcworkspace file is missing, check the BUILD_DIR"
|
124
|
+
exit 1
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
workspace
|
129
|
+
end
|
130
|
+
|
131
|
+
def check_scheme scheme_name
|
132
|
+
if scheme_name.blank?
|
133
|
+
logger.error "Must provide a scheme by `-s` option when build a workspace"
|
134
|
+
exit 1
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def is_project? path
|
139
|
+
File.extname(path) == '.xcodeproj'
|
140
|
+
end
|
141
|
+
|
142
|
+
def is_workspace? path
|
143
|
+
File.extname(path) == '.xcworkspace'
|
144
|
+
end
|
145
|
+
|
146
|
+
def zip_app2ipa app_path, ipa_path
|
147
|
+
Dir.mktmpdir do |tmpdir|
|
148
|
+
Dir.chdir(tmpdir) do
|
149
|
+
Dir.mkdir("Payload")
|
150
|
+
FileUtils.cp_r(app_path, "Payload")
|
151
|
+
system("rm -rf #{ipa_path}") if File.file? ipa_path
|
152
|
+
system("zip -qr #{ipa_path} Payload")
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
end
|
158
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module FIR
|
4
|
+
module Info
|
5
|
+
|
6
|
+
def info *args, options
|
7
|
+
file_path = File.absolute_path(args.first.to_s)
|
8
|
+
is_all = !options[:all].blank?
|
9
|
+
|
10
|
+
check_supported_file file_path
|
11
|
+
|
12
|
+
file_type = File.extname(file_path).delete('.')
|
13
|
+
|
14
|
+
logger.info "Analyzing #{file_type} file......"
|
15
|
+
logger_info_dividing_line
|
16
|
+
|
17
|
+
app_info = send("#{file_type}_info", file_path, is_all)
|
18
|
+
app_info.each { |k, v| logger.info "#{k}: #{v}" }
|
19
|
+
end
|
20
|
+
|
21
|
+
def ipa_info ipa_path, is_all
|
22
|
+
ipa = Parser::IPA.new(ipa_path)
|
23
|
+
app = ipa.app
|
24
|
+
|
25
|
+
info = {
|
26
|
+
type: 'ios',
|
27
|
+
identifier: app.identifier,
|
28
|
+
name: app.name,
|
29
|
+
display_name: app.display_name,
|
30
|
+
version: app.version,
|
31
|
+
short_version: app.short_version,
|
32
|
+
devices: app.devices,
|
33
|
+
release_type: app.release_type || ipa.release_type,
|
34
|
+
distribution_name: app.distribution_name
|
35
|
+
}
|
36
|
+
|
37
|
+
if is_all
|
38
|
+
info[:icons] = []
|
39
|
+
app.icons.each do |icon|
|
40
|
+
tmp_icon_path = "#{Dir.tmpdir}/icon-#{SecureRandom.hex[4..9]}.png"
|
41
|
+
FileUtils.cp(icon, tmp_icon_path)
|
42
|
+
info[:icons] << tmp_icon_path
|
43
|
+
end
|
44
|
+
|
45
|
+
app.hide_developer_certificates
|
46
|
+
|
47
|
+
info[:plist] = app.info
|
48
|
+
info[:mobileprovision] = app.mobileprovision
|
49
|
+
end
|
50
|
+
|
51
|
+
ipa.cleanup
|
52
|
+
info
|
53
|
+
end
|
54
|
+
|
55
|
+
def apk_info apk_path, is_all
|
56
|
+
apk = Android::Apk.new(apk_path)
|
57
|
+
info = {
|
58
|
+
type: 'android',
|
59
|
+
identifier: apk.manifest.package_name,
|
60
|
+
name: apk.label,
|
61
|
+
version: apk.manifest.version_code,
|
62
|
+
short_version: apk.manifest.version_name
|
63
|
+
}
|
64
|
+
|
65
|
+
# apk.icon is a hash, { icon_name: icon_data }
|
66
|
+
if is_all
|
67
|
+
info[:icons] = []
|
68
|
+
apk.icon.each do |name, data|
|
69
|
+
tmp_icon_path = "#{Dir.tmpdir}/icon-#{SecureRandom.hex[4..9]}.png"
|
70
|
+
File.open(tmp_icon_path, 'w+') { |f| f << data }
|
71
|
+
info[:icons] << tmp_icon_path
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
info
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module FIR
|
4
|
+
module Login
|
5
|
+
|
6
|
+
def login token
|
7
|
+
check_token_cannot_be_blank token
|
8
|
+
|
9
|
+
user_info = fetch_user_info(token)
|
10
|
+
|
11
|
+
logger.info "Login succeed, previous user's email: #{config[:email]}" unless config.blank?
|
12
|
+
write_config(email: user_info.fetch(:email, ''), token: user_info.fetch(:token, ''))
|
13
|
+
reload_config
|
14
|
+
logger.info "Login succeed, current user's email: #{config[:email]}"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|