app-info 3.1.4 → 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 678ea8ec8c58efc66f4937194709f72b81221613ba7da6c4db367e8e619b4fa9
4
- data.tar.gz: c8dffdd539e078ebc81de5f7335f9858a28a6373b3603cb47301174de69feb30
3
+ metadata.gz: 114f4899c399d6a4b8091b798a6089538b157eccba40c428ae36e445f79191d6
4
+ data.tar.gz: 872061f173786d61f2dbad70a25405b71fc2b054ea023fb375394aed52febeeb
5
5
  SHA512:
6
- metadata.gz: d270d7c5fb948109da2aca484479cfaf6d4acc629598dc9199b794286a600235dba5bcbb5205c52671dbecabe8ee0b9917f8dbf1389903ff9951baf673836ec3
7
- data.tar.gz: b91a9932a126c6af47db8310df78c0c3d1f9c4ff3224c4764b54e1b29cd59b4286fe2533c03499bf814227cd528b0b00ffdc739282f4605829bf23d71029e27e
6
+ metadata.gz: 435e4ce4855c42b94a5ef6a9bd12e208765691ad4d2ba1f0da92eb251e6fe12ed9f9aaa95ec8ba5fd3f53b1b4ff664fb8e8a1764b32992c5c0be220e51344ce1
7
+ data.tar.gz: 376b5e7ac14d0fa8a95d0bd0a6d2861532e937317ed3c6a1cea07bc3ebc7a4ac2b84b8648180a9ae298768e3b7e90335a055bf344a2a2f933a2106b2f853bb31
@@ -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
- os: [ ubuntu-latest ]
17
-
18
- runs-on: ${{ matrix.os }}
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: bundle exec rake
29
- run: bundle exec rake
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.1.4..HEAD
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
  [![Build Status](https://img.shields.io/github/actions/workflow/status/icyleaf/app_info/ci.yml)](https://github.com/icyleaf/app_info/actions/workflows/ci.yml)
6
6
  [![License](https://img.shields.io/github/license/icyleaf/app-info)](LICENSE)
7
7
 
8
- Teardown tool for mobile app (ipa, apk and aab file), macOS app, dSYM.zip file and Windows PE file.
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 mobile app(ipa/apk) and dSYM file, analysis metedata like version, name, icon'
14
- spec.description = 'Teardown tool for ipa/apk files and dSYM file, even support for info.plist and .mobileprovision files'
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
@@ -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
 
@@ -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
- # Apple Device Type
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
@@ -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
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'app_info/helper/file_type_detection'
3
4
  require 'app_info/helper/archive'
4
5
  require 'app_info/helper/file_size'
5
6
  require 'app_info/helper/generate_class'
@@ -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) =~ AppInfo::ZIP_RETGEX
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AppInfo
4
- VERSION = '3.1.4'
4
+ VERSION = '3.2.0'
5
5
  end
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 invaild date format warnings
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.1.4
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-06-28 00:00:00.000000000 Z
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/apk files and dSYM file, even support for info.plist
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 mobile app(ipa/apk) and dSYM file, analysis metedata like
337
- version, name, icon
334
+ summary: Teardown tool for all most app, analysis metedata like version, name, icon
338
335
  test_files: []