app-info 2.5.4 → 2.6.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/.github/dependabot.yml +9 -0
- data/.github/workflows/ci.yml +41 -0
- data/.rubocop.yml +26 -7
- data/CHANGELOG.md +43 -1
- data/Gemfile +5 -3
- data/README.md +110 -50
- data/app_info.gemspec +4 -1
- data/exe/app-info +12 -0
- data/lib/app_info/apk.rb +17 -21
- data/lib/app_info/core_ext/object/try.rb +77 -79
- data/lib/app_info/dsym.rb +3 -4
- data/lib/app_info/info_plist.rb +143 -0
- data/lib/app_info/ipa/framework.rb +3 -3
- data/lib/app_info/ipa.rb +72 -22
- data/lib/app_info/macos.rb +188 -0
- data/lib/app_info/{ipa/mobile_provision.rb → mobile_provision.rb} +9 -10
- data/lib/app_info/png_uncrush.rb +30 -31
- data/lib/app_info/proguard.rb +0 -1
- data/lib/app_info/shell.rb +34 -0
- data/lib/app_info/util.rb +46 -6
- data/lib/app_info/version.rb +1 -1
- data/lib/app_info.rb +21 -25
- metadata +41 -8
- data/.travis.yml +0 -9
- data/lib/app_info/ipa/info_plist.rb +0 -162
data/lib/app_info/apk.rb
CHANGED
@@ -3,7 +3,6 @@
|
|
3
3
|
require 'ruby_apk'
|
4
4
|
require 'image_size'
|
5
5
|
require 'forwardable'
|
6
|
-
require 'app_info/util'
|
7
6
|
|
8
7
|
module AppInfo
|
9
8
|
# Parse APK file
|
@@ -24,8 +23,8 @@ module AppInfo
|
|
24
23
|
@file = file
|
25
24
|
end
|
26
25
|
|
27
|
-
def size(
|
28
|
-
AppInfo::Util.file_size(@file,
|
26
|
+
def size(human_size: false)
|
27
|
+
AppInfo::Util.file_size(@file, human_size)
|
29
28
|
end
|
30
29
|
|
31
30
|
def os
|
@@ -77,6 +76,7 @@ module AppInfo
|
|
77
76
|
def min_sdk_version
|
78
77
|
manifest.min_sdk_ver
|
79
78
|
end
|
79
|
+
alias min_os_version min_sdk_version
|
80
80
|
|
81
81
|
def target_sdk_version
|
82
82
|
manifest.doc
|
@@ -110,29 +110,23 @@ module AppInfo
|
|
110
110
|
end
|
111
111
|
|
112
112
|
def apk
|
113
|
-
Zip.warn_invalid_date = false # fix invaild date format warnings
|
114
|
-
|
115
113
|
@apk ||= ::Android::Apk.new(@file)
|
116
114
|
end
|
117
115
|
|
118
116
|
def icons
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
}
|
132
|
-
end
|
117
|
+
@icons ||= apk.icon.each_with_object([]) do |(path, data), obj|
|
118
|
+
icon_name = File.basename(path)
|
119
|
+
icon_path = File.join(contents, File.dirname(path))
|
120
|
+
icon_file = File.join(icon_path, icon_name)
|
121
|
+
FileUtils.mkdir_p icon_path
|
122
|
+
File.write(icon_file, data, encoding: Encoding::BINARY)
|
123
|
+
|
124
|
+
obj << {
|
125
|
+
name: icon_name,
|
126
|
+
file: icon_file,
|
127
|
+
dimensions: ImageSize.path(icon_file).size
|
128
|
+
}
|
133
129
|
end
|
134
|
-
|
135
|
-
@icons
|
136
130
|
end
|
137
131
|
|
138
132
|
def clear!
|
@@ -164,6 +158,7 @@ module AppInfo
|
|
164
158
|
# Android Certificate
|
165
159
|
class Certificate
|
166
160
|
attr_reader :path, :certificate
|
161
|
+
|
167
162
|
def initialize(path, certificate)
|
168
163
|
@path = path
|
169
164
|
@certificate = certificate
|
@@ -173,6 +168,7 @@ module AppInfo
|
|
173
168
|
# Android Sign
|
174
169
|
class Sign
|
175
170
|
attr_reader :path, :sign
|
171
|
+
|
176
172
|
def initialize(path, sign)
|
177
173
|
@path = path
|
178
174
|
@sign = sign
|
@@ -4,7 +4,71 @@
|
|
4
4
|
# Copy from https://github.com/rails/rails/blob/master/activesupport/lib/active_support/core_ext/object/try.rb
|
5
5
|
|
6
6
|
module AppInfo
|
7
|
-
module Tryable
|
7
|
+
module Tryable # :nodoc:
|
8
|
+
##
|
9
|
+
# :method: try
|
10
|
+
#
|
11
|
+
# :call-seq:
|
12
|
+
# try(*a, &b)
|
13
|
+
#
|
14
|
+
# Invokes the public method whose name goes as first argument just like
|
15
|
+
# +public_send+ does, except that if the receiver does not respond to it the
|
16
|
+
# call returns +nil+ rather than raising an exception.
|
17
|
+
#
|
18
|
+
# This method is defined to be able to write
|
19
|
+
#
|
20
|
+
# @person.try(:name)
|
21
|
+
#
|
22
|
+
# instead of
|
23
|
+
#
|
24
|
+
# @person.name if @person
|
25
|
+
#
|
26
|
+
# +try+ calls can be chained:
|
27
|
+
#
|
28
|
+
# @person.try(:spouse).try(:name)
|
29
|
+
#
|
30
|
+
# instead of
|
31
|
+
#
|
32
|
+
# @person.spouse.name if @person && @person.spouse
|
33
|
+
#
|
34
|
+
# +try+ will also return +nil+ if the receiver does not respond to the method:
|
35
|
+
#
|
36
|
+
# @person.try(:non_existing_method) # => nil
|
37
|
+
#
|
38
|
+
# instead of
|
39
|
+
#
|
40
|
+
# @person.non_existing_method if @person.respond_to?(:non_existing_method) # => nil
|
41
|
+
#
|
42
|
+
# +try+ returns +nil+ when called on +nil+ regardless of whether it responds
|
43
|
+
# to the method:
|
44
|
+
#
|
45
|
+
# nil.try(:to_i) # => nil, rather than 0
|
46
|
+
#
|
47
|
+
# Arguments and blocks are forwarded to the method if invoked:
|
48
|
+
#
|
49
|
+
# @posts.try(:each_slice, 2) do |a, b|
|
50
|
+
# ...
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# The number of arguments in the signature must match. If the object responds
|
54
|
+
# to the method the call is attempted and +ArgumentError+ is still raised
|
55
|
+
# in case of argument mismatch.
|
56
|
+
#
|
57
|
+
# If +try+ is called without arguments it yields the receiver to a given
|
58
|
+
# block unless it is +nil+:
|
59
|
+
#
|
60
|
+
# @person.try do |p|
|
61
|
+
# ...
|
62
|
+
# end
|
63
|
+
#
|
64
|
+
# You can also call try with a block without accepting an argument, and the block
|
65
|
+
# will be instance_eval'ed instead:
|
66
|
+
#
|
67
|
+
# @person.try { upcase.truncate(50) }
|
68
|
+
#
|
69
|
+
# Please also note that +try+ is defined on +Object+. Therefore, it won't work
|
70
|
+
# with instances of classes that do not have +Object+ among their ancestors,
|
71
|
+
# like direct subclasses of +BasicObject+.
|
8
72
|
def try(method_name = nil, *args, &block)
|
9
73
|
if method_name.nil? && block_given?
|
10
74
|
if block.arity.zero?
|
@@ -17,6 +81,18 @@ module AppInfo
|
|
17
81
|
end
|
18
82
|
end
|
19
83
|
|
84
|
+
##
|
85
|
+
# :method: try!
|
86
|
+
#
|
87
|
+
# :call-seq:
|
88
|
+
# try!(*a, &b)
|
89
|
+
#
|
90
|
+
# Same as #try, but raises a +NoMethodError+ exception if the receiver is
|
91
|
+
# not +nil+ and does not implement the tried method.
|
92
|
+
#
|
93
|
+
# "a".try!(:upcase) # => "A"
|
94
|
+
# nil.try!(:upcase) # => nil
|
95
|
+
# 123.try!(:upcase) # => NoMethodError: undefined method `upcase' for 123:Integer
|
20
96
|
def try!(method_name = nil, *args, &block)
|
21
97
|
if method_name.nil? && block_given?
|
22
98
|
if block.arity.zero?
|
@@ -33,82 +109,4 @@ end
|
|
33
109
|
|
34
110
|
class Object
|
35
111
|
include AppInfo::Tryable
|
36
|
-
|
37
|
-
##
|
38
|
-
# :method: try
|
39
|
-
#
|
40
|
-
# :call-seq:
|
41
|
-
# try(*a, &b)
|
42
|
-
#
|
43
|
-
# Invokes the public method whose name goes as first argument just like
|
44
|
-
# +public_send+ does, except that if the receiver does not respond to it the
|
45
|
-
# call returns +nil+ rather than raising an exception.
|
46
|
-
#
|
47
|
-
# This method is defined to be able to write
|
48
|
-
#
|
49
|
-
# @person.try(:name)
|
50
|
-
#
|
51
|
-
# instead of
|
52
|
-
#
|
53
|
-
# @person.name if @person
|
54
|
-
#
|
55
|
-
# +try+ calls can be chained:
|
56
|
-
#
|
57
|
-
# @person.try(:spouse).try(:name)
|
58
|
-
#
|
59
|
-
# instead of
|
60
|
-
#
|
61
|
-
# @person.spouse.name if @person && @person.spouse
|
62
|
-
#
|
63
|
-
# +try+ will also return +nil+ if the receiver does not respond to the method:
|
64
|
-
#
|
65
|
-
# @person.try(:non_existing_method) # => nil
|
66
|
-
#
|
67
|
-
# instead of
|
68
|
-
#
|
69
|
-
# @person.non_existing_method if @person.respond_to?(:non_existing_method) # => nil
|
70
|
-
#
|
71
|
-
# +try+ returns +nil+ when called on +nil+ regardless of whether it responds
|
72
|
-
# to the method:
|
73
|
-
#
|
74
|
-
# nil.try(:to_i) # => nil, rather than 0
|
75
|
-
#
|
76
|
-
# Arguments and blocks are forwarded to the method if invoked:
|
77
|
-
#
|
78
|
-
# @posts.try(:each_slice, 2) do |a, b|
|
79
|
-
# ...
|
80
|
-
# end
|
81
|
-
#
|
82
|
-
# The number of arguments in the signature must match. If the object responds
|
83
|
-
# to the method the call is attempted and +ArgumentError+ is still raised
|
84
|
-
# in case of argument mismatch.
|
85
|
-
#
|
86
|
-
# If +try+ is called without arguments it yields the receiver to a given
|
87
|
-
# block unless it is +nil+:
|
88
|
-
#
|
89
|
-
# @person.try do |p|
|
90
|
-
# ...
|
91
|
-
# end
|
92
|
-
#
|
93
|
-
# You can also call try with a block without accepting an argument, and the block
|
94
|
-
# will be instance_eval'ed instead:
|
95
|
-
#
|
96
|
-
# @person.try { upcase.truncate(50) }
|
97
|
-
#
|
98
|
-
# Please also note that +try+ is defined on +Object+. Therefore, it won't work
|
99
|
-
# with instances of classes that do not have +Object+ among their ancestors,
|
100
|
-
# like direct subclasses of +BasicObject+.
|
101
|
-
|
102
|
-
##
|
103
|
-
# :method: try!
|
104
|
-
#
|
105
|
-
# :call-seq:
|
106
|
-
# try!(*a, &b)
|
107
|
-
#
|
108
|
-
# Same as #try, but raises a +NoMethodError+ exception if the receiver is
|
109
|
-
# not +nil+ and does not implement the tried method.
|
110
|
-
#
|
111
|
-
# "a".try!(:upcase) # => "A"
|
112
|
-
# nil.try!(:upcase) # => nil
|
113
|
-
# 123.try!(:upcase) # => NoMethodError: undefined method `upcase' for 123:Integer
|
114
112
|
end
|
data/lib/app_info/dsym.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'macho'
|
4
|
-
require 'app_info/util'
|
5
4
|
|
6
5
|
module AppInfo
|
7
6
|
# DSYM parser
|
@@ -130,8 +129,8 @@ module AppInfo
|
|
130
129
|
@file.filetype
|
131
130
|
end
|
132
131
|
|
133
|
-
def size(
|
134
|
-
return Util.
|
132
|
+
def size(human_size: false)
|
133
|
+
return Util.size_to_human_size(@size) if human_size
|
135
134
|
|
136
135
|
@size
|
137
136
|
end
|
@@ -152,7 +151,7 @@ module AppInfo
|
|
152
151
|
cpu_name: cpu_name,
|
153
152
|
cpu_type: cpu_type,
|
154
153
|
size: size,
|
155
|
-
|
154
|
+
human_size: size(human_size: true)
|
156
155
|
}
|
157
156
|
end
|
158
157
|
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'forwardable'
|
4
|
+
require 'cfpropertylist'
|
5
|
+
require 'app_info/png_uncrush'
|
6
|
+
|
7
|
+
module AppInfo
|
8
|
+
# iOS Info.plist parser
|
9
|
+
class InfoPlist
|
10
|
+
extend Forwardable
|
11
|
+
|
12
|
+
def initialize(file)
|
13
|
+
@file = file
|
14
|
+
end
|
15
|
+
|
16
|
+
def version
|
17
|
+
release_version || build_version
|
18
|
+
end
|
19
|
+
|
20
|
+
def build_version
|
21
|
+
info.try(:[], 'CFBundleVersion')
|
22
|
+
end
|
23
|
+
|
24
|
+
def release_version
|
25
|
+
info.try(:[], 'CFBundleShortVersionString')
|
26
|
+
end
|
27
|
+
|
28
|
+
def identifier
|
29
|
+
info.try(:[], 'CFBundleIdentifier')
|
30
|
+
end
|
31
|
+
alias bundle_id identifier
|
32
|
+
|
33
|
+
def name
|
34
|
+
display_name || bundle_name
|
35
|
+
end
|
36
|
+
|
37
|
+
def display_name
|
38
|
+
info.try(:[], 'CFBundleDisplayName')
|
39
|
+
end
|
40
|
+
|
41
|
+
def bundle_name
|
42
|
+
info.try(:[], 'CFBundleName')
|
43
|
+
end
|
44
|
+
|
45
|
+
def min_os_version
|
46
|
+
min_sdk_version || min_system_version
|
47
|
+
end
|
48
|
+
|
49
|
+
#
|
50
|
+
# Extract the Minimum OS Version from the Info.plist (iOS Only)
|
51
|
+
#
|
52
|
+
def min_sdk_version
|
53
|
+
info.try(:[], 'MinimumOSVersion')
|
54
|
+
end
|
55
|
+
|
56
|
+
#
|
57
|
+
# Extract the Minimum OS Version from the Info.plist (macOS Only)
|
58
|
+
#
|
59
|
+
def min_system_version
|
60
|
+
info.try(:[], 'LSMinimumSystemVersion')
|
61
|
+
end
|
62
|
+
|
63
|
+
def icons
|
64
|
+
@icons ||= ICON_KEYS[device_type]
|
65
|
+
end
|
66
|
+
|
67
|
+
def device_type
|
68
|
+
device_family = info.try(:[], 'UIDeviceFamily')
|
69
|
+
if device_family == [1]
|
70
|
+
AppInfo::Device::IPHONE
|
71
|
+
elsif device_family == [2]
|
72
|
+
AppInfo::Device::IPAD
|
73
|
+
elsif device_family == [1, 2]
|
74
|
+
AppInfo::Device::UNIVERSAL
|
75
|
+
elsif !info.try(:[], 'DTSDKName').nil? || !info.try(:[], 'DTPlatformName').nil?
|
76
|
+
AppInfo::Device::MACOS
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def iphone?
|
81
|
+
device_type == Device::IPHONE
|
82
|
+
end
|
83
|
+
|
84
|
+
def ipad?
|
85
|
+
device_type == Device::IPAD
|
86
|
+
end
|
87
|
+
|
88
|
+
def universal?
|
89
|
+
device_type == Device::UNIVERSAL
|
90
|
+
end
|
91
|
+
|
92
|
+
def macos?
|
93
|
+
device_type == Device::MACOS
|
94
|
+
end
|
95
|
+
|
96
|
+
def device_family
|
97
|
+
info.try(:[], 'UIDeviceFamily') || []
|
98
|
+
end
|
99
|
+
|
100
|
+
def release_type
|
101
|
+
if stored?
|
102
|
+
'Store'
|
103
|
+
else
|
104
|
+
build_type
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def [](key)
|
109
|
+
info.try(:[], key.to_s)
|
110
|
+
end
|
111
|
+
|
112
|
+
def_delegators :info, :to_h
|
113
|
+
|
114
|
+
def method_missing(method_name, *args, &block)
|
115
|
+
info.try(:[], Util.format_key(method_name)) ||
|
116
|
+
info.send(method_name) ||
|
117
|
+
super
|
118
|
+
end
|
119
|
+
|
120
|
+
def respond_to_missing?(method_name, *args)
|
121
|
+
info.key?(Util.format_key(method_name)) ||
|
122
|
+
info.respond_to?(method_name) ||
|
123
|
+
super
|
124
|
+
end
|
125
|
+
|
126
|
+
private
|
127
|
+
|
128
|
+
def info
|
129
|
+
return unless File.file?(@file)
|
130
|
+
|
131
|
+
@info ||= CFPropertyList.native_types(CFPropertyList::List.new(file: @file).value)
|
132
|
+
end
|
133
|
+
|
134
|
+
def app_path
|
135
|
+
@app_path ||= case device_type
|
136
|
+
when Device::MACOS
|
137
|
+
File.dirname(@file)
|
138
|
+
else
|
139
|
+
File.expand_path('../', @file)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
require 'forwardable'
|
4
4
|
|
5
5
|
module AppInfo
|
6
|
-
# iOS
|
6
|
+
# iOS Framework parser
|
7
7
|
class Framework
|
8
8
|
extend Forwardable
|
9
9
|
|
@@ -11,7 +11,7 @@ module AppInfo
|
|
11
11
|
files = Dir.glob(File.join(path, name.to_s, '*'))
|
12
12
|
return [] if files.empty?
|
13
13
|
|
14
|
-
files.each_with_object([]) do |file, obj|
|
14
|
+
files.sort.each_with_object([]) do |file, obj|
|
15
15
|
obj << new(file)
|
16
16
|
end
|
17
17
|
end
|
@@ -41,7 +41,7 @@ module AppInfo
|
|
41
41
|
end
|
42
42
|
|
43
43
|
def info
|
44
|
-
@info ||= InfoPlist.new(file)
|
44
|
+
@info ||= InfoPlist.new(File.join(file, 'Info.plist'))
|
45
45
|
end
|
46
46
|
|
47
47
|
def to_s
|