app-info 3.1.4 → 3.2.0
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/workflows/ci.yml +27 -6
- data/CHANGELOG.md +8 -1
- data/README.md +29 -1
- data/app_info.gemspec +2 -12
- data/lib/app_info/android.rb +5 -5
- data/lib/app_info/const.rb +81 -1
- data/lib/app_info/hap.rb +73 -0
- data/lib/app_info/happ.rb +46 -0
- data/lib/app_info/harmonyos.rb +65 -0
- data/lib/app_info/helper/file_type_detection.rb +111 -0
- data/lib/app_info/helper.rb +1 -0
- data/lib/app_info/info_plist.rb +19 -19
- data/lib/app_info/ipa.rb +3 -3
- data/lib/app_info/pack_info.rb +51 -0
- data/lib/app_info/pe.rb +2 -2
- data/lib/app_info/version.rb +1 -1
- data/lib/app_info.rb +10 -80
- metadata +11 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 114f4899c399d6a4b8091b798a6089538b157eccba40c428ae36e445f79191d6
|
4
|
+
data.tar.gz: 872061f173786d61f2dbad70a25405b71fc2b054ea023fb375394aed52febeeb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 435e4ce4855c42b94a5ef6a9bd12e208765691ad4d2ba1f0da92eb251e6fe12ed9f9aaa95ec8ba5fd3f53b1b4ff664fb8e8a1764b32992c5c0be220e51344ce1
|
7
|
+
data.tar.gz: 376b5e7ac14d0fa8a95d0bd0a6d2861532e937317ed3c6a1cea07bc3ebc7a4ac2b84b8648180a9ae298768e3b7e90335a055bf344a2a2f933a2106b2f853bb31
|
data/.github/workflows/ci.yml
CHANGED
@@ -8,15 +8,35 @@ on:
|
|
8
8
|
pull_request:
|
9
9
|
|
10
10
|
jobs:
|
11
|
+
linting:
|
12
|
+
runs-on: ubuntu-latest
|
13
|
+
|
14
|
+
steps:
|
15
|
+
- uses: actions/checkout@v4
|
16
|
+
|
17
|
+
- name: Setup Ruby 3.x
|
18
|
+
uses: ruby/setup-ruby@v1
|
19
|
+
with:
|
20
|
+
ruby-version: 3
|
21
|
+
bundler-cache: true
|
22
|
+
|
23
|
+
- name: Rubocop
|
24
|
+
run: bundle exec rubocop --format progress
|
25
|
+
|
11
26
|
test:
|
27
|
+
needs: [ linting ]
|
28
|
+
name: test ${{ matrix.ruby }}
|
29
|
+
runs-on: ubuntu-latest
|
12
30
|
strategy:
|
13
31
|
fail-fast: false
|
14
32
|
matrix:
|
15
33
|
ruby: [ ruby-3.1, ruby-3.2, ruby-3.3 ]
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
34
|
+
experimental: [false]
|
35
|
+
include:
|
36
|
+
- ruby: head
|
37
|
+
experimental: true
|
38
|
+
- ruby: truffleruby-head
|
39
|
+
experimental: true
|
20
40
|
steps:
|
21
41
|
- uses: actions/checkout@v4
|
22
42
|
|
@@ -25,5 +45,6 @@ jobs:
|
|
25
45
|
ruby-version: ${{ matrix.ruby }}
|
26
46
|
bundler-cache: true
|
27
47
|
|
28
|
-
- name:
|
29
|
-
|
48
|
+
- name: RSpec
|
49
|
+
continue-on-error: ${{ matrix.experimental }}
|
50
|
+
run: bundle exec rspec
|
data/CHANGELOG.md
CHANGED
@@ -9,6 +9,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|
9
9
|
|
10
10
|
> List all changes before release a new version.
|
11
11
|
|
12
|
+
## [3.2.0] (2024-09-09)
|
13
|
+
|
14
|
+
### Added
|
15
|
+
|
16
|
+
- Add HarmonyOS app support (basic). #[76](https://github.com/icyleaf/app-info/pull/76) Many thanks to @[InjoyDeng](https://github.com/InjoyDeng)
|
17
|
+
|
12
18
|
## [3.1.4] (2024-06-27)
|
13
19
|
|
14
20
|
### Added
|
@@ -355,7 +361,8 @@ Dropped Ruby 2.5 ~ 3.0 support (no changes required.).
|
|
355
361
|
|
356
362
|
- Updated dependency of CFPropertly list be a range between 2.3.4. (thanks @[cschroed](https://github.com/cschroed))
|
357
363
|
|
358
|
-
[Unreleased]: https://github.com/icyleaf/app-info/compare/v3.
|
364
|
+
[Unreleased]: https://github.com/icyleaf/app-info/compare/v3.2.0..HEAD
|
365
|
+
[3.2.0]: https://github.com/icyleaf/app-info/compare/v3.1.4...v3.2.0
|
359
366
|
[3.1.4]: https://github.com/icyleaf/app-info/compare/v3.1.2...v3.1.4
|
360
367
|
[3.1.2]: https://github.com/icyleaf/app-info/compare/v3.1.0...v3.1.2
|
361
368
|
[3.1.0]: https://github.com/icyleaf/app-info/compare/v3.0.0...v3.1.0
|
data/README.md
CHANGED
@@ -5,7 +5,8 @@
|
|
5
5
|
[](https://github.com/icyleaf/app_info/actions/workflows/ci.yml)
|
6
6
|
[](LICENSE)
|
7
7
|
|
8
|
-
Teardown tool for mobile app (ipa, apk
|
8
|
+
Teardown tool for mobile app (iOS: ipa, Android: apk/aab, HarmonyOS: .hap/.app file), macOS app, dSYM.zip file and Windows PE file.
|
9
|
+
|
9
10
|
Analysis metedata like version, name, icon etc.
|
10
11
|
|
11
12
|
## Support
|
@@ -17,6 +18,9 @@ Analysis metedata like version, name, icon etc.
|
|
17
18
|
- `.ipa`
|
18
19
|
- `Info.plist` file
|
19
20
|
- `.mobileprovision`/`.provisionprofile` file
|
21
|
+
- HarmonyOS file (basic)
|
22
|
+
- `.hap`
|
23
|
+
- `.app`
|
20
24
|
- macOS App file (archived by starnd pkzip format)
|
21
25
|
- `.app.zip`
|
22
26
|
- dSYMs file (archived by starnd pkzip format)
|
@@ -246,6 +250,30 @@ android.signatures
|
|
246
250
|
# => [...]
|
247
251
|
```
|
248
252
|
|
253
|
+
### HarmonyOS
|
254
|
+
|
255
|
+
Accept `.hap` and `.app` HarmonyOS file. Only metabase, except resources mapping.
|
256
|
+
|
257
|
+
```ruby
|
258
|
+
hap = AppInfo.parse('app.hap')
|
259
|
+
|
260
|
+
# get app file size
|
261
|
+
android.size
|
262
|
+
# => 2013213
|
263
|
+
|
264
|
+
# get app file size in human reable.
|
265
|
+
android.size(human_size: true)
|
266
|
+
# => 21 MB
|
267
|
+
|
268
|
+
# get app release version
|
269
|
+
android.release_version
|
270
|
+
# => 1.0
|
271
|
+
|
272
|
+
# get app package name
|
273
|
+
android.bundle_id
|
274
|
+
# => com.icyleaf.AppInfoDemo
|
275
|
+
```
|
276
|
+
|
249
277
|
### macOS
|
250
278
|
|
251
279
|
Only accept zipped macOS file.
|
data/app_info.gemspec
CHANGED
@@ -10,8 +10,8 @@ Gem::Specification.new do |spec|
|
|
10
10
|
spec.authors = ['icyleaf']
|
11
11
|
spec.email = ['icyleaf.cn@gmail.com']
|
12
12
|
|
13
|
-
spec.summary = 'Teardown tool for
|
14
|
-
spec.description = 'Teardown tool for ipa
|
13
|
+
spec.summary = 'Teardown tool for all most app, analysis metedata like version, name, icon'
|
14
|
+
spec.description = 'Teardown tool for ipa, apk, aab, hap mobile files and Windows, macOS and dSYM file, even support for info.plist and .mobileprovision files'
|
15
15
|
spec.homepage = 'http://github.com/icyleaf/app-info'
|
16
16
|
spec.license = 'MIT'
|
17
17
|
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
@@ -34,14 +34,4 @@ Gem::Specification.new do |spec|
|
|
34
34
|
|
35
35
|
spec.add_development_dependency 'bundler', '>= 1.12'
|
36
36
|
spec.add_development_dependency 'rake', '>= 10.0'
|
37
|
-
|
38
|
-
spec.post_install_message = <<~ENDBANNER
|
39
|
-
AppInfo 3.0 was out!
|
40
|
-
**********************
|
41
|
-
The public API of some AppInfo classes has been changed.
|
42
|
-
|
43
|
-
Please ensure that your Gemfiles and .gemspecs are suitably restrictive
|
44
|
-
to avoid an unexpected breakage when 3.0 is released (e.g. ~> 2.8.5).
|
45
|
-
See https://github.com/icyleaf/app_info for details.
|
46
|
-
ENDBANNER
|
47
37
|
end
|
data/lib/app_info/android.rb
CHANGED
@@ -36,15 +36,15 @@ module AppInfo
|
|
36
36
|
# @return [Symbol] {Device}
|
37
37
|
def device
|
38
38
|
if watch?
|
39
|
-
Device::WATCH
|
39
|
+
Device::Google::WATCH
|
40
40
|
elsif television?
|
41
|
-
Device::TELEVISION
|
41
|
+
Device::Google::TELEVISION
|
42
42
|
elsif automotive?
|
43
|
-
Device::AUTOMOTIVE
|
43
|
+
Device::Google::AUTOMOTIVE
|
44
44
|
elsif tablet?
|
45
|
-
Device::TABLET
|
45
|
+
Device::Google::TABLET
|
46
46
|
else
|
47
|
-
Device::PHONE
|
47
|
+
Device::Google::PHONE
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
data/lib/app_info/const.rb
CHANGED
@@ -23,6 +23,11 @@ module AppInfo
|
|
23
23
|
AAB = :aab
|
24
24
|
PROGUARD = :proguard
|
25
25
|
|
26
|
+
# HarmonyOS
|
27
|
+
|
28
|
+
HAP = :hap
|
29
|
+
HAPP = :app
|
30
|
+
|
26
31
|
# Windows
|
27
32
|
|
28
33
|
PE = :pe
|
@@ -35,6 +40,7 @@ module AppInfo
|
|
35
40
|
APPLE = :apple
|
36
41
|
GOOGLE = :google
|
37
42
|
MICROSOFT = :microsoft
|
43
|
+
HUAWEI = :huawei
|
38
44
|
end
|
39
45
|
|
40
46
|
# Platform
|
@@ -44,36 +50,110 @@ module AppInfo
|
|
44
50
|
ANDROID = :android
|
45
51
|
APPLETV = :appletv
|
46
52
|
WINDOWS = :windows
|
53
|
+
HARMONYOS = :harmonyos
|
47
54
|
end
|
48
55
|
|
49
|
-
#
|
56
|
+
# Device Type
|
50
57
|
module Device
|
58
|
+
module Apple
|
59
|
+
# macOS
|
60
|
+
MACOS = :macos
|
61
|
+
|
62
|
+
# Apple iPhone
|
63
|
+
IPHONE = :iphone
|
64
|
+
# Apple iPad
|
65
|
+
IPAD = :ipad
|
66
|
+
# Apple Universal (iPhone and iPad)
|
67
|
+
UNIVERSAL = :universal
|
68
|
+
# Apple TV
|
69
|
+
APPLETV = :appletv
|
70
|
+
# Apple Watch (TODO: not implemented yet)
|
71
|
+
IWATCH = :iwatch
|
72
|
+
end
|
73
|
+
|
74
|
+
module Google
|
75
|
+
# Android Phone
|
76
|
+
PHONE = :phone
|
77
|
+
# Android Tablet (TODO: not implemented yet)
|
78
|
+
TABLET = :tablet
|
79
|
+
# Android Watch
|
80
|
+
WATCH = :watch
|
81
|
+
# Android TV
|
82
|
+
TELEVISION = :television
|
83
|
+
# Android Car Automotive
|
84
|
+
AUTOMOTIVE = :automotive
|
85
|
+
end
|
86
|
+
|
87
|
+
module Huawei
|
88
|
+
# HarmonyOS Default
|
89
|
+
DEFAULT = :default
|
90
|
+
# HarmonyOS Phone
|
91
|
+
PHONE = :phone
|
92
|
+
# HarmonyOS Tablet
|
93
|
+
TABLET = :tablet
|
94
|
+
# HarmonyOS TV
|
95
|
+
TV = :tv
|
96
|
+
# HarmonyOS wearable
|
97
|
+
WEARABLE = :wearable
|
98
|
+
# HarmonyOS Car
|
99
|
+
CAR = :car
|
100
|
+
# HarmonyOS 2-in-1 tablet and laptop
|
101
|
+
TWO_IN_ONE = :two_in_one
|
102
|
+
end
|
103
|
+
|
104
|
+
module Microsoft
|
105
|
+
# Windows
|
106
|
+
WINDOWS = :windows
|
107
|
+
end
|
108
|
+
|
109
|
+
# legacy consts
|
110
|
+
|
51
111
|
# macOS
|
112
|
+
# @deprecated Use {Device::Apple#MACOS} instead, this method will remove in 3.3.0.
|
52
113
|
MACOS = :macos
|
53
114
|
|
54
115
|
# Apple iPhone
|
116
|
+
# @deprecated Use {Device::Apple#IPHONE} instead, this method will remove in 3.3.0.
|
55
117
|
IPHONE = :iphone
|
118
|
+
|
56
119
|
# Apple iPad
|
120
|
+
# @deprecated Use {Device::Apple#IPAD} instead, this method will remove in 3.3.0.
|
57
121
|
IPAD = :ipad
|
122
|
+
|
58
123
|
# Apple Universal (iPhone and iPad)
|
124
|
+
# @deprecated Use {Device::Apple#UNIVERSAL} instead, this method will remove in 3.3.0.
|
59
125
|
UNIVERSAL = :universal
|
126
|
+
|
60
127
|
# Apple TV
|
128
|
+
# @deprecated Use {Device::Apple#APPLETV} instead, this method will remove in 3.3.0.
|
61
129
|
APPLETV = :appletv
|
130
|
+
|
62
131
|
# Apple Watch (TODO: not implemented yet)
|
132
|
+
# @deprecated Use {Device::Apple#IWATCH} instead, this method will remove in 3.3.0.
|
63
133
|
IWATCH = :iwatch
|
64
134
|
|
65
135
|
# Android Phone
|
136
|
+
# @deprecated Use {Device::Google#PHONE} instead, this method will remove in 3.3.0.
|
66
137
|
PHONE = :phone
|
138
|
+
|
67
139
|
# Android Tablet (TODO: not implemented yet)
|
140
|
+
# @deprecated Use {Device::Google#TABLET} instead, this method will remove in 3.3.0.
|
68
141
|
TABLET = :tablet
|
142
|
+
|
69
143
|
# Android Watch
|
144
|
+
# @deprecated Use {Device::Google#WATCH} instead, this method will remove in 3.3.0.
|
70
145
|
WATCH = :watch
|
146
|
+
|
71
147
|
# Android TV
|
148
|
+
# @deprecated Use {Device::Google#TELEVISION} instead, this method will remove in 3.3.0.
|
72
149
|
TELEVISION = :television
|
150
|
+
|
73
151
|
# Android Car Automotive
|
152
|
+
# @deprecated Use {Device::Google#AUTOMOTIVE} instead, this method will remove in 3.3.0.
|
74
153
|
AUTOMOTIVE = :automotive
|
75
154
|
|
76
155
|
# Windows
|
156
|
+
# @deprecated Use {Device::Microsoft#WINDOWS} instead, this method will remove in 3.3.0.
|
77
157
|
WINDOWS = :windows
|
78
158
|
end
|
79
159
|
end
|
data/lib/app_info/hap.rb
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AppInfo
|
4
|
+
# Parse HAP file parser
|
5
|
+
class HAP < HarmonyOS
|
6
|
+
# Full icons metadata
|
7
|
+
# @example
|
8
|
+
# ipa.icons
|
9
|
+
# # => [
|
10
|
+
# # {
|
11
|
+
# # name: 'icon.png',
|
12
|
+
# # file: '/path/to/icon.png',
|
13
|
+
# # uncrushed_file: '/path/to/uncrushed_icon.png',
|
14
|
+
# # dimensions: [64, 64]
|
15
|
+
# # },
|
16
|
+
# # {
|
17
|
+
# # name: 'icon1.png',
|
18
|
+
# # file: '/path/to/icon1.png',
|
19
|
+
# # uncrushed_file: '/path/to/uncrushed_icon1.png',
|
20
|
+
# # dimensions: [120, 120]
|
21
|
+
# # }
|
22
|
+
# # ]
|
23
|
+
# @return [Array<Hash{Symbol => String, Array<Integer>}>] icons paths of icons
|
24
|
+
def icons
|
25
|
+
@icons ||= icons_path.each_with_object([]) do |file, obj|
|
26
|
+
obj << {
|
27
|
+
name: ::File.basename(file),
|
28
|
+
file: file,
|
29
|
+
uncrushed_file: file,
|
30
|
+
dimensions: ImageSize.path(file).size
|
31
|
+
}
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# @return [Array<String>]
|
36
|
+
def icons_path
|
37
|
+
@icons_path ||= [::File.join(contents, 'resources', 'base', 'media', 'app_icon.png')]
|
38
|
+
end
|
39
|
+
|
40
|
+
# @return [JSON]
|
41
|
+
def module_info
|
42
|
+
@module_info ||= JSON.parse(::File.read(module_info_path))
|
43
|
+
end
|
44
|
+
|
45
|
+
# @return [String]
|
46
|
+
def module_info_path
|
47
|
+
@module_info_path ||= ::File.join(contents, 'module.json')
|
48
|
+
end
|
49
|
+
|
50
|
+
# @return [String]
|
51
|
+
def name
|
52
|
+
# TODO: The application display name should be determined by looking up
|
53
|
+
# the value of the variable named in the "label" field of the "module.json"
|
54
|
+
# file within the "resources.index" file.
|
55
|
+
pack_info.bundle_name
|
56
|
+
end
|
57
|
+
|
58
|
+
def clear!
|
59
|
+
return unless @contents
|
60
|
+
|
61
|
+
FileUtils.rm_rf(@contents)
|
62
|
+
|
63
|
+
@pack_info = nil
|
64
|
+
@info_path = nil
|
65
|
+
@contents = nil
|
66
|
+
|
67
|
+
@module_info_path = nil
|
68
|
+
@module_info = nil
|
69
|
+
@icons_path = nil
|
70
|
+
@icons = nil
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AppInfo
|
4
|
+
# parser for HarmonyOS .APP file
|
5
|
+
class HAPP < HarmonyOS
|
6
|
+
def_delegators :default_entry, :icons
|
7
|
+
# @return [HAP]
|
8
|
+
def default_entry
|
9
|
+
hap_path = ::File.join(contents, "#{default_entry_name}.hap")
|
10
|
+
@default_entry ||= HAP.new(hap_path)
|
11
|
+
end
|
12
|
+
|
13
|
+
# @return [String]
|
14
|
+
def default_entry_name
|
15
|
+
return @default_entry_name if @default_entry_name
|
16
|
+
|
17
|
+
pack_info.packages.each do |package|
|
18
|
+
if package['moduleType'] == 'entry' && package['deliveryWithInstall']
|
19
|
+
@default_entry_name ||= package['name']
|
20
|
+
break
|
21
|
+
end
|
22
|
+
end
|
23
|
+
@default_entry_name
|
24
|
+
end
|
25
|
+
|
26
|
+
# @return [String]
|
27
|
+
def name
|
28
|
+
default_entry.name
|
29
|
+
end
|
30
|
+
|
31
|
+
def clear!
|
32
|
+
return unless @contents
|
33
|
+
|
34
|
+
FileUtils.rm_rf(@contents)
|
35
|
+
|
36
|
+
@pack_info = nil
|
37
|
+
@info_path = nil
|
38
|
+
@contents = nil
|
39
|
+
|
40
|
+
@default_entry_name = nil
|
41
|
+
@default_entry&.clear!
|
42
|
+
|
43
|
+
@default_entry = nil
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AppInfo
|
4
|
+
# HarmonyOS base parser for hap and app file
|
5
|
+
class HarmonyOS < File
|
6
|
+
extend Forwardable
|
7
|
+
include Helper::HumanFileSize
|
8
|
+
include Helper::Archive
|
9
|
+
|
10
|
+
def_delegators :pack_info, :build_version, :release_version, :bundle_id
|
11
|
+
|
12
|
+
# return file size
|
13
|
+
# @example Read file size in integer
|
14
|
+
# aab.size # => 3618865
|
15
|
+
#
|
16
|
+
# @example Read file size in human readabale
|
17
|
+
# aab.size(human_size: true) # => '3.45 MB'
|
18
|
+
#
|
19
|
+
# @param [Boolean] human_size Convert integer value to human readable.
|
20
|
+
# @return [Integer, String]
|
21
|
+
def size(human_size: false)
|
22
|
+
file_to_human_size(@file, human_size: human_size)
|
23
|
+
end
|
24
|
+
|
25
|
+
# @return [Symbol] {Manufacturer}
|
26
|
+
def manufacturer
|
27
|
+
Manufacturer::HUAWEI
|
28
|
+
end
|
29
|
+
|
30
|
+
# @return [Symbol] {Platform}
|
31
|
+
def platform
|
32
|
+
Platform::HARMONYOS
|
33
|
+
end
|
34
|
+
|
35
|
+
# @return [Symbol] {Device}
|
36
|
+
def device
|
37
|
+
Device::Huawei::DEFAULT
|
38
|
+
end
|
39
|
+
|
40
|
+
# @return [PackInfo]
|
41
|
+
def pack_info
|
42
|
+
@pack_info ||= PackInfo.new(info_path)
|
43
|
+
end
|
44
|
+
|
45
|
+
# @return [String]
|
46
|
+
def info_path
|
47
|
+
@info_path ||= ::File.join(contents, 'pack.info')
|
48
|
+
end
|
49
|
+
|
50
|
+
# @return [String] unzipped file path
|
51
|
+
def contents
|
52
|
+
@contents ||= unarchive(@file, prefix: format.to_s)
|
53
|
+
end
|
54
|
+
|
55
|
+
# @abstract Subclass and override {#name} to implement.
|
56
|
+
def name
|
57
|
+
not_implemented_error!(__method__)
|
58
|
+
end
|
59
|
+
|
60
|
+
# @abstract Subclass and override {#clear!} to implement.
|
61
|
+
def clear!
|
62
|
+
not_implemented_error!(__method__)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AppInfo::Helper
|
4
|
+
module FileTypeDetection
|
5
|
+
# Detect file type by reading file header
|
6
|
+
#
|
7
|
+
# TODO: This can be better solution, if anyone knows, tell me please.
|
8
|
+
def file_type(file)
|
9
|
+
header_hex = ::File.read(file, 100)
|
10
|
+
case header_hex
|
11
|
+
when ZIP_RETGEX
|
12
|
+
detect_zip_file(file)
|
13
|
+
when PE_REGEX
|
14
|
+
AppInfo::Format::PE
|
15
|
+
when PLIST_REGEX, BPLIST_REGEX
|
16
|
+
AppInfo::Format::MOBILEPROVISION
|
17
|
+
else
|
18
|
+
AppInfo::Format::UNKNOWN
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
# Detect file type for zip files
|
25
|
+
def detect_zip_file(file)
|
26
|
+
Zip.warn_invalid_date = false
|
27
|
+
zip_file = Zip::File.open(file)
|
28
|
+
|
29
|
+
return AppInfo::Format::PROGUARD if proguard_clues?(zip_file)
|
30
|
+
return AppInfo::Format::APK if apk_clues?(zip_file)
|
31
|
+
return AppInfo::Format::AAB if aab_clues?(zip_file)
|
32
|
+
return AppInfo::Format::MACOS if macos_clues?(zip_file)
|
33
|
+
return AppInfo::Format::PE if pe_clues?(zip_file)
|
34
|
+
return AppInfo::Format::HAP if hap_clues?(zip_file)
|
35
|
+
return AppInfo::Format::HAPP if happ_clues?(zip_file)
|
36
|
+
return AppInfo::Format::UNKNOWN unless clue = other_clues?(zip_file)
|
37
|
+
|
38
|
+
clue
|
39
|
+
ensure
|
40
|
+
zip_file.close
|
41
|
+
end
|
42
|
+
|
43
|
+
# Check for Proguard clues in zip file
|
44
|
+
def proguard_clues?(zip_file)
|
45
|
+
!zip_file.glob('*mapping*.txt').empty?
|
46
|
+
end
|
47
|
+
|
48
|
+
# Check for APK clues in zip file
|
49
|
+
def apk_clues?(zip_file)
|
50
|
+
!zip_file.find_entry('AndroidManifest.xml').nil? &&
|
51
|
+
!zip_file.find_entry('classes.dex').nil?
|
52
|
+
end
|
53
|
+
|
54
|
+
# Check for AAB clues in zip file
|
55
|
+
def aab_clues?(zip_file)
|
56
|
+
!zip_file.find_entry('base/manifest/AndroidManifest.xml').nil? &&
|
57
|
+
!zip_file.find_entry('BundleConfig.pb').nil?
|
58
|
+
end
|
59
|
+
|
60
|
+
# Check for macOS clues in zip file
|
61
|
+
def macos_clues?(zip_file)
|
62
|
+
!zip_file.glob('*/Contents/MacOS/*').empty? &&
|
63
|
+
!zip_file.glob('*/Contents/Info.plist').empty?
|
64
|
+
end
|
65
|
+
|
66
|
+
# Check for PE clues in zip file
|
67
|
+
def pe_clues?(zip_file)
|
68
|
+
!zip_file.glob('*.exe').empty?
|
69
|
+
end
|
70
|
+
|
71
|
+
# Check for HAP clues in zip file
|
72
|
+
def hap_clues?(zip_file)
|
73
|
+
!zip_file.find_entry('pack.info').nil? && !zip_file.find_entry('module.json').nil?
|
74
|
+
end
|
75
|
+
|
76
|
+
# Check for HAPP clues in zip file
|
77
|
+
def happ_clues?(zip_file)
|
78
|
+
pack_info_count = 0
|
79
|
+
hap_count = 0
|
80
|
+
|
81
|
+
zip_file.each do |f|
|
82
|
+
path = f.name
|
83
|
+
|
84
|
+
if path == 'pack.info'
|
85
|
+
pack_info_count += 1
|
86
|
+
elsif path.end_with?('.hap')
|
87
|
+
hap_count += 1
|
88
|
+
else
|
89
|
+
return false
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
pack_info_count == 1 && hap_count >= 1
|
94
|
+
end
|
95
|
+
|
96
|
+
# Check for other clues in zip file
|
97
|
+
def other_clues?(zip_file)
|
98
|
+
zip_file.each do |f|
|
99
|
+
path = f.name
|
100
|
+
|
101
|
+
return AppInfo::Format::IPA if path.include?('Payload/') && path.end_with?('Info.plist')
|
102
|
+
return AppInfo::Format::DSYM if path.include?('Contents/Resources/DWARF/')
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
ZIP_RETGEX = /^\x50\x4b\x03\x04/.freeze
|
107
|
+
PE_REGEX = /^MZ/.freeze
|
108
|
+
PLIST_REGEX = /\x3C\x3F\x78\x6D\x6C/.freeze
|
109
|
+
BPLIST_REGEX = /^\x62\x70\x6C\x69\x73\x74/.freeze
|
110
|
+
end
|
111
|
+
end
|
data/lib/app_info/helper.rb
CHANGED
data/lib/app_info/info_plist.rb
CHANGED
@@ -11,10 +11,10 @@ module AppInfo
|
|
11
11
|
|
12
12
|
# Icon Key
|
13
13
|
ICON_KEYS = {
|
14
|
-
Device::IPHONE => ['CFBundleIcons'],
|
15
|
-
Device::IPAD => ['CFBundleIcons~ipad'],
|
16
|
-
Device::UNIVERSAL => ['CFBundleIcons', 'CFBundleIcons~ipad'],
|
17
|
-
Device::MACOS => %w[CFBundleIconFile CFBundleIconName]
|
14
|
+
Device::Apple::IPHONE => ['CFBundleIcons'],
|
15
|
+
Device::Apple::IPAD => ['CFBundleIcons~ipad'],
|
16
|
+
Device::Apple::UNIVERSAL => ['CFBundleIcons', 'CFBundleIcons~ipad'],
|
17
|
+
Device::Apple::MACOS => %w[CFBundleIconFile CFBundleIconName]
|
18
18
|
}.freeze
|
19
19
|
|
20
20
|
# @return [Symbol] {Manufacturer}
|
@@ -25,11 +25,11 @@ module AppInfo
|
|
25
25
|
# @return [Symbol] {Platform}
|
26
26
|
def platform
|
27
27
|
case device
|
28
|
-
when Device::MACOS
|
28
|
+
when Device::Apple::MACOS
|
29
29
|
Platform::MACOS
|
30
|
-
when Device::IPHONE, Device::IPAD, Device::UNIVERSAL
|
30
|
+
when Device::Apple::IPHONE, Device::Apple::IPAD, Device::Apple::UNIVERSAL
|
31
31
|
Platform::IOS
|
32
|
-
when Device::APPLETV
|
32
|
+
when Device::Apple::APPLETV
|
33
33
|
Platform::APPLETV
|
34
34
|
end
|
35
35
|
end
|
@@ -37,17 +37,17 @@ module AppInfo
|
|
37
37
|
# @return [Symbol] {Device}
|
38
38
|
def device
|
39
39
|
if device_family == [1]
|
40
|
-
Device::IPHONE
|
40
|
+
Device::Apple::IPHONE
|
41
41
|
elsif device_family == [2]
|
42
|
-
Device::IPAD
|
42
|
+
Device::Apple::IPAD
|
43
43
|
elsif device_family == [1, 2]
|
44
|
-
Device::UNIVERSAL
|
44
|
+
Device::Apple::UNIVERSAL
|
45
45
|
elsif device_family == [3]
|
46
|
-
Device::APPLETV
|
46
|
+
Device::Apple::APPLETV
|
47
47
|
elsif device_family == [6]
|
48
|
-
Device::APPMACOSLETV
|
48
|
+
Device::Apple::APPMACOSLETV
|
49
49
|
elsif !info.try(:[], 'DTSDKName').nil? || !info.try(:[], 'DTManufacturerName').nil?
|
50
|
-
Device::MACOS
|
50
|
+
Device::Apple::MACOS
|
51
51
|
else
|
52
52
|
raise NotImplementedError, "Unkonwn device: #{device_family}"
|
53
53
|
end
|
@@ -113,27 +113,27 @@ module AppInfo
|
|
113
113
|
|
114
114
|
# @return [Boolean]
|
115
115
|
def iphone?
|
116
|
-
device == Device::IPHONE
|
116
|
+
device == Device::Apple::IPHONE
|
117
117
|
end
|
118
118
|
|
119
119
|
# @return [Boolean]
|
120
120
|
def ipad?
|
121
|
-
device == Device::IPAD
|
121
|
+
device == Device::Apple::IPAD
|
122
122
|
end
|
123
123
|
|
124
124
|
# @return [Boolean]
|
125
125
|
def universal?
|
126
|
-
device == Device::UNIVERSAL
|
126
|
+
device == Device::Apple::UNIVERSAL
|
127
127
|
end
|
128
128
|
|
129
129
|
# @return [Boolean]
|
130
130
|
def macos?
|
131
|
-
device == Device::MACOS
|
131
|
+
device == Device::Apple::MACOS
|
132
132
|
end
|
133
133
|
|
134
134
|
# @return [Boolean]
|
135
135
|
def appletv?
|
136
|
-
device == Device::APPLETV
|
136
|
+
device == Device::Apple::APPLETV
|
137
137
|
end
|
138
138
|
|
139
139
|
# @return [Array<String>]
|
@@ -212,7 +212,7 @@ module AppInfo
|
|
212
212
|
|
213
213
|
def app_path
|
214
214
|
@app_path ||= case device
|
215
|
-
when Device::MACOS
|
215
|
+
when Device::Apple::MACOS
|
216
216
|
::File.dirname(@file)
|
217
217
|
else
|
218
218
|
::File.expand_path('../', @file)
|
data/lib/app_info/ipa.rb
CHANGED
@@ -149,11 +149,11 @@ module AppInfo
|
|
149
149
|
|
150
150
|
def icon_keys
|
151
151
|
@icon_keys ||= case device
|
152
|
-
when Device::IPHONE, Device::APPLETV
|
152
|
+
when Device::Apple::IPHONE, Device::Apple::APPLETV
|
153
153
|
[IPHONE_KEY]
|
154
|
-
when Device::IPAD
|
154
|
+
when Device::Apple::IPAD
|
155
155
|
[IPAD_KEY]
|
156
|
-
when Device::UNIVERSAL
|
156
|
+
when Device::Apple::UNIVERSAL
|
157
157
|
[IPHONE_KEY, IPAD_KEY]
|
158
158
|
end
|
159
159
|
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module AppInfo
|
6
|
+
# HarmonyOS pack.info parser
|
7
|
+
class PackInfo < File
|
8
|
+
# @return [String]
|
9
|
+
def version_code
|
10
|
+
app['version']['code']
|
11
|
+
end
|
12
|
+
alias build_version version_code
|
13
|
+
|
14
|
+
# @return [String]
|
15
|
+
def version_name
|
16
|
+
app['version']['name']
|
17
|
+
end
|
18
|
+
alias release_version version_name
|
19
|
+
|
20
|
+
# @return [String]
|
21
|
+
def bundle_name
|
22
|
+
app['bundleName']
|
23
|
+
end
|
24
|
+
alias bundle_id bundle_name
|
25
|
+
|
26
|
+
# @return [JSON]
|
27
|
+
def app
|
28
|
+
@app ||= summary['app']
|
29
|
+
end
|
30
|
+
|
31
|
+
# @return [Array<JSON>]
|
32
|
+
def modules
|
33
|
+
@modules ||= summary['modules']
|
34
|
+
end
|
35
|
+
|
36
|
+
# @return [JSON]
|
37
|
+
def summary
|
38
|
+
@summary ||= content['summary']
|
39
|
+
end
|
40
|
+
|
41
|
+
# @return [Array<JSON>]
|
42
|
+
def packages
|
43
|
+
@packages ||= content['packages']
|
44
|
+
end
|
45
|
+
|
46
|
+
# @return [JSON]
|
47
|
+
def content
|
48
|
+
JSON.parse(::File.read(@file))
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/lib/app_info/pe.rb
CHANGED
@@ -36,7 +36,7 @@ module AppInfo
|
|
36
36
|
|
37
37
|
# @return [Symbol] {Device}
|
38
38
|
def device
|
39
|
-
Device::WINDOWS
|
39
|
+
Device::Microsoft::WINDOWS
|
40
40
|
end
|
41
41
|
|
42
42
|
# return file size
|
@@ -189,7 +189,7 @@ module AppInfo
|
|
189
189
|
def binary_file
|
190
190
|
@binary_file ||= lambda {
|
191
191
|
file_io = ::File.open(@file, 'rb')
|
192
|
-
return @file unless file_io.read(100) =~
|
192
|
+
return @file unless file_io.read(100) =~ Helper::FileTypeDetection::ZIP_RETGEX
|
193
193
|
|
194
194
|
zip_file = Zip::File.open(@file)
|
195
195
|
zip_entry = zip_file.glob('*.exe').first
|
data/lib/app_info/version.rb
CHANGED
data/lib/app_info.rb
CHANGED
@@ -10,6 +10,7 @@ require 'app_info/error'
|
|
10
10
|
require 'app_info/file'
|
11
11
|
require 'app_info/info_plist'
|
12
12
|
require 'app_info/mobile_provision'
|
13
|
+
require 'app_info/pack_info'
|
13
14
|
|
14
15
|
require 'app_info/apple'
|
15
16
|
require 'app_info/macos'
|
@@ -21,16 +22,22 @@ require 'app_info/android'
|
|
21
22
|
require 'app_info/apk'
|
22
23
|
require 'app_info/aab'
|
23
24
|
|
25
|
+
require 'app_info/harmonyos'
|
26
|
+
require 'app_info/happ'
|
27
|
+
require 'app_info/hap'
|
28
|
+
|
24
29
|
require 'app_info/proguard'
|
25
30
|
require 'app_info/dsym'
|
26
31
|
|
27
32
|
require 'app_info/pe'
|
28
33
|
|
29
|
-
# fix
|
34
|
+
# fix invalid date format warnings
|
30
35
|
Zip.warn_invalid_date = false
|
31
36
|
|
32
37
|
# AppInfo Module
|
33
38
|
module AppInfo
|
39
|
+
extend Helper::FileTypeDetection
|
40
|
+
|
34
41
|
class << self
|
35
42
|
# Get a new parser for automatic
|
36
43
|
def parse(file)
|
@@ -40,6 +47,8 @@ module AppInfo
|
|
40
47
|
when Format::IPA then IPA.new(file)
|
41
48
|
when Format::APK then APK.new(file)
|
42
49
|
when Format::AAB then AAB.new(file)
|
50
|
+
when Format::HAP then HAP.new(file)
|
51
|
+
when Format::HAPP then HAPP.new(file)
|
43
52
|
when Format::MOBILEPROVISION then MobileProvision.new(file)
|
44
53
|
when Format::DSYM then DSYM.new(file)
|
45
54
|
when Format::PROGUARD then Proguard.new(file)
|
@@ -61,89 +70,10 @@ module AppInfo
|
|
61
70
|
file_type(file) != Format::UNKNOWN
|
62
71
|
end
|
63
72
|
|
64
|
-
# Detect file type by read file header
|
65
|
-
#
|
66
|
-
# TODO: This can be better solution, if anyone knows, tell me please.
|
67
|
-
def file_type(file)
|
68
|
-
header_hex = ::File.read(file, 100)
|
69
|
-
case header_hex
|
70
|
-
when ZIP_RETGEX
|
71
|
-
detect_zip_file(file)
|
72
|
-
when PE_REGEX
|
73
|
-
Format::PE
|
74
|
-
when PLIST_REGEX, BPLIST_REGEX
|
75
|
-
Format::MOBILEPROVISION
|
76
|
-
else
|
77
|
-
Format::UNKNOWN
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
73
|
def logger
|
82
74
|
@logger ||= Logger.new($stdout, level: :warn)
|
83
75
|
end
|
84
76
|
|
85
77
|
attr_writer :logger
|
86
|
-
|
87
|
-
private
|
88
|
-
|
89
|
-
# :nodoc:
|
90
|
-
def detect_zip_file(file)
|
91
|
-
Zip.warn_invalid_date = false
|
92
|
-
zip_file = Zip::File.open(file)
|
93
|
-
|
94
|
-
return Format::PROGUARD if proguard_clues?(zip_file)
|
95
|
-
return Format::APK if apk_clues?(zip_file)
|
96
|
-
return Format::AAB if aab_clues?(zip_file)
|
97
|
-
return Format::MACOS if macos_clues?(zip_file)
|
98
|
-
return Format::PE if pe_clues?(zip_file)
|
99
|
-
return Format::UNKNOWN unless clue = other_clues?(zip_file)
|
100
|
-
|
101
|
-
clue
|
102
|
-
ensure
|
103
|
-
zip_file.close
|
104
|
-
end
|
105
|
-
|
106
|
-
# :nodoc:
|
107
|
-
def proguard_clues?(zip_file)
|
108
|
-
!zip_file.glob('*mapping*.txt').empty?
|
109
|
-
end
|
110
|
-
|
111
|
-
# :nodoc:
|
112
|
-
def apk_clues?(zip_file)
|
113
|
-
!zip_file.find_entry('AndroidManifest.xml').nil? &&
|
114
|
-
!zip_file.find_entry('classes.dex').nil?
|
115
|
-
end
|
116
|
-
|
117
|
-
# :nodoc:
|
118
|
-
def aab_clues?(zip_file)
|
119
|
-
!zip_file.find_entry('base/manifest/AndroidManifest.xml').nil? &&
|
120
|
-
!zip_file.find_entry('BundleConfig.pb').nil?
|
121
|
-
end
|
122
|
-
|
123
|
-
# :nodoc:
|
124
|
-
def macos_clues?(zip_file)
|
125
|
-
!zip_file.glob('*/Contents/MacOS/*').empty? &&
|
126
|
-
!zip_file.glob('*/Contents/Info.plist').empty?
|
127
|
-
end
|
128
|
-
|
129
|
-
# :nodoc:
|
130
|
-
def pe_clues?(zip_file)
|
131
|
-
!zip_file.glob('*.exe').empty?
|
132
|
-
end
|
133
|
-
|
134
|
-
# :nodoc:
|
135
|
-
def other_clues?(zip_file)
|
136
|
-
zip_file.each do |f|
|
137
|
-
path = f.name
|
138
|
-
|
139
|
-
return Format::IPA if path.include?('Payload/') && path.end_with?('Info.plist')
|
140
|
-
return Format::DSYM if path.include?('Contents/Resources/DWARF/')
|
141
|
-
end
|
142
|
-
end
|
143
78
|
end
|
144
|
-
|
145
|
-
ZIP_RETGEX = /^\x50\x4b\x03\x04/.freeze
|
146
|
-
PE_REGEX = /^MZ/.freeze
|
147
|
-
PLIST_REGEX = /\x3C\x3F\x78\x6D\x6C/.freeze
|
148
|
-
BPLIST_REGEX = /^\x62\x70\x6C\x69\x73\x74/.freeze
|
149
79
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: app-info
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- icyleaf
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-09-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: CFPropertyList
|
@@ -234,8 +234,8 @@ dependencies:
|
|
234
234
|
- - ">="
|
235
235
|
- !ruby/object:Gem::Version
|
236
236
|
version: '10.0'
|
237
|
-
description: Teardown tool for ipa
|
238
|
-
and .mobileprovision files
|
237
|
+
description: Teardown tool for ipa, apk, aab, hap mobile files and Windows, macOS
|
238
|
+
and dSYM file, even support for info.plist and .mobileprovision files
|
239
239
|
email:
|
240
240
|
- icyleaf.cn@gmail.com
|
241
241
|
executables:
|
@@ -280,9 +280,13 @@ files:
|
|
280
280
|
- lib/app_info/dsym/macho.rb
|
281
281
|
- lib/app_info/error.rb
|
282
282
|
- lib/app_info/file.rb
|
283
|
+
- lib/app_info/hap.rb
|
284
|
+
- lib/app_info/happ.rb
|
285
|
+
- lib/app_info/harmonyos.rb
|
283
286
|
- lib/app_info/helper.rb
|
284
287
|
- lib/app_info/helper/archive.rb
|
285
288
|
- lib/app_info/helper/file_size.rb
|
289
|
+
- lib/app_info/helper/file_type_detection.rb
|
286
290
|
- lib/app_info/helper/generate_class.rb
|
287
291
|
- lib/app_info/helper/protobuf.rb
|
288
292
|
- lib/app_info/helper/signatures.rb
|
@@ -292,6 +296,7 @@ files:
|
|
292
296
|
- lib/app_info/ipa/plugin.rb
|
293
297
|
- lib/app_info/macos.rb
|
294
298
|
- lib/app_info/mobile_provision.rb
|
299
|
+
- lib/app_info/pack_info.rb
|
295
300
|
- lib/app_info/pe.rb
|
296
301
|
- lib/app_info/png_uncrush.rb
|
297
302
|
- lib/app_info/proguard.rb
|
@@ -308,14 +313,7 @@ homepage: http://github.com/icyleaf/app-info
|
|
308
313
|
licenses:
|
309
314
|
- MIT
|
310
315
|
metadata: {}
|
311
|
-
post_install_message:
|
312
|
-
AppInfo 3.0 was out!
|
313
|
-
**********************
|
314
|
-
The public API of some AppInfo classes has been changed.
|
315
|
-
|
316
|
-
Please ensure that your Gemfiles and .gemspecs are suitably restrictive
|
317
|
-
to avoid an unexpected breakage when 3.0 is released (e.g. ~> 2.8.5).
|
318
|
-
See https://github.com/icyleaf/app_info for details.
|
316
|
+
post_install_message:
|
319
317
|
rdoc_options: []
|
320
318
|
require_paths:
|
321
319
|
- lib
|
@@ -333,6 +331,5 @@ requirements: []
|
|
333
331
|
rubygems_version: 3.5.9
|
334
332
|
signing_key:
|
335
333
|
specification_version: 4
|
336
|
-
summary: Teardown tool for
|
337
|
-
version, name, icon
|
334
|
+
summary: Teardown tool for all most app, analysis metedata like version, name, icon
|
338
335
|
test_files: []
|