fastlane-plugin-analyze_ios_ipa 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: fec5a2e4c46d0daf15cb3f87edfa5ec21eb173d30f9b4ad4ae97dff6b131f399
4
+ data.tar.gz: dc0bc29517ab852c654c5bc6c08c2f8f2e1ce65db6fee1a4895bf86050f681ee
5
+ SHA512:
6
+ metadata.gz: fe52a439560b36db95ec4ad52193454d4979acf994f18ca972b3e4a3af49f47a3cf171520c09f6ef381540ff339811fe58dcbd2471bda06f69da50e46f4fab84
7
+ data.tar.gz: 4177b5b9addcffa3a70889c698a4f9f5c39dc14deee0fb20a668b2e43a9fbf3382ea06ed6acdbb6bd746c023ec47dbfcd934167d1a1c74cb98221503480a77c7
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2019 xiongzenghui <zxcvb1234001@163.com>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,52 @@
1
+ # analyze_ios_ipa plugin
2
+
3
+ [![fastlane Plugin Badge](https://rawcdn.githack.com/fastlane/fastlane/master/fastlane/assets/plugin-badge.svg)](https://rubygems.org/gems/fastlane-plugin-analyze_ios_ipa)
4
+
5
+ ## Getting Started
6
+
7
+ This project is a [_fastlane_](https://github.com/fastlane/fastlane) plugin. To get started with `fastlane-plugin-analyze_ios_ipa`, add it to your project by running:
8
+
9
+ ```bash
10
+ fastlane add_plugin analyze_ios_ipa
11
+ ```
12
+
13
+ ## About analyze_ios_ipa
14
+
15
+ xx
16
+
17
+ **Note to author:** Add a more detailed description about this plugin here. If your plugin contains multiple actions, make sure to mention them here.
18
+
19
+ ## Example
20
+
21
+ Check out the [example `Fastfile`](fastlane/Fastfile) to see how to use this plugin. Try it by cloning the repo, running `fastlane install_plugins` and `bundle exec fastlane test`.
22
+
23
+ **Note to author:** Please set up a sample project to make it easy for users to explore what your plugin does. Provide everything that is necessary to try out the plugin in this project (including a sample Xcode/Android project if necessary)
24
+
25
+ ## Run tests for this plugin
26
+
27
+ To run both the tests, and code style validation, run
28
+
29
+ ```
30
+ rake
31
+ ```
32
+
33
+ To automatically fix many of the styling issues, use
34
+ ```
35
+ rubocop -a
36
+ ```
37
+
38
+ ## Issues and Feedback
39
+
40
+ For any other issues and feedback about this plugin, please submit it to this repository.
41
+
42
+ ## Troubleshooting
43
+
44
+ If you have trouble using plugins, check out the [Plugins Troubleshooting](https://docs.fastlane.tools/plugins/plugins-troubleshooting/) guide.
45
+
46
+ ## Using _fastlane_ Plugins
47
+
48
+ For more information about how the `fastlane` plugin system works, check out the [Plugins documentation](https://docs.fastlane.tools/plugins/create-plugin/).
49
+
50
+ ## About _fastlane_
51
+
52
+ _fastlane_ is the easiest way to automate beta deployments and releases for your iOS and Android apps. To learn more, check out [fastlane.tools](https://fastlane.tools).
@@ -0,0 +1,26 @@
1
+ require 'fastlane/plugin/analyze_ios_ipa/version'
2
+
3
+ module Fastlane
4
+ module Helper
5
+ require 'pp'
6
+ require 'json'
7
+ require 'zip'
8
+ require 'fileutils'
9
+ require 'singleton'
10
+ end
11
+ end
12
+
13
+ module Fastlane
14
+ module AnalyzeIosIpa
15
+ # Return all .rb files inside the "actions" and "helper" directory
16
+ def self.all_classes
17
+ Dir[File.expand_path('**/{actions,helper}/*.rb', File.dirname(__FILE__))]
18
+ end
19
+ end
20
+ end
21
+
22
+ # By default we want to import all available actions and helpers
23
+ # A plugin can contain any number of actions and plugins
24
+ Fastlane::AnalyzeIosIpa.all_classes.each do |current|
25
+ require current
26
+ end
@@ -0,0 +1,157 @@
1
+ require 'fastlane/action'
2
+ require_relative '../helper/config'
3
+ require_relative '../helper/file_helper'
4
+ require_relative '../helper/app'
5
+
6
+ module Fastlane
7
+ module Actions
8
+ module SharedValues
9
+ AnalyzeIosIpaActionResultHash = :AnalyzeIosIpaActionResultHash
10
+ AnalyzeIosIpaActionResultJSON = :AnalyzeIosIpaActionResultJSON
11
+ end
12
+
13
+ # Fastlane::Actions.lane_context[Fastlane::Actions::SharedValues::GITLAB_AUTO_MERGE_WEB_URL]
14
+
15
+ class AnalyzeIosIpaAction < Action
16
+ def self.run(params)
17
+ ipa_path = params[:ipa_path]
18
+ app_name = params[:app_name]
19
+ app_path = params[:app_path]
20
+ group = params[:group] || true
21
+
22
+ valid_params(ipa_path, app_path)
23
+ analyze_ipa(group) if ipa_path
24
+ analyze_app(group) if app_path
25
+
26
+ Actions.lane_context[Actions::SharedValues::AnalyzeIosIpaActionResultHash] = generate_hash
27
+ Actions.lane_context[Actions::SharedValues::AnalyzeIosIpaActionResultJSON] = generate_json
28
+ end
29
+
30
+ def self.valid_params(ipa_path, app_path)
31
+ UI.user_error!("❌ ipa_path 与 app_path 至少传入一个!") if !ipa_path && !app_path
32
+
33
+ if ipa_path
34
+ UI.user_error!("❌ #{ipa_path} 文件不存在!") unless File.exist?(ipa_path)
35
+ end
36
+
37
+ if app_path
38
+ UI.user_error!("❌ #{app_path} 文件不存在!") unless File.exist?(app_path)
39
+ end
40
+
41
+ Fastlane::Helper::Config.instance.ipa_path = ipa_path
42
+ Fastlane::Helper::Config.instance.app_path = app_path
43
+ end
44
+
45
+ def self.analyze_ipa(group)
46
+ ipa_path = Fastlane::Helper::Config.instance.ipa_path
47
+ UI.important "❗️[analyze_ipa] ipa_path: #{ipa_path}"
48
+
49
+ return false unless ipa_path
50
+ return false if ipa_path.empty?
51
+ return false unless File.exist?(ipa_path)
52
+
53
+ # 解析 xx.ipa
54
+ ipa = Fastlane::Helper::Ipa.new(ipa_path)
55
+ Fastlane::Helper::Config.instance.ipa = ipa
56
+
57
+ # xx.ipa => xx.app
58
+ output = File.expand_path('output', File.dirname(ipa_path))
59
+ payload = Fastlane::Helper::FileHelper.unzip_ipa(ipa_path, output)
60
+ UI.success "✅ unzip ipa to: #{payload}"
61
+
62
+ # find xx.app
63
+ app_path = nil
64
+ app_paths = Fastlane::Helper::FileHelper.glob_files('*.app', payload)
65
+ if app_paths.empty?
66
+ UI.user_error!("❌ app_name not give") unless app_name
67
+
68
+ app_path = File.expand_path("#{app_name}.app", payload)
69
+ UI.user_error!("❌ #{app_path} not exist") unless File.exist?(app_path)
70
+ else
71
+ app_path = app_paths.first
72
+ end
73
+ # UI.important("❗️[ipa_unzip_to_app_call_analyze] app_path: #{app_path}")
74
+ Fastlane::Helper::Config.instance.app_path = app_path
75
+
76
+ # 解析 xx.app
77
+ analyze_app(group)
78
+ end
79
+
80
+ def self.analyze_app(group)
81
+ app_path = Fastlane::Helper::Config.instance.app_path
82
+ UI.important "❗️[analyze_app] app_path: #{app_path}"
83
+
84
+ return false unless app_path
85
+ return false if app_path.empty?
86
+ return false unless File.exist?(app_path)
87
+
88
+ # 解析 xx.app
89
+ app = Fastlane::Helper::App.new(app_path, group: group)
90
+ Fastlane::Helper::Config.instance.app = app
91
+ end
92
+
93
+ def self.generate_json
94
+ JSON.generate(generate_hash)
95
+ end
96
+
97
+ def self.generate_hash
98
+ {
99
+ ipa: Fastlane::Helper::Config.instance.ipa.generate_hash,
100
+ app: Fastlane::Helper::Config.instance.app.generate_hash
101
+ }
102
+ end
103
+
104
+ def self.description
105
+ 'analysis iOS app/ipa multiple data. eg: 1) ipa basic info 2) Size occupied by each component = code + resource'
106
+ end
107
+
108
+ def self.authors
109
+ ["xiongzenghui"]
110
+ end
111
+
112
+ def self.return_value
113
+ # If your method provides a return value, you can describe here what it does
114
+ end
115
+
116
+ def self.details
117
+ 'analysis iOS app/ipa multiple data. eg: 1) ipa basic info 2) Size occupied by each component = code + resource'
118
+ end
119
+
120
+ def self.available_options
121
+ [
122
+ FastlaneCore::ConfigItem.new(
123
+ key: :ipa_path,
124
+ description: 'ipa file path',
125
+ type: String,
126
+ optional: true,
127
+ conflicting_options: [:app_path]
128
+ ),
129
+ FastlaneCore::ConfigItem.new(
130
+ key: :app_path,
131
+ description: 'app file path',
132
+ type: String,
133
+ optional: true,
134
+ conflicting_options: [:ipa_path]
135
+ ),
136
+ FastlaneCore::ConfigItem.new(
137
+ key: :app_name,
138
+ description: 'app file path',
139
+ type: String,
140
+ optional: true
141
+ ),
142
+ FastlaneCore::ConfigItem.new(
143
+ key: :group,
144
+ description: 'is group files in xx.app ?',
145
+ optional: true,
146
+ default_value: true,
147
+ is_string: false
148
+ )
149
+ ]
150
+ end
151
+
152
+ def self.is_supported?(platform)
153
+ :ios == platform
154
+ end
155
+ end
156
+ end
157
+ end
@@ -0,0 +1,178 @@
1
+ require 'fastlane_core/ui/ui'
2
+ module Fastlane
3
+ UI = FastlaneCore::UI unless Fastlane.const_defined?("UI")
4
+ module Helper
5
+ require_relative 'info_plist'
6
+ require_relative 'ipa'
7
+ require_relative 'file_info'
8
+ require_relative 'file_category'
9
+
10
+ class App
11
+ ATTRS = [:group, :size, :formar_size, :info, :executable]
12
+ attr_accessor(*ATTRS)
13
+ alias group? group
14
+
15
+
16
+ INFO_PLIST = 'Info.plist'
17
+
18
+ def to_hash
19
+ h = {
20
+ size: @size,
21
+ formar_size: @formar_size,
22
+ info: {
23
+ executable: @info.executable,
24
+ device_capabilities: @info.device_capabilities,
25
+ app_type: @info.app_type, #=> 只有自己项目使用
26
+ identifier: @info.identifier,
27
+ display_name: @info.display_name,
28
+ version: @info.version,
29
+ short_version: @info.short_version
30
+ }
31
+ }
32
+
33
+ if group?
34
+ h[:categories] = categories.map(&:to_hash)
35
+ else
36
+ h[:files] = parse_app_files.map(&:to_hash)
37
+ end
38
+
39
+ h
40
+ end
41
+
42
+ def initialize(app_path, options = {})
43
+ @group = options[:group] || false
44
+ @app_path = app_path
45
+
46
+ return nil unless app_path
47
+ return nil if app_path.empty?
48
+ return nil unless File.exist?(app_path)
49
+
50
+ # size
51
+ @size = FileHelper.file_size(app_path)
52
+ @formar_size = FileHelper.format_size(@size)
53
+
54
+ # info.plist
55
+ info_plist_path = File.expand_path(INFO_PLIST, app_path)
56
+ @info = InfoPlist.new(info_plist_path)
57
+ end
58
+
59
+ def parse_app_files
60
+ return @files if @files
61
+
62
+ files = FileHelper.glob_files('*', @app_path)
63
+ # pp files
64
+ return nil unless files
65
+ return nil if files.empty?
66
+
67
+ #
68
+ # 解析 具体文件 => FileInfo
69
+ @file_infos = files.map {|f|
70
+ FileInfo.new(f)
71
+ }
72
+ @file_infos
73
+ end
74
+
75
+ def group_app_files
76
+ return @group_app_files if @group_app_files
77
+
78
+ #
79
+ # 按照 type 对 [#<FileInfo:0x01>, #<FileInfo:0x02>, ... #<FileInfo:0x0N>] 进行【分组】
80
+ @group_app_files = parse_app_files.group_by { |e|
81
+ e.type
82
+ }
83
+ # pp @group_app_files
84
+
85
+ @group_app_files
86
+ end
87
+
88
+ def category_app_files
89
+ return @category_app_files if @category_app_files
90
+
91
+ #
92
+ # 解析生成 FileCategory(文件类型)/FileInfo(文件) 结构
93
+ # [
94
+ # #<FileCategory:0x01>, @name="分类1", @file_infos=[#<FileInfo:0x01>, #<FileInfo:0x02>, ... #<FileInfo:0x0N>],
95
+ # #<FileCategory:0x01>, @name="分类2", @file_infos=[#<FileInfo:0x01>, #<FileInfo:0x02>, ... #<FileInfo:0x0N>],
96
+ # ...
97
+ # #<FileCategory:0x01>, @name="分类3", @file_infos=[#<FileInfo:0x01>, #<FileInfo:0x02>, ... #<FileInfo:0x0N>],
98
+ # ]
99
+ @category_app_files = group_app_files.map { |k ,v|
100
+ FileCategory.categories(k, v, executable: @info.executable)
101
+ }.compact.flatten
102
+ # pp file_categories.count
103
+ # pp file_categories
104
+
105
+ @category_app_files
106
+ end
107
+
108
+ def flatten_unknown_category
109
+ return @unknown_category if @unknown_category
110
+
111
+ #
112
+ # 从 grouped 之后的 文件类型, 移除出来的 重复的 name == 'Unknown' 的 UNKNOWN FileCategory
113
+ categories_unknowned = category_app_files.select { |c|
114
+ c.name == FileCategory::UNKNOWN
115
+ }
116
+ # pp categories_unknowned
117
+
118
+ #
119
+ # 平铺 UNKNOWN FileCategory 中的, 多个 FileCategory/FileInfo数组
120
+ unknown_files_infos = []
121
+ categories_unknowned.each_with_object(unknown_files_infos) { |e, mem|
122
+ mem.concat(e.file_infos).compact.flatten
123
+ }
124
+ unknown_files_infos.uniq! { |e| e.name }
125
+
126
+ #
127
+ # 重新创建 FileCategory, 并归属包含的 FileInfo
128
+ @unknown_category = FileCategory.categories(FileCategory::UNKNOWN_FILES, unknown_files_infos)
129
+ @unknown_category
130
+ end
131
+
132
+ def categories
133
+ return @categories if @categories
134
+
135
+ #
136
+ # 去除 file categories 中, name == 'Unknown' 重复的 Unknown FileCategory
137
+ categories_rejected = category_app_files.reject { |c|
138
+ c.name == FileCategory::UNKNOWN
139
+ }
140
+ # pp categories_rejected.count
141
+
142
+ #
143
+ # 再追加 平铺(flatten) 之后的 UNKNOWN FileCategory
144
+ categories_rejected.push(flatten_unknown_category)
145
+
146
+ #
147
+ # sort FileCategory
148
+ categories_sorted = categories_rejected.sort { |a, b|
149
+ b.size <=> a.size
150
+ }
151
+
152
+ # sort FileCategory.files
153
+ @categories = categories_sorted.map { |e|
154
+ e.file_infos = e.file_infos.sort { |a, b|
155
+ b.size <=> a.size
156
+ }
157
+ e
158
+ }
159
+ # pp @categories.count
160
+ # pp @categories
161
+
162
+ @categories
163
+ end
164
+
165
+ def generate_json
166
+ return @result_json if @result_json
167
+ @result_json = JSON.generate(generate_hash)
168
+ @result_json
169
+ end
170
+
171
+ def generate_hash
172
+ return @result_hash if @result_hash
173
+ @result_hash = to_hash
174
+ @result_hash
175
+ end
176
+ end
177
+ end
178
+ end
@@ -0,0 +1,19 @@
1
+ require 'fastlane_core/ui/ui'
2
+ module Fastlane
3
+ UI = FastlaneCore::UI unless Fastlane.const_defined?("UI")
4
+ module Helper
5
+ class Config
6
+ include Singleton
7
+ ATTRS = [:ipa_path, :app_path, :ipa, :app]
8
+ attr_accessor(*ATTRS)
9
+
10
+ def self.ipa_path
11
+ self.instance.ipa_path
12
+ end
13
+
14
+ def self.app_path
15
+ self.instance.app_path
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,117 @@
1
+ require 'fastlane_core/ui/ui'
2
+ module Fastlane
3
+ UI = FastlaneCore::UI unless Fastlane.const_defined?("UI")
4
+ module Helper
5
+ require_relative 'file_info'
6
+ require_relative 'file_helper'
7
+
8
+ #
9
+ # 文件类型
10
+ #
11
+ class FileCategory
12
+ attr_accessor :name, :file_infos, :size, :formar_size
13
+
14
+ PLUGINS = 'PlugIns'
15
+ FRAMEWORKS = 'Frameworks'
16
+ UNKNOWN = 'Unknown'
17
+ UNKNOWN_FILES = 'UnknownFiles'
18
+ EXECUTABLE = 'executable'
19
+
20
+ def to_hash
21
+ {
22
+ name: @name,
23
+ size: @size,
24
+ formar_size: @formar_size,
25
+ files: @file_infos.map(&:to_hash)
26
+ }
27
+ end
28
+
29
+ #
30
+ # @param type: 文件类型: FileInfo::FileInfoUnknownDir, FileInfo::FileInfoUnknownFile, FileInfo::FileInfoUnknown
31
+ # @param infos: 文件数组: [#<FileInfo:0x01>, #<FileInfo:0x02>, ... #<FileInfo:0x0N>]
32
+ #
33
+ def self.categories(type, infos, options = {})
34
+ executable_name = options[:executable]
35
+
36
+ block = {
37
+ FileInfoUnknownDir: lambda do |infos|
38
+ # 主要是将如下两个目录, 继续递归遍历解析
39
+ # - 1) PlugIns 类型
40
+ # - 2) Frameworks 类型
41
+ # - 3) Unknown 类型
42
+
43
+ infos.map { |e|
44
+ fc = FileCategory.new
45
+ if e.name == PLUGINS
46
+ fc.name = PLUGINS
47
+ fc.merge(FileHelper.glob_files('*', e.path).map { |f|
48
+ FileInfo.new(f)
49
+ })
50
+ elsif e.name == FRAMEWORKS
51
+ fc = FileCategory.new
52
+ fc.name = FRAMEWORKS
53
+ fc.merge(FileHelper.glob_files('*', e.path).map { |f|
54
+ FileInfo.new(f)
55
+ })
56
+ else
57
+ fc.name = UNKNOWN
58
+ fc.push(e)
59
+ end
60
+ fc.finish
61
+ fc
62
+ }
63
+ end,
64
+ FileInfoUnknownFile: lambda do |infos|
65
+ # - 1) executable
66
+
67
+ infos.map { |e|
68
+ fc = FileCategory.new
69
+ if e.name == executable_name
70
+ fc.name = EXECUTABLE
71
+ fc.push(FileInfo.new(e.path))
72
+ else
73
+ fc.name = UNKNOWN
74
+ fc.push(e)
75
+ end
76
+ fc.finish
77
+ fc
78
+ }
79
+ end
80
+ }[type]
81
+
82
+ if block
83
+ # puts "[1] type: #{type} -- #{infos.count}"
84
+ block.call(infos)
85
+ else
86
+ # puts "[2] type: #{type} -- #{infos.count}"
87
+
88
+ #
89
+ # Plugins/、Frameworks/、app mach-o , 三者之外的其他类型的文件
90
+
91
+ fc = FileCategory.new
92
+ fc.name = type
93
+ fc.merge(infos)
94
+ fc.finish
95
+ fc
96
+ end
97
+ end
98
+
99
+ def initialize
100
+ @file_infos = []
101
+ end
102
+
103
+ def push(info)
104
+ @file_infos << info
105
+ end
106
+
107
+ def merge(infos)
108
+ @file_infos += infos
109
+ end
110
+
111
+ def finish
112
+ @size = @file_infos.map(&:size).inject(0, :+)
113
+ @formar_size = FileHelper.format_size(@size)
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,89 @@
1
+ require 'fastlane_core/ui/ui'
2
+
3
+ module Fastlane
4
+ UI = FastlaneCore::UI unless Fastlane.const_defined?("UI")
5
+ module Helper
6
+ class FileHelper
7
+ def self.file_join(files)
8
+ # File.join('DKNightVersion', 'Pod', 'Classes', 'UIKit') => DKNightVersion/Pod/Classes/UIKit
9
+ File.join(files)
10
+ end
11
+
12
+ def self.file_size(file_path, log = false)
13
+ total = 0
14
+ return 0 unless File.exist?(file_path)
15
+
16
+ base = File.basename(file_path)
17
+ return 0 if ['.', '..'].include?(base)
18
+
19
+ dir = File.dirname(file_path)
20
+ file_path = file_join([dir, base])
21
+
22
+ if File.directory?(file_path)
23
+ printf("Dir: %s\n", file_path) if log
24
+ Dir.foreach(file_path) { |file_name|
25
+ total += file_size(file_join([file_path, file_name]), log)
26
+ }
27
+ else
28
+ size = File.stat(file_path).size
29
+ printf("File: %s - %d byte\n", file_path, size) if log
30
+ total += size
31
+ end
32
+
33
+ total
34
+ end
35
+
36
+ def self.format_size(bytes, k = 1024)
37
+ return '0 B' unless bytes
38
+ return '0 B' if bytes.zero?
39
+
40
+ suffix = %w[B KB MB GB TB PB EB ZB YB]
41
+ i = (Math.log(bytes) / Math.log(k)).floor
42
+ base = (k ** i).to_f
43
+ num = (bytes / base).round(2)
44
+ "#{num} " + suffix[i]
45
+ end
46
+
47
+ def self.glob_files(expr, dir)
48
+ Dir.glob(File.expand_path(expr, dir))
49
+ end
50
+
51
+ def self.mv_file(src, dest)
52
+ FileUtils.mv(src, dest)
53
+ end
54
+
55
+ def self.cp_file(src, dest)
56
+ FileUtils.cp_r(src, dest)
57
+ end
58
+
59
+ def self.unzip_file(src, dest)
60
+ FileUtils.mkdir_p(dest) unless Dir.exist?(dest)
61
+ Zip::File.open(src) do |zip_file|
62
+ zip_file.each do |f|
63
+ fpath = File.join(dest, f.name)
64
+ zip_file.extract(f, fpath) unless File.exist?(fpath)
65
+ end
66
+ end
67
+ end
68
+
69
+ def self.unzip_ipa(ipa, output)
70
+ # return nil unless ipa
71
+ # return nil if ipa.empty?
72
+ # return nil unless File.exist?(ipa)
73
+
74
+ # ## create output dir if need ?
75
+ # FileUtils.rm_rf(output)
76
+ # FileUtils.mkdir_p(output)
77
+
78
+ # ## xx.ipa => xx.zip
79
+ # cp_dest = File.expand_path('ipa.zip', output)
80
+ # cp_file(ipa, cp_dest)
81
+
82
+ # ## xx.zip => Payload/
83
+ # unzip_file(cp_dest, output)
84
+
85
+ File.expand_path('Payload', output)
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,76 @@
1
+ require 'fastlane_core/ui/ui'
2
+ module Fastlane
3
+ UI = FastlaneCore::UI unless Fastlane.const_defined?("UI")
4
+ module Helper
5
+ require_relative 'info_plist'
6
+
7
+ #
8
+ # 文件
9
+ #
10
+ class FileInfo
11
+ FileInfoUnknownFile = :FileInfoUnknownFile
12
+ FileInfoUnknownDir = :FileInfoUnknownDir
13
+ FileInfoUnknown = :FileInfoUnknown
14
+
15
+ attr_accessor :name, :path, :size, :formar_size, :type
16
+
17
+ def to_hash
18
+ {
19
+ name: @name,
20
+ size: @size,
21
+ formar_size: @formar_size,
22
+ path: @path,
23
+ type: @type
24
+ }
25
+ end
26
+
27
+ def initialize(file_path)
28
+ @name = File.basename(file_path)
29
+ @path = file_path
30
+ @size = FileHelper.file_size(file_path)
31
+ @formar_size = FileHelper.format_size(@size)
32
+
33
+ names = @name.split('.')
34
+ @type = if names.count > 1
35
+ names.last
36
+ else
37
+ if File.stat(file_path).file?
38
+ FileInfoUnknownFile
39
+ elsif File.directory?(".")
40
+ FileInfoUnknownDir
41
+ else
42
+ FileInfoUnknown
43
+ end
44
+ end
45
+ end
46
+
47
+ def unknown_file?
48
+ @type == FileInfoUnknownFile
49
+ end
50
+
51
+ def unknown_dir?
52
+ @type == FileInfoUnknownDir
53
+ end
54
+
55
+ def bundle?
56
+ @type == 'bundle'
57
+ end
58
+
59
+ def framework?
60
+ @name == 'framework'
61
+ end
62
+
63
+ def dylib?
64
+ @name == 'dylib'
65
+ end
66
+
67
+ def strings?
68
+ @type == 'strings'
69
+ end
70
+
71
+ def plist?
72
+ @type == 'plist'
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,54 @@
1
+ require 'fastlane_core/ui/ui'
2
+ module Fastlane
3
+ UI = FastlaneCore::UI unless Fastlane.const_defined?("UI")
4
+ module Helper
5
+ require 'cfpropertylist'
6
+ class InfoPlist
7
+ def initialize(plist)
8
+ @plist = plist
9
+ end
10
+
11
+ def prase
12
+ return @info if @info
13
+ @info = CFPropertyList.native_types(CFPropertyList::List.new(file: @plist).value)
14
+ @info
15
+ end
16
+
17
+ # def
18
+ # prase['']
19
+ # end
20
+
21
+ def executable
22
+ prase['CFBundleExecutable']
23
+ end
24
+
25
+ def device_capabilities
26
+ prase['UIRequiredDeviceCapabilities']
27
+ end
28
+
29
+ def app_type
30
+ prase['ZHAppBuildType']
31
+ end
32
+
33
+ def identifier
34
+ prase['CFBundleIdentifier']
35
+ end
36
+
37
+ def bundle_name
38
+ prase['CFBundleName']
39
+ end
40
+
41
+ def display_name
42
+ prase['CFBundleDisplayName']
43
+ end
44
+
45
+ def version
46
+ prase['CFBundleVersion']
47
+ end
48
+
49
+ def short_version
50
+ prase['CFBundleShortVersionString']
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,39 @@
1
+ require 'fastlane_core/ui/ui'
2
+ module Fastlane
3
+ UI = FastlaneCore::UI unless Fastlane.const_defined?("UI")
4
+ module Helper
5
+ class Ipa
6
+ ATTRS = [:size, :formar_size]
7
+ attr_accessor(*ATTRS)
8
+
9
+ def initialize(ipa_path)
10
+ return nil unless ipa_path
11
+ return nil if ipa_path.empty?
12
+ return nil unless File.exist?(ipa_path)
13
+
14
+ # size
15
+ @size = FileHelper.file_size(ipa_path)
16
+ @formar_size = FileHelper.format_size(@size)
17
+ end
18
+
19
+ def to_hash
20
+ {
21
+ size: @size,
22
+ formar_size: @formar_size
23
+ }
24
+ end
25
+
26
+ def generate_json
27
+ return @result_json if @result_json
28
+ @result_json = JSON.generate(generate_hash)
29
+ @result_json
30
+ end
31
+
32
+ def generate_hash
33
+ return @result_hash if @result_hash
34
+ @result_hash = to_hash
35
+ @result_hash
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,5 @@
1
+ module Fastlane
2
+ module AnalyzeIosIpa
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
metadata ADDED
@@ -0,0 +1,201 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fastlane-plugin-analyze_ios_ipa
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - xiongzenghui
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-12-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: CFPropertyList
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 3.0.2
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '4.0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: 3.0.2
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '4.0'
33
+ - !ruby/object:Gem::Dependency
34
+ name: pry
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ type: :development
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ - !ruby/object:Gem::Dependency
48
+ name: bundler
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ - !ruby/object:Gem::Dependency
62
+ name: rspec
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ - !ruby/object:Gem::Dependency
76
+ name: rspec_junit_formatter
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ - !ruby/object:Gem::Dependency
90
+ name: rake
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ - !ruby/object:Gem::Dependency
104
+ name: rubocop
105
+ requirement: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - '='
108
+ - !ruby/object:Gem::Version
109
+ version: 0.49.1
110
+ type: :development
111
+ prerelease: false
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - '='
115
+ - !ruby/object:Gem::Version
116
+ version: 0.49.1
117
+ - !ruby/object:Gem::Dependency
118
+ name: rubocop-require_tools
119
+ requirement: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ type: :development
125
+ prerelease: false
126
+ version_requirements: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ - !ruby/object:Gem::Dependency
132
+ name: simplecov
133
+ requirement: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ version: '0'
138
+ type: :development
139
+ prerelease: false
140
+ version_requirements: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - ">="
143
+ - !ruby/object:Gem::Version
144
+ version: '0'
145
+ - !ruby/object:Gem::Dependency
146
+ name: fastlane
147
+ requirement: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ version: 2.128.1
152
+ type: :development
153
+ prerelease: false
154
+ version_requirements: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - ">="
157
+ - !ruby/object:Gem::Version
158
+ version: 2.128.1
159
+ description:
160
+ email: zxcvb1234001@163.com
161
+ executables: []
162
+ extensions: []
163
+ extra_rdoc_files: []
164
+ files:
165
+ - LICENSE
166
+ - README.md
167
+ - lib/fastlane/plugin/analyze_ios_ipa.rb
168
+ - lib/fastlane/plugin/analyze_ios_ipa/actions/analyze_ios_ipa_action.rb
169
+ - lib/fastlane/plugin/analyze_ios_ipa/helper/app.rb
170
+ - lib/fastlane/plugin/analyze_ios_ipa/helper/config.rb
171
+ - lib/fastlane/plugin/analyze_ios_ipa/helper/file_category.rb
172
+ - lib/fastlane/plugin/analyze_ios_ipa/helper/file_helper.rb
173
+ - lib/fastlane/plugin/analyze_ios_ipa/helper/file_info.rb
174
+ - lib/fastlane/plugin/analyze_ios_ipa/helper/info_plist.rb
175
+ - lib/fastlane/plugin/analyze_ios_ipa/helper/ipa.rb
176
+ - lib/fastlane/plugin/analyze_ios_ipa/version.rb
177
+ homepage: https://github.com/xzhhe/fastlane-plugin-analyze_ios_ipa
178
+ licenses:
179
+ - MIT
180
+ metadata: {}
181
+ post_install_message:
182
+ rdoc_options: []
183
+ require_paths:
184
+ - lib
185
+ required_ruby_version: !ruby/object:Gem::Requirement
186
+ requirements:
187
+ - - ">="
188
+ - !ruby/object:Gem::Version
189
+ version: '0'
190
+ required_rubygems_version: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - ">="
193
+ - !ruby/object:Gem::Version
194
+ version: '0'
195
+ requirements: []
196
+ rubygems_version: 3.0.6
197
+ signing_key:
198
+ specification_version: 4
199
+ summary: 'analysis iOS app/ipa multiple data. eg: 1) ipa basic info 2) Size occupied
200
+ by each component = code + resource'
201
+ test_files: []