pindo 5.13.5 → 5.13.6
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/lib/pindo/command/appstore/adhocbuild.rb +61 -30
- data/lib/pindo/command/appstore/autobuild.rb +18 -5
- data/lib/pindo/command/appstore/autoresign.rb +294 -163
- data/lib/pindo/command/jps/upload.rb +1 -1
- data/lib/pindo/module/pgyer/pgyerhelper.rb +162 -0
- data/lib/pindo/module/task/model/ipa_local_resign_task.rb +189 -0
- data/lib/pindo/module/task/model/jps_upload_task.rb +65 -21
- data/lib/pindo/module/xcode/ipa_resign_helper.rb +1 -1
- data/lib/pindo/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bdf43dd4c0b65e41d7ae75cfaafa4bb9446dbf0ab735e6179e03f09ff25e96d8
|
|
4
|
+
data.tar.gz: 781f6bc039fe30f66f9d024f5a0ffe498c969687ffb0053113d45464e13621f9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ee0911513edf4360270825cf5ee938736b0aba41a0b89d0ad9fd6b1062c590b7594edd8258d3c245eee8f36f565c48056dc6e8119d7dcf84ce616751db76b705
|
|
7
|
+
data.tar.gz: 82d6558b35038c5f97ee2595947ad0afa080a184d079b0d86cea3590830b2d3d5ebd21d0c5be61f724474c853f5aa08073a45c8eec7c5dc84a844f5614b411a9
|
|
@@ -48,9 +48,15 @@ module Pindo
|
|
|
48
48
|
|
|
49
49
|
* 支持自动增加版本号
|
|
50
50
|
|
|
51
|
+
* 支持位置参数(向后兼容)和选项参数两种格式
|
|
52
|
+
|
|
51
53
|
使用示例:
|
|
52
54
|
|
|
53
|
-
$ pindo appstore adhocbuild #
|
|
55
|
+
$ pindo appstore adhocbuild # 使用当前目录的 config.json
|
|
56
|
+
|
|
57
|
+
$ pindo appstore adhocbuild com.example.app # 位置参数指定 Bundle ID(向后兼容)
|
|
58
|
+
|
|
59
|
+
$ pindo appstore adhocbuild --bundleid=com.example.app # 选项参数指定 Bundle ID
|
|
54
60
|
|
|
55
61
|
$ pindo appstore adhocbuild --upload # 编译并上传到JPS
|
|
56
62
|
|
|
@@ -59,13 +65,11 @@ module Pindo
|
|
|
59
65
|
$ pindo appstore adhocbuild --increase # 自动增加版本号
|
|
60
66
|
|
|
61
67
|
$ pindo appstore adhocbuild --proj="My App" # 指定项目名称
|
|
62
|
-
|
|
63
|
-
$ pindo appstore adhocbuild --bundleid=com.example.app # 指定 Bundle ID
|
|
64
68
|
DESC
|
|
65
69
|
|
|
66
|
-
#
|
|
70
|
+
# 定义位置参数(向后兼容)
|
|
67
71
|
self.arguments = [
|
|
68
|
-
|
|
72
|
+
CLAide::Argument.new('bundleid', true)
|
|
69
73
|
]
|
|
70
74
|
|
|
71
75
|
# 定义此命令使用的参数项(类方法,避免重复定义)
|
|
@@ -96,11 +100,15 @@ module Pindo
|
|
|
96
100
|
end
|
|
97
101
|
|
|
98
102
|
def initialize(argv)
|
|
103
|
+
# 首先获取位置参数(向后兼容)
|
|
104
|
+
positional_bundleid = argv.shift_argument
|
|
105
|
+
|
|
99
106
|
# 一行代码完成参数初始化(自动推导命令名、自动启用缓存)
|
|
100
107
|
@options = initialize_options(argv)
|
|
101
108
|
|
|
102
109
|
# 保存参数到实例变量
|
|
103
|
-
|
|
110
|
+
# 优先使用选项参数,如果没有则使用位置参数
|
|
111
|
+
@args_bundle_id = @options[:bundleid] || positional_bundleid
|
|
104
112
|
@args_send_flag = @options[:send]
|
|
105
113
|
# send 依赖 upload:如果指定了 send,自动启用 upload
|
|
106
114
|
@args_upload_flag = @options[:send] || @options[:upload]
|
|
@@ -182,7 +190,9 @@ module Pindo
|
|
|
182
190
|
puts "\n使用当前目录配置文件: #{config_file}"
|
|
183
191
|
Pindo::IosConfigParser.instance.load_config(config_file: config_file)
|
|
184
192
|
else
|
|
185
|
-
raise Informative, "当前目录未找到 config.json
|
|
193
|
+
raise Informative, "当前目录未找到 config.json 文件,请使用以下方式指定 Bundle ID:\n" \
|
|
194
|
+
" 方式1: pindo appstore adhocbuild com.example.app\n" \
|
|
195
|
+
" 方式2: pindo appstore adhocbuild --bundleid=com.example.app"
|
|
186
196
|
end
|
|
187
197
|
end
|
|
188
198
|
end
|
|
@@ -236,9 +246,7 @@ module Pindo
|
|
|
236
246
|
project_path: ios_project_path,
|
|
237
247
|
proj_name: @args_proj_name,
|
|
238
248
|
upload: @args_upload_flag,
|
|
239
|
-
send: @args_send_flag
|
|
240
|
-
app_info_obj: config[:app_info_obj],
|
|
241
|
-
workflow_info: config[:workflow_info]
|
|
249
|
+
send: @args_send_flag
|
|
242
250
|
}
|
|
243
251
|
build_options[:bundle_id] = @args_bundle_id if @args_bundle_id
|
|
244
252
|
build_options[:unity_root_path] = config[:project_path] if is_unity
|
|
@@ -251,20 +259,23 @@ module Pindo
|
|
|
251
259
|
build_task.dependencies << tasks.last.id if tasks.any?
|
|
252
260
|
tasks << build_task
|
|
253
261
|
|
|
254
|
-
# 3.
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
262
|
+
# 3. 上传任务(仅在需要时添加)
|
|
263
|
+
if @args_upload_flag
|
|
264
|
+
upload_task = Pindo::TaskSystem::JPSUploadTask.new(
|
|
265
|
+
'ipa',
|
|
266
|
+
File.join(ios_project_path, 'build'),
|
|
267
|
+
nil,
|
|
268
|
+
app_info_obj: config[:app_info_obj],
|
|
269
|
+
workflow_info: config[:workflow_info],
|
|
270
|
+
project_name: @args_proj_name,
|
|
271
|
+
upload_desc: "AdHoc 包",
|
|
272
|
+
context: {
|
|
273
|
+
send_to_chat: @args_send_flag
|
|
274
|
+
},
|
|
275
|
+
dependencies: [build_task.id]
|
|
276
|
+
)
|
|
277
|
+
tasks << upload_task
|
|
278
|
+
end
|
|
268
279
|
|
|
269
280
|
tasks
|
|
270
281
|
else
|
|
@@ -274,12 +285,32 @@ module Pindo
|
|
|
274
285
|
|
|
275
286
|
# 准备 iOS 配置
|
|
276
287
|
def prepare_ios_config(pindo_project_dir)
|
|
277
|
-
#
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
288
|
+
# 从 IosConfigParser 获取项目名称
|
|
289
|
+
proj_name = nil
|
|
290
|
+
begin
|
|
291
|
+
require 'pindo/config/ios_config_parser'
|
|
292
|
+
config_parser = Pindo::IosConfigParser.instance
|
|
293
|
+
if config_parser.config_json
|
|
294
|
+
proj_name = config_parser.config_json.dig("project_info", "project_name")
|
|
295
|
+
end
|
|
296
|
+
rescue => e
|
|
297
|
+
# 忽略错误,proj_name 保持为 nil
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
# 如果有命令行参数的 proj_name,优先使用
|
|
301
|
+
proj_name = @args_proj_name if @args_proj_name && !@args_proj_name.empty?
|
|
302
|
+
|
|
303
|
+
# 只在需要上传时获取 JPS 配置
|
|
304
|
+
if @args_upload_flag
|
|
305
|
+
app_info_obj, workflow_info = PgyerHelper.share_instace.get_adhoc_upload_info(
|
|
306
|
+
working_directory: pindo_project_dir,
|
|
307
|
+
package_type: 'ipa',
|
|
308
|
+
proj_name: proj_name
|
|
309
|
+
)
|
|
310
|
+
else
|
|
311
|
+
app_info_obj = nil
|
|
312
|
+
workflow_info = nil
|
|
313
|
+
end
|
|
283
314
|
|
|
284
315
|
{
|
|
285
316
|
project_path: pindo_project_dir,
|
|
@@ -40,14 +40,21 @@ module Pindo
|
|
|
40
40
|
|
|
41
41
|
* 处理 Applovin(如果使用)
|
|
42
42
|
|
|
43
|
+
* 支持位置参数(向后兼容)和选项参数两种格式
|
|
44
|
+
|
|
43
45
|
使用示例:
|
|
44
46
|
|
|
45
|
-
$ pindo appstore autobuild #
|
|
47
|
+
$ pindo appstore autobuild # 使用当前目录的 config.json
|
|
48
|
+
|
|
49
|
+
$ pindo appstore autobuild com.example.app # 位置参数指定 Bundle ID(向后兼容)
|
|
46
50
|
|
|
47
|
-
$ pindo appstore autobuild --bundleid=com.example.app #
|
|
51
|
+
$ pindo appstore autobuild --bundleid=com.example.app # 选项参数指定 Bundle ID
|
|
48
52
|
DESC
|
|
49
53
|
|
|
50
|
-
|
|
54
|
+
# 定义位置参数(向后兼容)
|
|
55
|
+
self.arguments = [
|
|
56
|
+
CLAide::Argument.new('bundleid', true)
|
|
57
|
+
]
|
|
51
58
|
|
|
52
59
|
# 定义此命令使用的参数项
|
|
53
60
|
def self.option_items
|
|
@@ -61,11 +68,15 @@ module Pindo
|
|
|
61
68
|
end
|
|
62
69
|
|
|
63
70
|
def initialize(argv)
|
|
71
|
+
# 首先获取位置参数(向后兼容)
|
|
72
|
+
positional_bundleid = argv.shift_argument
|
|
73
|
+
|
|
64
74
|
# 使用 Options 模块初始化参数
|
|
65
75
|
@options = initialize_options(argv)
|
|
66
76
|
|
|
67
77
|
# 保存参数到实例变量
|
|
68
|
-
|
|
78
|
+
# 优先使用选项参数,如果没有则使用位置参数
|
|
79
|
+
@args_bundle_id = @options[:bundleid] || positional_bundleid
|
|
69
80
|
|
|
70
81
|
super(argv)
|
|
71
82
|
end
|
|
@@ -133,7 +144,9 @@ module Pindo
|
|
|
133
144
|
puts "\n使用当前目录配置文件: #{config_file}"
|
|
134
145
|
Pindo::IosConfigParser.instance.load_config(config_file: config_file)
|
|
135
146
|
else
|
|
136
|
-
raise Informative, "当前目录未找到 config.json
|
|
147
|
+
raise Informative, "当前目录未找到 config.json 文件,请使用以下方式指定 Bundle ID:\n" \
|
|
148
|
+
" 方式1: pindo appstore autobuild com.example.app\n" \
|
|
149
|
+
" 方式2: pindo appstore autobuild --bundleid=com.example.app"
|
|
137
150
|
end
|
|
138
151
|
end
|
|
139
152
|
end
|
|
@@ -4,196 +4,327 @@ require 'find'
|
|
|
4
4
|
require 'fileutils'
|
|
5
5
|
require 'pindo/base/executable'
|
|
6
6
|
require 'pindo/config/ios_config_parser'
|
|
7
|
-
require 'pindo/
|
|
7
|
+
require 'pindo/config/build_info_manager'
|
|
8
|
+
require 'pindo/base/pindocontext'
|
|
9
|
+
require 'pindo/module/task/task_manager'
|
|
10
|
+
require 'pindo/module/task/model/ipa_local_resign_task'
|
|
11
|
+
require 'pindo/module/task/model/jps_upload_task'
|
|
12
|
+
require 'pindo/options/options'
|
|
8
13
|
|
|
9
14
|
module Pindo
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
15
|
+
class Command
|
|
16
|
+
class Appstore < Command
|
|
17
|
+
class Autoresign < Appstore
|
|
13
18
|
|
|
14
|
-
|
|
19
|
+
include Appselect
|
|
15
20
|
|
|
16
|
-
|
|
21
|
+
# 命令的简要说明
|
|
22
|
+
self.summary = 'IPA 包重签名及上传到 JPS 测试平台'
|
|
17
23
|
|
|
18
|
-
|
|
19
|
-
|
|
24
|
+
# 启用缓存机制
|
|
25
|
+
def self.use_cache?
|
|
26
|
+
true
|
|
27
|
+
end
|
|
20
28
|
|
|
21
|
-
|
|
29
|
+
# 命令的详细说明
|
|
30
|
+
self.description = <<-DESC
|
|
31
|
+
对 IPA 文件进行重签名,并可选上传到 JPS 测试平台。
|
|
22
32
|
|
|
23
|
-
|
|
33
|
+
支持功能:
|
|
24
34
|
|
|
25
|
-
|
|
35
|
+
* IPA 文件重签名(支持 dev/adhoc 证书)
|
|
26
36
|
|
|
27
|
-
|
|
37
|
+
* 自动查找或手动指定 IPA 文件
|
|
28
38
|
|
|
29
|
-
|
|
39
|
+
* 上传到 JPS 测试平台
|
|
30
40
|
|
|
31
|
-
|
|
41
|
+
* 发送测试通知到群组
|
|
32
42
|
|
|
33
|
-
|
|
43
|
+
* 支持 dev/adhoc 构建类型
|
|
34
44
|
|
|
35
|
-
|
|
36
|
-
DESC
|
|
45
|
+
使用示例:
|
|
37
46
|
|
|
38
|
-
|
|
39
|
-
CLAide::Argument.new('path/to/ipa', true),
|
|
40
|
-
]
|
|
47
|
+
$ pindo appstore autoresign # 自动查找并重签名(adhoc)
|
|
41
48
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
['--proj', '指定哪个项目(忽略大小写空格等等字符): --proj=\"prancksoundv4\"'],
|
|
46
|
-
['--upload', '编译完成后是否上传ipa到JPS: --upload'],
|
|
47
|
-
['--send', '上传到JPS之后是否发送测试信息: --send'],
|
|
48
|
-
['--ipa', '强制指定重签名的ipa包,用法: --ipa=path/to/demo.ipa'],
|
|
49
|
-
['--dev', '使用dev证书,默认使用adhoc证书: --dev'],
|
|
50
|
-
['--test', '使用测试的bundle id, 默认是用发布bundle id,用法:--test'],
|
|
51
|
-
].concat(super)
|
|
52
|
-
end
|
|
49
|
+
$ pindo appstore autoresign demo.ipa # 重签名指定 IPA(adhoc)
|
|
50
|
+
|
|
51
|
+
$ pindo appstore autoresign --ipa=demo.ipa # 使用选项指定 IPA
|
|
53
52
|
|
|
53
|
+
$ pindo appstore autoresign --type=dev # 使用 dev 证书和测试 Bundle ID
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
$ pindo appstore autoresign --upload # 重签名并上传
|
|
56
56
|
|
|
57
|
-
|
|
58
|
-
@args_set_ipa_name = argv.option('ipa')
|
|
59
|
-
@args_dev_flag = argv.flag?('dev', false)
|
|
60
|
-
@args_test_flag = argv.flag?('test', false)
|
|
57
|
+
$ pindo appstore autoresign --send # 重签名、上传并发送通知
|
|
61
58
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
@args_proj_name = argv.option('proj')
|
|
59
|
+
$ pindo appstore autoresign --proj="My App" # 指定项目名称
|
|
60
|
+
DESC
|
|
65
61
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
62
|
+
# 位置参数(向后兼容)
|
|
63
|
+
self.arguments = [
|
|
64
|
+
CLAide::Argument.new('path/to/ipa', true)
|
|
65
|
+
]
|
|
69
66
|
|
|
70
|
-
|
|
71
|
-
|
|
67
|
+
# 定义此命令使用的参数项
|
|
68
|
+
def self.option_items
|
|
69
|
+
@option_items ||= begin
|
|
70
|
+
items = []
|
|
71
|
+
|
|
72
|
+
# 添加 --ipa 参数
|
|
73
|
+
items << Pindo::Options::OptionItem.new(
|
|
74
|
+
key: :ipa,
|
|
75
|
+
description: '指定要重签名的 IPA 文件路径',
|
|
76
|
+
type: String,
|
|
77
|
+
optional: true,
|
|
78
|
+
verify_block: proc do |value|
|
|
79
|
+
unless value.end_with?('.ipa')
|
|
80
|
+
raise "IPA 文件路径格式错误: #{value},必须以 .ipa 结尾"
|
|
81
|
+
end
|
|
82
|
+
unless File.exist?(value)
|
|
83
|
+
raise "IPA 文件不存在: #{value}"
|
|
72
84
|
end
|
|
85
|
+
end,
|
|
86
|
+
example: 'pindo appstore autoresign --ipa=path/to/demo.ipa'
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
# 添加 --type 参数
|
|
90
|
+
items << Pindo::Options::OptionItem.new(
|
|
91
|
+
key: :type,
|
|
92
|
+
description: '构建类型(dev/adhoc,默认 adhoc)。dev=使用dev证书+测试BundleID,adhoc=使用adhoc证书+发布BundleID',
|
|
93
|
+
type: String,
|
|
94
|
+
default_value: 'adhoc',
|
|
95
|
+
optional: true,
|
|
96
|
+
verify_block: proc do |value|
|
|
97
|
+
valid_types = ['dev', 'adhoc']
|
|
98
|
+
unless valid_types.include?(value.to_s.downcase)
|
|
99
|
+
raise "构建类型错误: #{value},必须是 dev 或 adhoc"
|
|
100
|
+
end
|
|
101
|
+
end,
|
|
102
|
+
example: 'pindo appstore autoresign --type=dev'
|
|
103
|
+
)
|
|
73
104
|
|
|
105
|
+
# 合并 JPSOptions
|
|
106
|
+
items.concat(Pindo::Options::JPSOptions.select(:proj, :upload, :send))
|
|
74
107
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
if !@args_set_ipa_name.nil?
|
|
79
|
-
ipa_file_name = @args_set_ipa_name
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
if !ipa_file_name.nil? && File.exist?(ipa_file_name)
|
|
83
|
-
else
|
|
84
|
-
current_dir = Dir.pwd
|
|
85
|
-
# File.basename(f).include?("_resigned") ? Time.local(0, 0, 0) : File.mtime(f)
|
|
86
|
-
ipa_file_name = Dir.glob(File.join(current_dir, "/*.ipa")).max_by {|f| File.basename(f).include?("_resigned") ? Time.local(0, 1, 1) : File.mtime(f)}
|
|
87
|
-
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
if !ipa_file_name.nil? && File.exist?(ipa_file_name)
|
|
91
|
-
puts
|
|
92
|
-
puts "正在重签名的ipa: #{ipa_file_name}"
|
|
93
|
-
puts "时间: #{File.mtime(ipa_file_name)}"
|
|
94
|
-
puts
|
|
95
|
-
else
|
|
96
|
-
return
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
mainapp_bundleid = nil
|
|
101
|
-
|
|
102
|
-
if @args_test_flag
|
|
103
|
-
mainapp_bundleid = get_selected_dev_bundleid()
|
|
104
|
-
else
|
|
105
|
-
mainapp_bundleid = get_selected_deploy_bundleid()
|
|
106
|
-
end
|
|
107
|
-
puts "mainapp_bundleid: #{mainapp_bundleid}"
|
|
108
|
-
|
|
109
|
-
# 拉取应用配置
|
|
110
|
-
require 'pindo/config/build_info_manager'
|
|
111
|
-
Pindo::BuildInfoManager.share_instance.pull_appconfig_with_reponame(
|
|
112
|
-
repo_name: mainapp_bundleid,
|
|
113
|
-
target_dir: Dir.pwd
|
|
114
|
-
)
|
|
115
|
-
|
|
116
|
-
app_info_obj = nil
|
|
117
|
-
workflow_info = nil
|
|
118
|
-
if @args_upload_flag || @args_send_flag
|
|
119
|
-
# 传入 package_type 获取 workflow_info
|
|
120
|
-
app_info_obj, workflow_info = PgyerHelper.share_instace.prepare_upload(
|
|
121
|
-
working_directory: Dir.pwd,
|
|
122
|
-
proj_name: @args_proj_name,
|
|
123
|
-
package_type: 'ipa'
|
|
124
|
-
)
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
if !ipa_file_name.nil?
|
|
129
|
-
# 加载配置获取 Bundle ID
|
|
130
|
-
config_file = File.join(Dir.pwd, "config.json")
|
|
131
|
-
config_parser = Pindo::IosConfigParser.instance
|
|
132
|
-
config_parser.load_config(config_file: config_file)
|
|
133
|
-
bundle_id = config_parser.bundle_id
|
|
134
|
-
|
|
135
|
-
if bundle_id.nil? || bundle_id.empty?
|
|
136
|
-
raise Informative, "无法从配置文件中获取 Bundle ID"
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
# 确定构建类型并安装证书
|
|
140
|
-
build_type = @args_dev_flag ? 'dev' : 'adhoc'
|
|
141
|
-
cert_args = build_type == 'adhoc' ? ['--build_type=adhoc'] : []
|
|
142
|
-
Pindo::Command::Appstore::Cert::run(cert_args)
|
|
143
|
-
|
|
144
|
-
# 使用 IpaResignHelper 进行重签名
|
|
145
|
-
ipa_file_upload = Pindo::IpaResignHelper.resign_ipa(
|
|
146
|
-
ipa_file_path: ipa_file_name,
|
|
147
|
-
bundle_id: bundle_id
|
|
148
|
-
)
|
|
149
|
-
|
|
150
|
-
if ipa_file_upload.nil?
|
|
151
|
-
raise Informative, "重签名失败"
|
|
152
|
-
end
|
|
153
|
-
|
|
154
|
-
puts "\n重签名成功!"
|
|
155
|
-
puts "重签名后的 IPA: #{ipa_file_upload}"
|
|
156
|
-
end
|
|
157
|
-
|
|
158
|
-
if !ipa_file_upload.nil? && !app_info_obj.nil?
|
|
159
|
-
|
|
160
|
-
if !ipa_file_upload.nil? && !app_info_obj.nil?
|
|
161
|
-
description = "提交包重签名"
|
|
162
|
-
result_data = PgyerHelper.share_instace.start_upload(
|
|
163
|
-
app_info_obj: app_info_obj,
|
|
164
|
-
ipa_file_upload: ipa_file_upload,
|
|
165
|
-
description: description,
|
|
166
|
-
workflow_info: workflow_info
|
|
167
|
-
)
|
|
168
|
-
if !result_data.nil? && !result_data["data"].nil? && !result_data["data"]["id"].nil?
|
|
169
|
-
PgyerHelper.share_instace.print_app_version_info(
|
|
170
|
-
app_info_obj: app_info_obj,
|
|
171
|
-
app_version_info_obj: result_data["data"]
|
|
172
|
-
)
|
|
173
|
-
# 始终发送给自己
|
|
174
|
-
PgyerHelper.share_instace.send_apptest_msg(
|
|
175
|
-
app_info_obj: app_info_obj,
|
|
176
|
-
app_version_info_obj: result_data["data"],
|
|
177
|
-
receiveType: "self"
|
|
178
|
-
)
|
|
179
|
-
|
|
180
|
-
# 如果有 --send 参数,额外发送到测试群
|
|
181
|
-
if @args_send_flag
|
|
182
|
-
PgyerHelper.share_instace.send_apptest_msg(
|
|
183
|
-
app_info_obj: app_info_obj,
|
|
184
|
-
app_version_info_obj: result_data["data"],
|
|
185
|
-
chatEnv: "DevTest",
|
|
186
|
-
receiveType: "chat"
|
|
187
|
-
)
|
|
188
|
-
end
|
|
189
|
-
end
|
|
190
|
-
end
|
|
191
|
-
|
|
192
|
-
end
|
|
108
|
+
items
|
|
109
|
+
end
|
|
110
|
+
end
|
|
193
111
|
|
|
112
|
+
# 命令的选项列表
|
|
113
|
+
def self.options
|
|
114
|
+
option_items.map { |item| item.to_claide_option }.concat(super)
|
|
115
|
+
end
|
|
194
116
|
|
|
195
|
-
|
|
117
|
+
def initialize(argv)
|
|
118
|
+
# 首先获取位置参数(向后兼容)
|
|
119
|
+
positional_ipa = argv.shift_argument
|
|
120
|
+
|
|
121
|
+
# 使用 Options 模块初始化参数
|
|
122
|
+
@options = initialize_options(argv)
|
|
123
|
+
|
|
124
|
+
# 优先使用选项参数,如果没有则使用位置参数
|
|
125
|
+
@args_ipa_file = @options[:ipa] || positional_ipa
|
|
126
|
+
@args_build_type = (@options[:type] || 'adhoc').downcase
|
|
127
|
+
@args_upload_flag = @options[:send] || @options[:upload]
|
|
128
|
+
@args_send_flag = @options[:send]
|
|
129
|
+
@args_proj_name = @options[:proj]
|
|
130
|
+
|
|
131
|
+
super
|
|
132
|
+
@additional_args = argv.remainder!
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def validate!
|
|
136
|
+
super
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def run
|
|
140
|
+
begin
|
|
141
|
+
# 加载 JPS 配置(如果存在)
|
|
142
|
+
context = Pindo::PindoContext.instance
|
|
143
|
+
context.load_and_apply_jps_config(Dir.pwd)
|
|
144
|
+
|
|
145
|
+
# 1. 查找或指定 IPA 文件
|
|
146
|
+
ipa_file_path = find_ipa_file()
|
|
147
|
+
|
|
148
|
+
unless ipa_file_path && File.exist?(ipa_file_path)
|
|
149
|
+
raise Informative, "未找到需要重签名的 IPA 文件"
|
|
196
150
|
end
|
|
151
|
+
|
|
152
|
+
# 2. 选择 Bundle ID(根据 build_type)
|
|
153
|
+
bundle_id = select_bundle_id()
|
|
154
|
+
|
|
155
|
+
# 3. 拉取应用配置
|
|
156
|
+
pull_app_config(bundle_id)
|
|
157
|
+
|
|
158
|
+
# 4. 准备 JPS 配置(如果需要上传)
|
|
159
|
+
app_info_obj, workflow_info = prepare_jps_config()
|
|
160
|
+
|
|
161
|
+
# 5. 计算重签名后的 IPA 文件名
|
|
162
|
+
resigned_ipa_file = calculate_resigned_ipa_name(ipa_file_path)
|
|
163
|
+
|
|
164
|
+
# 6. 创建任务
|
|
165
|
+
tasks = create_resign_tasks(
|
|
166
|
+
ipa_file_path: ipa_file_path,
|
|
167
|
+
resigned_ipa_file: resigned_ipa_file,
|
|
168
|
+
bundle_id: bundle_id,
|
|
169
|
+
app_info_obj: app_info_obj,
|
|
170
|
+
workflow_info: workflow_info
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
# 7. 执行任务
|
|
174
|
+
task_manager = Pindo::TaskSystem::TaskManager.instance
|
|
175
|
+
task_manager.clear_all
|
|
176
|
+
tasks.each { |task| task_manager.add_task(task) }
|
|
177
|
+
task_manager.start
|
|
178
|
+
|
|
179
|
+
ensure
|
|
180
|
+
# 清除命令状态(如果启用了缓存,这里会自动保存)
|
|
181
|
+
Pindo::Options::GlobalOptionsState.instance.clear
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
private
|
|
186
|
+
|
|
187
|
+
# 查找 IPA 文件
|
|
188
|
+
def find_ipa_file
|
|
189
|
+
# 1. 如果通过选项或位置参数指定了 IPA 文件且存在,直接返回
|
|
190
|
+
if @args_ipa_file && File.exist?(@args_ipa_file)
|
|
191
|
+
puts "\n使用指定的 IPA 文件: #{@args_ipa_file}"
|
|
192
|
+
puts "修改时间: #{File.mtime(@args_ipa_file)}"
|
|
193
|
+
return @args_ipa_file
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# 2. 在当前目录查找最新的非 _resigned 的 IPA
|
|
197
|
+
current_dir = Dir.pwd
|
|
198
|
+
ipa_files = Dir.glob(File.join(current_dir, "*.ipa"))
|
|
199
|
+
|
|
200
|
+
# 过滤掉 _resigned.ipa 并找到最新的
|
|
201
|
+
ipa_file = ipa_files.max_by do |f|
|
|
202
|
+
File.basename(f).include?("_resigned") ? Time.local(0, 1, 1) : File.mtime(f)
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
if ipa_file && File.exist?(ipa_file)
|
|
206
|
+
puts "\n自动找到 IPA 文件: #{ipa_file}"
|
|
207
|
+
puts "修改时间: #{File.mtime(ipa_file)}"
|
|
208
|
+
return ipa_file
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# 3. 如果没有找到,从终端输入
|
|
212
|
+
puts "\n⚠️ 未找到 IPA 文件"
|
|
213
|
+
puts "请输入 IPA 文件路径(支持相对路径或绝对路径):"
|
|
214
|
+
|
|
215
|
+
ipa_path = ask("IPA 文件路径: ") do |q|
|
|
216
|
+
q.validate = /\.ipa$/i
|
|
217
|
+
q.responses[:not_valid] = "❌ 请输入有效的 IPA 文件路径(必须以 .ipa 结尾)"
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
# 去除首尾空格
|
|
221
|
+
ipa_path = ipa_path.strip if ipa_path
|
|
222
|
+
|
|
223
|
+
# 验证文件是否存在
|
|
224
|
+
if ipa_path && !ipa_path.empty?
|
|
225
|
+
if File.exist?(ipa_path)
|
|
226
|
+
puts "✓ 使用输入的 IPA 文件: #{ipa_path}"
|
|
227
|
+
puts " 修改时间: #{File.mtime(ipa_path)}"
|
|
228
|
+
return ipa_path
|
|
229
|
+
else
|
|
230
|
+
puts "❌ IPA 文件不存在: #{ipa_path}"
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
nil
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
# 选择 Bundle ID(根据 build_type)
|
|
238
|
+
def select_bundle_id
|
|
239
|
+
bundle_id = if @args_build_type == 'dev'
|
|
240
|
+
# dev 类型使用测试 Bundle ID
|
|
241
|
+
get_selected_dev_bundleid()
|
|
242
|
+
else
|
|
243
|
+
# adhoc 类型使用发布 Bundle ID
|
|
244
|
+
get_selected_deploy_bundleid()
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
puts "目标 Bundle ID: #{bundle_id}"
|
|
248
|
+
bundle_id
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
# 拉取应用配置
|
|
252
|
+
def pull_app_config(bundle_id)
|
|
253
|
+
puts "\n拉取应用配置: #{bundle_id}"
|
|
254
|
+
|
|
255
|
+
require 'pindo/config/build_info_manager'
|
|
256
|
+
success = Pindo::BuildInfoManager.share_instance.pull_appconfig_with_reponame(
|
|
257
|
+
repo_name: bundle_id,
|
|
258
|
+
target_dir: Dir.pwd
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
unless success
|
|
262
|
+
raise Informative, "拉取配置失败: #{bundle_id}"
|
|
263
|
+
end
|
|
197
264
|
end
|
|
265
|
+
|
|
266
|
+
# 准备 JPS 配置
|
|
267
|
+
def prepare_jps_config
|
|
268
|
+
if @args_upload_flag || @args_send_flag
|
|
269
|
+
# 只使用命令行参数的 proj_name,不从 config.json 获取
|
|
270
|
+
# skip_config_file: true 会跳过从 config.json 读取项目名称
|
|
271
|
+
# 这样如果用户没有指定 --proj,会直接提示用户输入
|
|
272
|
+
proj_name = @args_proj_name
|
|
273
|
+
|
|
274
|
+
app_info_obj, workflow_info = PgyerHelper.share_instace.get_adhoc_upload_info(
|
|
275
|
+
working_directory: Dir.pwd,
|
|
276
|
+
package_type: 'ipa',
|
|
277
|
+
proj_name: proj_name,
|
|
278
|
+
skip_config_file: true
|
|
279
|
+
)
|
|
280
|
+
return [app_info_obj, workflow_info]
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
[nil, nil]
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
# 计算重签名后的 IPA 文件名
|
|
287
|
+
# 例如:SlimeSavior.ipa → SlimeSavior_resigned.ipa
|
|
288
|
+
def calculate_resigned_ipa_name(ipa_file_path)
|
|
289
|
+
ipa_file_path.gsub(/\.ipa$/, '_resigned.ipa')
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
# 创建任务列表
|
|
293
|
+
def create_resign_tasks(ipa_file_path:, resigned_ipa_file:, bundle_id:, app_info_obj:, workflow_info:)
|
|
294
|
+
tasks = []
|
|
295
|
+
|
|
296
|
+
# 1. 重签名任务
|
|
297
|
+
resign_task = Pindo::TaskSystem::IpaLocalResignTask.new(
|
|
298
|
+
Dir.pwd, # ipa_path(搜索路径)
|
|
299
|
+
ipa_file_path, # ipa_file(明确指定)
|
|
300
|
+
bundle_id, # bundle_id
|
|
301
|
+
build_type: @args_build_type, # dev 或 adhoc
|
|
302
|
+
project_dir: Dir.pwd
|
|
303
|
+
)
|
|
304
|
+
tasks << resign_task
|
|
305
|
+
|
|
306
|
+
# 2. 上传任务(仅在需要时创建)
|
|
307
|
+
if @args_upload_flag && app_info_obj
|
|
308
|
+
upload_task = Pindo::TaskSystem::JPSUploadTask.new(
|
|
309
|
+
'ipa',
|
|
310
|
+
Dir.pwd, # upload_path(搜索路径)
|
|
311
|
+
resigned_ipa_file, # upload_file(明确指定重签名后的文件)
|
|
312
|
+
app_info_obj: app_info_obj,
|
|
313
|
+
workflow_info: workflow_info,
|
|
314
|
+
project_name: @args_proj_name,
|
|
315
|
+
upload_desc: "重签名包", # 上传描述
|
|
316
|
+
context: {
|
|
317
|
+
send_to_chat: @args_send_flag
|
|
318
|
+
},
|
|
319
|
+
dependencies: [resign_task.id] # 依赖重签名任务
|
|
320
|
+
)
|
|
321
|
+
tasks << upload_task
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
tasks
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
end
|
|
198
328
|
end
|
|
329
|
+
end
|
|
199
330
|
end
|
|
@@ -220,6 +220,168 @@ module Pindo
|
|
|
220
220
|
}
|
|
221
221
|
end
|
|
222
222
|
|
|
223
|
+
# AdHoc 专用工作流选择
|
|
224
|
+
# 筛选条件:
|
|
225
|
+
# 1. package_type 匹配
|
|
226
|
+
# 2. tab_name 必须包含 "提交包"
|
|
227
|
+
#
|
|
228
|
+
# @param project_id [String] 项目ID
|
|
229
|
+
# @param package_type [String] 包类型(ipa/apk/zip/app)
|
|
230
|
+
# @return [Hash] 选择的工作流信息
|
|
231
|
+
def select_workflow_for_adhoc(project_id:, package_type:)
|
|
232
|
+
# 1. 从 JPS API 获取可用工作流列表
|
|
233
|
+
workflow_result = @pgyer_client.get_project_workflows(project_id: project_id)
|
|
234
|
+
|
|
235
|
+
if workflow_result.nil? || workflow_result['data'].nil?
|
|
236
|
+
raise Informative, "获取工作流列表失败"
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
workflows = workflow_result['data']
|
|
240
|
+
|
|
241
|
+
if workflows.empty?
|
|
242
|
+
raise Informative, "该项目没有可用的工作流"
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
# 2. 第一层过滤:根据 package_type
|
|
246
|
+
filtered_by_type = workflows.select do |w|
|
|
247
|
+
if w['packageType']
|
|
248
|
+
case package_type
|
|
249
|
+
when 'ipa'
|
|
250
|
+
w['packageType'] == 'ipa'
|
|
251
|
+
when 'apk'
|
|
252
|
+
w['packageType'] == 'apk'
|
|
253
|
+
when 'zip'
|
|
254
|
+
w['packageType'] == 'zip'
|
|
255
|
+
when 'app'
|
|
256
|
+
w['packageType'] == 'app'
|
|
257
|
+
else
|
|
258
|
+
true
|
|
259
|
+
end
|
|
260
|
+
else
|
|
261
|
+
# 如果没有 packageType 字段,保留(后续通过名称筛选)
|
|
262
|
+
true
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
# 3. 第二层过滤:tab_name 必须包含 "提交包"
|
|
267
|
+
adhoc_workflows = filtered_by_type.select do |w|
|
|
268
|
+
tab_name = w['tabName'] || w['tab_name'] || ''
|
|
269
|
+
tab_name.include?('提交包')
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
if adhoc_workflows.empty?
|
|
273
|
+
raise Informative, "未找到符合条件的 AdHoc 工作流\n" \
|
|
274
|
+
"要求:package_type=#{package_type} 且 tab_name 包含 '提交包'\n" \
|
|
275
|
+
"请检查 JPS 工作流配置"
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
# 4. 根据匹配数量决定选择方式
|
|
279
|
+
workflow = nil
|
|
280
|
+
|
|
281
|
+
if adhoc_workflows.size == 1
|
|
282
|
+
# 只有 1 个匹配,自动选择
|
|
283
|
+
workflow = adhoc_workflows.first
|
|
284
|
+
else
|
|
285
|
+
# 有多个匹配,让用户选择
|
|
286
|
+
require 'highline/import'
|
|
287
|
+
cli = HighLine.new
|
|
288
|
+
|
|
289
|
+
workflow_choices = adhoc_workflows.map do |w|
|
|
290
|
+
tab_name = w['tabName'] || w['tab_name']
|
|
291
|
+
workflow_id = w['id'] || w['workflow_id']
|
|
292
|
+
"#{tab_name} (ID: #{workflow_id})"
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
selected = cli.choose do |menu|
|
|
296
|
+
menu.prompt = "请选择 AdHoc 工作流:"
|
|
297
|
+
menu.choices(*workflow_choices)
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
# 提取选择的工作流
|
|
301
|
+
selected_index = workflow_choices.index(selected)
|
|
302
|
+
workflow = adhoc_workflows[selected_index]
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
# 5. 返回标准化的工作流信息
|
|
306
|
+
{
|
|
307
|
+
workflow_id: workflow['id'] || workflow['workflow_id'],
|
|
308
|
+
tab_name: workflow['tabName'] || workflow['tab_name'],
|
|
309
|
+
package_type: workflow['packageType'] || package_type,
|
|
310
|
+
package_name: workflow['packageName'] || workflow['package_name'] || '',
|
|
311
|
+
manage_type: workflow['manageType'] || workflow['manage_type'] || ''
|
|
312
|
+
}
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
# 获取 AdHoc 上传信息
|
|
316
|
+
# 用于 adhoc 编译命令,不保存配置到 JPSBuildConfig.json
|
|
317
|
+
#
|
|
318
|
+
# @param working_directory [String] 工作目录
|
|
319
|
+
# @param package_type [String] 包类型(ipa/apk/zip/app)
|
|
320
|
+
# @param proj_name [String] 项目名称(可选)
|
|
321
|
+
# @param skip_config_file [Boolean] 是否跳过从 config.json 获取项目名称(默认 false)
|
|
322
|
+
# @return [Array] [app_info_obj, workflow_info]
|
|
323
|
+
def get_adhoc_upload_info(working_directory:, package_type:, proj_name: nil, skip_config_file: false)
|
|
324
|
+
upload_proj_name = proj_name
|
|
325
|
+
|
|
326
|
+
# 1. 项目名称获取逻辑
|
|
327
|
+
# 1.1 如果 proj_name 为空,从环境变量获取
|
|
328
|
+
if upload_proj_name.nil? || upload_proj_name.empty?
|
|
329
|
+
upload_proj_name = ENV['PINDO_PROJECT_NAME']
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
# 1.2 如果仍为空且未禁用,从 IosConfigParser 获取
|
|
333
|
+
if (upload_proj_name.nil? || upload_proj_name.empty?) && !skip_config_file
|
|
334
|
+
require 'pindo/config/ios_config_parser'
|
|
335
|
+
config_parser = Pindo::IosConfigParser.instance
|
|
336
|
+
if config_parser.config_json
|
|
337
|
+
upload_proj_name = config_parser.config_json.dig("project_info", "project_name")
|
|
338
|
+
end
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
# 1.3 如果还是为空,让用户输入
|
|
342
|
+
if upload_proj_name.nil? || upload_proj_name.empty?
|
|
343
|
+
require 'highline/import'
|
|
344
|
+
puts "\n未找到项目名称配置"
|
|
345
|
+
upload_proj_name = ask("请输入 JPS 项目名称: ") do |q|
|
|
346
|
+
q.validate = /\S+/
|
|
347
|
+
q.responses[:not_valid] = "项目名称不能为空"
|
|
348
|
+
end
|
|
349
|
+
upload_proj_name = upload_proj_name.strip if upload_proj_name
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
# 2. 确保已登录
|
|
353
|
+
unless login
|
|
354
|
+
raise Informative, "请先登录 JPS 网站"
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
# 3. 获取 app_info_obj
|
|
358
|
+
app_info_obj = find_app_info_with_obj_list(proj_name: upload_proj_name)
|
|
359
|
+
|
|
360
|
+
unless app_info_obj
|
|
361
|
+
raise Informative, "未找到项目: #{upload_proj_name},请检查项目名称是否正确"
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
# 4. 获取 AdHoc 工作流(调用专用函数)
|
|
365
|
+
workflow_info = select_workflow_for_adhoc(
|
|
366
|
+
project_id: app_info_obj["id"],
|
|
367
|
+
package_type: package_type
|
|
368
|
+
)
|
|
369
|
+
|
|
370
|
+
unless workflow_info
|
|
371
|
+
raise Informative, "未能获取 AdHoc 工作流信息"
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
# 5. 输出最终选择的项目和工作流
|
|
375
|
+
puts "\n✓ 项目: #{app_info_obj['projectName']}"
|
|
376
|
+
puts "✓ 工作流: #{workflow_info[:tab_name]}"
|
|
377
|
+
|
|
378
|
+
# 6. 保存项目名称到实例变量
|
|
379
|
+
@proj_name = upload_proj_name
|
|
380
|
+
|
|
381
|
+
# 7. 返回结果(不保存配置到 JPSBuildConfig.json)
|
|
382
|
+
return app_info_obj, workflow_info
|
|
383
|
+
end
|
|
384
|
+
|
|
223
385
|
def prepare_upload(working_directory:nil, proj_name:nil, package_type:nil)
|
|
224
386
|
upload_proj_name = proj_name
|
|
225
387
|
if upload_proj_name.nil? || upload_proj_name.empty?
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
require_relative '../pindo_task'
|
|
2
|
+
require_relative '../task_config'
|
|
3
|
+
|
|
4
|
+
module Pindo
|
|
5
|
+
module TaskSystem
|
|
6
|
+
# IPA 本地重签名任务
|
|
7
|
+
# 使用新的证书和 Bundle ID 对 IPA 文件进行重签名
|
|
8
|
+
class IpaLocalResignTask < PindoTask
|
|
9
|
+
attr_reader :ipa_path, :ipa_file, :bundle_id, :build_type
|
|
10
|
+
|
|
11
|
+
# 任务类型
|
|
12
|
+
def self.task_type
|
|
13
|
+
:resign
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# 重试配置
|
|
17
|
+
def self.default_retry_mode
|
|
18
|
+
RetryMode::DELAYED
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.default_retry_count
|
|
22
|
+
2 # 允许重试 2 次
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.default_retry_delay
|
|
26
|
+
5 # 延迟 5 秒
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# 初始化重签名任务
|
|
30
|
+
# @param ipa_path [String] IPA 搜索路径(当 ipa_file 为 nil 时使用)
|
|
31
|
+
# @param ipa_file [String] 指定的 IPA 文件(nil 表示自动查找)
|
|
32
|
+
# @param bundle_id [String] 目标 Bundle ID
|
|
33
|
+
# @param options [Hash] 选项
|
|
34
|
+
# @option options [String] :build_type 构建类型('dev' 或 'adhoc',默认 'adhoc')
|
|
35
|
+
# @option options [String] :project_dir 项目目录(用于加载 config.json)
|
|
36
|
+
def initialize(ipa_path, ipa_file, bundle_id, options = {})
|
|
37
|
+
@ipa_path = ipa_path # IPA 搜索路径
|
|
38
|
+
@ipa_file = ipa_file # 指定的 IPA 文件(nil 表示自动查找)
|
|
39
|
+
@bundle_id = bundle_id # 目标 Bundle ID
|
|
40
|
+
@build_type = options[:build_type] || 'adhoc'
|
|
41
|
+
@project_dir = options[:project_dir] || Dir.pwd
|
|
42
|
+
|
|
43
|
+
# 设置任务优先级
|
|
44
|
+
options[:priority] ||= TaskPriority::HIGH
|
|
45
|
+
|
|
46
|
+
super("重签名 IPA", options)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# 验证任务参数
|
|
50
|
+
def validate
|
|
51
|
+
# 验证 Bundle ID
|
|
52
|
+
unless @bundle_id && !@bundle_id.empty?
|
|
53
|
+
@error = "缺少必需参数: bundle_id"
|
|
54
|
+
return false
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# 验证 build_type
|
|
58
|
+
unless ['dev', 'adhoc'].include?(@build_type)
|
|
59
|
+
@error = "无效的 build_type: #{@build_type}(必须是 'dev' 或 'adhoc')"
|
|
60
|
+
return false
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# 验证 ipa_path(当 ipa_file 为空时)
|
|
64
|
+
if (@ipa_file.nil? || @ipa_file.empty?)
|
|
65
|
+
unless @ipa_path && !@ipa_path.empty?
|
|
66
|
+
@error = "缺少必需参数: ipa_path(当 ipa_file 未指定时)"
|
|
67
|
+
return false
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
true
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
protected
|
|
75
|
+
|
|
76
|
+
# 执行重签名
|
|
77
|
+
def do_work
|
|
78
|
+
# 1. 确定要重签名的 IPA 文件
|
|
79
|
+
ipa_to_resign = determine_ipa_file
|
|
80
|
+
unless ipa_to_resign && File.exist?(ipa_to_resign)
|
|
81
|
+
raise "未找到需要重签名的 IPA 文件"
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
puts "\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
85
|
+
puts " 正在重签名 IPA"
|
|
86
|
+
puts "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
87
|
+
puts " IPA 文件: #{ipa_to_resign}"
|
|
88
|
+
puts " Bundle ID: #{@bundle_id}"
|
|
89
|
+
puts " 构建类型: #{@build_type}"
|
|
90
|
+
puts " 修改时间: #{File.mtime(ipa_to_resign)}"
|
|
91
|
+
puts "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n"
|
|
92
|
+
|
|
93
|
+
# 2. 加载配置(如果尚未加载)
|
|
94
|
+
load_config_if_needed
|
|
95
|
+
|
|
96
|
+
# 3. 安装证书
|
|
97
|
+
install_certificate
|
|
98
|
+
|
|
99
|
+
# 4. 执行重签名
|
|
100
|
+
require 'pindo/module/xcode/ipa_resign_helper'
|
|
101
|
+
resigned_ipa = Pindo::IpaResignHelper.resign_ipa(
|
|
102
|
+
ipa_file_path: ipa_to_resign,
|
|
103
|
+
bundle_id: @bundle_id
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
# 5. 验证重签名结果
|
|
107
|
+
unless resigned_ipa && File.exist?(resigned_ipa)
|
|
108
|
+
raise "重签名失败:未生成重签名后的 IPA 文件"
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
puts "\n✅ 重签名成功!"
|
|
112
|
+
puts " 重签名后的 IPA: #{resigned_ipa}"
|
|
113
|
+
|
|
114
|
+
# 6. 返回重签名后的 IPA 路径
|
|
115
|
+
@result = resigned_ipa
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
private
|
|
119
|
+
|
|
120
|
+
# 确定要重签名的 IPA 文件
|
|
121
|
+
def determine_ipa_file
|
|
122
|
+
if @ipa_file && !@ipa_file.empty? && File.exist?(@ipa_file)
|
|
123
|
+
# 使用指定的 IPA 文件
|
|
124
|
+
puts " 使用指定的 IPA 文件: #{@ipa_file}"
|
|
125
|
+
return @ipa_file
|
|
126
|
+
else
|
|
127
|
+
# 在 ipa_path 中查找最新的非 _resigned 的 IPA
|
|
128
|
+
find_latest_ipa_file
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# 查找最新的 IPA 文件(排除 _resigned.ipa)
|
|
133
|
+
def find_latest_ipa_file
|
|
134
|
+
unless File.directory?(@ipa_path)
|
|
135
|
+
return nil
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
ipa_files = Dir.glob(File.join(@ipa_path, "*.ipa"))
|
|
139
|
+
|
|
140
|
+
# 过滤掉 _resigned.ipa 文件
|
|
141
|
+
ipa_files = ipa_files.reject { |f| File.basename(f).include?("_resigned") }
|
|
142
|
+
|
|
143
|
+
if ipa_files.empty?
|
|
144
|
+
return nil
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# 返回最新的 IPA 文件
|
|
148
|
+
latest_ipa = ipa_files.max_by { |f| File.mtime(f) }
|
|
149
|
+
puts " 自动查找到 IPA 文件: #{latest_ipa}"
|
|
150
|
+
|
|
151
|
+
latest_ipa
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# 加载配置(如果尚未加载)
|
|
155
|
+
def load_config_if_needed
|
|
156
|
+
require 'pindo/config/ios_config_parser'
|
|
157
|
+
config_parser = Pindo::IosConfigParser.instance
|
|
158
|
+
|
|
159
|
+
# 如果配置已加载,直接返回
|
|
160
|
+
if config_parser.config_json && !config_parser.config_json.empty?
|
|
161
|
+
puts " 使用已加载的配置"
|
|
162
|
+
return
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# 加载 config.json
|
|
166
|
+
config_file = File.join(@project_dir, "config.json")
|
|
167
|
+
if File.exist?(config_file)
|
|
168
|
+
puts " 加载配置文件: #{config_file}"
|
|
169
|
+
config_parser.load_config(config_file: config_file)
|
|
170
|
+
else
|
|
171
|
+
raise "配置文件不存在: #{config_file}"
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# 安装证书
|
|
176
|
+
def install_certificate
|
|
177
|
+
puts "\n📜 安装证书..."
|
|
178
|
+
puts " 证书类型: #{@build_type}"
|
|
179
|
+
|
|
180
|
+
# 调用证书安装命令
|
|
181
|
+
require 'pindo/command/appstore/cert'
|
|
182
|
+
cert_args = ["--build_type=#{@build_type}"]
|
|
183
|
+
Pindo::Command::Appstore::Cert.run(cert_args)
|
|
184
|
+
|
|
185
|
+
puts " ✓ 证书安装完成"
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
end
|
|
@@ -34,6 +34,7 @@ module Pindo
|
|
|
34
34
|
# @option options [Hash] :app_info_obj JPS 应用信息对象(可选,如为 nil 则延迟获取)
|
|
35
35
|
# @option options [Hash] :workflow_info 工作流信息(可选,如为 nil 则延迟获取)
|
|
36
36
|
# @option options [String] :project_name 项目名称(可选)
|
|
37
|
+
# @option options [String] :upload_desc 上传描述(可选)
|
|
37
38
|
def initialize(file_type, upload_path, upload_file, options = {})
|
|
38
39
|
@file_type = file_type # 'ipa' | 'apk' | 'html' | 'app'
|
|
39
40
|
@upload_path = upload_path # 搜索文件的路径
|
|
@@ -43,6 +44,7 @@ module Pindo
|
|
|
43
44
|
@app_info_obj = options[:app_info_obj]
|
|
44
45
|
@workflow_info = options[:workflow_info]
|
|
45
46
|
@project_name = options[:project_name]
|
|
47
|
+
@upload_desc = options[:upload_desc] # 上传描述
|
|
46
48
|
|
|
47
49
|
# 设置上传任务的优先级为 LOW,确保在构建任务之后执行
|
|
48
50
|
options[:priority] ||= TaskPriority::LOW
|
|
@@ -180,38 +182,80 @@ module Pindo
|
|
|
180
182
|
result_data = pgyer_helper.start_upload(
|
|
181
183
|
app_info_obj: @app_info_obj,
|
|
182
184
|
ipa_file_upload: file_path,
|
|
183
|
-
description: @
|
|
185
|
+
description: @upload_desc, # 使用上传描述
|
|
184
186
|
workflow_info: @workflow_info
|
|
185
187
|
)
|
|
186
188
|
|
|
187
|
-
#
|
|
188
|
-
|
|
189
|
-
|
|
189
|
+
# 验证上传结果
|
|
190
|
+
unless result_data && result_data["data"] && result_data["data"]["id"]
|
|
191
|
+
raise "上传失败:未获取到有效的返回数据"
|
|
192
|
+
end
|
|
190
193
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
app_version_info_obj: app_version_info
|
|
195
|
-
)
|
|
194
|
+
# ========== 上传成功,后续操作失败不应导致重试 ==========
|
|
195
|
+
# 将重试次数设为 0,即使后续操作失败也不会重复上传
|
|
196
|
+
@retry_count = 0
|
|
196
197
|
|
|
197
|
-
|
|
198
|
-
pgyer_helper.send_apptest_msg(
|
|
199
|
-
app_info_obj: @app_info_obj,
|
|
200
|
-
app_version_info_obj: app_version_info,
|
|
201
|
-
receiveType: "self"
|
|
202
|
-
)
|
|
198
|
+
app_version_info = result_data["data"]
|
|
203
199
|
|
|
204
|
-
|
|
205
|
-
|
|
200
|
+
# 处理上传成功后的操作
|
|
201
|
+
handle_post_upload_actions(app_version_info)
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
# 处理上传成功后的操作
|
|
205
|
+
# @param app_version_info [Hash] 应用版本信息
|
|
206
|
+
def handle_post_upload_actions(app_version_info)
|
|
207
|
+
puts "\n 📋 开始处理上传后续操作..."
|
|
208
|
+
|
|
209
|
+
begin
|
|
210
|
+
pgyer_helper = PgyerHelper.share_instace
|
|
211
|
+
|
|
212
|
+
# 打印应用版本信息(失败只警告,不中断)
|
|
213
|
+
begin
|
|
214
|
+
puts " 📝 打印应用版本信息..."
|
|
215
|
+
pgyer_helper.print_app_version_info(
|
|
216
|
+
app_info_obj: @app_info_obj,
|
|
217
|
+
app_version_info_obj: app_version_info
|
|
218
|
+
)
|
|
219
|
+
puts " ✓ 应用版本信息打印成功"
|
|
220
|
+
rescue => e
|
|
221
|
+
puts " ⚠️ 打印应用版本信息失败: #{e.message}"
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
# 发送消息给自己(失败只警告,不中断)
|
|
225
|
+
begin
|
|
226
|
+
puts " 📨 发送消息给自己..."
|
|
206
227
|
pgyer_helper.send_apptest_msg(
|
|
207
228
|
app_info_obj: @app_info_obj,
|
|
208
229
|
app_version_info_obj: app_version_info,
|
|
209
|
-
|
|
210
|
-
receiveType: "chat"
|
|
230
|
+
receiveType: "self"
|
|
211
231
|
)
|
|
232
|
+
puts " ✓ 消息发送成功"
|
|
233
|
+
rescue => e
|
|
234
|
+
puts " ⚠️ 发送消息给自己失败: #{e.message}"
|
|
212
235
|
end
|
|
213
|
-
|
|
214
|
-
|
|
236
|
+
|
|
237
|
+
# 如果需要发送到测试群(失败只警告,不中断)
|
|
238
|
+
if @context[:send_to_chat]
|
|
239
|
+
begin
|
|
240
|
+
puts " 📢 发送消息到测试群..."
|
|
241
|
+
pgyer_helper.send_apptest_msg(
|
|
242
|
+
app_info_obj: @app_info_obj,
|
|
243
|
+
app_version_info_obj: app_version_info,
|
|
244
|
+
chatEnv: "DevTest",
|
|
245
|
+
receiveType: "chat"
|
|
246
|
+
)
|
|
247
|
+
puts " ✓ 测试群消息发送成功"
|
|
248
|
+
rescue => e
|
|
249
|
+
puts " ⚠️ 发送消息到测试群失败: #{e.message}"
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
puts " ✅ 上传后续操作全部完成\n"
|
|
254
|
+
rescue => e
|
|
255
|
+
# 外层错误保护:任何未预期的错误都不应影响上传任务的成功状态
|
|
256
|
+
puts " ⚠️ 上传后续操作发生未预期错误: #{e.message}"
|
|
257
|
+
puts " ⚠️ 错误堆栈: #{e.backtrace.first(3).join("\n ")}" if e.backtrace
|
|
258
|
+
puts " ℹ️ 上传已成功,后续操作失败不影响上传结果\n"
|
|
215
259
|
end
|
|
216
260
|
end
|
|
217
261
|
end
|
|
@@ -184,7 +184,7 @@ module Pindo
|
|
|
184
184
|
def sign_ipa_with_new_cert(ipa_name:, bundle_id:)
|
|
185
185
|
require 'pindo/config/pindoconfig'
|
|
186
186
|
|
|
187
|
-
profil_info_array = Pindo::
|
|
187
|
+
profil_info_array = Pindo::Pindoconfig.instance.get_cert_info
|
|
188
188
|
bundle_id_signing_identity = profil_info_array.first['signing_identity']
|
|
189
189
|
|
|
190
190
|
profile_dict = build_resign_profile_dict(profil_info_array: profil_info_array)
|
data/lib/pindo/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: pindo
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 5.13.
|
|
4
|
+
version: 5.13.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- wade
|
|
@@ -441,6 +441,7 @@ files:
|
|
|
441
441
|
- lib/pindo/module/task/model/build/web_build_dev_task.rb
|
|
442
442
|
- lib/pindo/module/task/model/build_task.rb
|
|
443
443
|
- lib/pindo/module/task/model/git_tag_task.rb
|
|
444
|
+
- lib/pindo/module/task/model/ipa_local_resign_task.rb
|
|
444
445
|
- lib/pindo/module/task/model/jps_resign_task.rb
|
|
445
446
|
- lib/pindo/module/task/model/jps_upload_task.rb
|
|
446
447
|
- lib/pindo/module/task/model/unity_export_task.rb
|