pindo 5.11.4 → 5.12.2

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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/lib/pindo/base/githelper.rb +16 -0
  3. data/lib/pindo/base/pindocontext.rb +13 -4
  4. data/lib/pindo/command/android/autobuild.rb +108 -185
  5. data/lib/pindo/command/android/build.rb +10 -2
  6. data/lib/pindo/command/ios/autobuild.rb +116 -213
  7. data/lib/pindo/command/ios/build.rb +12 -3
  8. data/lib/pindo/command/jps/upload.rb +253 -118
  9. data/lib/pindo/command/unity/autobuild.rb +297 -227
  10. data/lib/pindo/command/unity.rb +0 -3
  11. data/lib/pindo/command/utils/boss.rb +18 -15
  12. data/lib/pindo/command/utils/clearcert.rb +26 -18
  13. data/lib/pindo/command/utils/device.rb +28 -19
  14. data/lib/pindo/command/utils/feishu.rb +11 -4
  15. data/lib/pindo/command/utils/icon.rb +26 -20
  16. data/lib/pindo/command/utils/renewcert.rb +35 -29
  17. data/lib/pindo/command/utils/renewproj.rb +32 -25
  18. data/lib/pindo/command/utils/repoinit.rb +1 -1
  19. data/lib/pindo/command/utils/tag.rb +6 -180
  20. data/lib/pindo/command/utils/tgate.rb +34 -28
  21. data/lib/pindo/command/utils/xcassets.rb +30 -20
  22. data/lib/pindo/command/web/autobuild.rb +148 -128
  23. data/lib/pindo/module/android/android_build_helper.rb +0 -6
  24. data/lib/pindo/module/android/android_config_helper.rb +4 -26
  25. data/lib/pindo/module/build/build_helper.rb +18 -294
  26. data/lib/pindo/module/build/git_repo_helper.rb +530 -0
  27. data/lib/pindo/module/build/icon_downloader.rb +85 -0
  28. data/lib/pindo/module/pgyer/pgyerhelper.rb +16 -11
  29. data/lib/pindo/module/task/model/build/android_dev_build_task.rb +209 -0
  30. data/lib/pindo/module/task/model/build/android_release_build_task.rb +29 -0
  31. data/lib/pindo/module/task/model/build/ios_adhoc_build_task.rb +53 -0
  32. data/lib/pindo/module/task/model/build/ios_dev_build_task.rb +251 -0
  33. data/lib/pindo/module/task/model/build/ios_release_build_task.rb +53 -0
  34. data/lib/pindo/module/task/model/build/web_dev_build_task.rb +43 -0
  35. data/lib/pindo/module/task/model/build_task.rb +125 -301
  36. data/lib/pindo/module/task/model/git_tag_task.rb +80 -0
  37. data/lib/pindo/module/task/model/unity_export_task.rb +53 -41
  38. data/lib/pindo/module/task/model/upload_task.rb +149 -208
  39. data/lib/pindo/module/task/pindo_task.rb +135 -95
  40. data/lib/pindo/module/task/task_manager.rb +202 -352
  41. data/lib/pindo/module/unity/unity_helper.rb +7 -3
  42. data/lib/pindo/module/xcode/xcode_build_config.rb +4 -10
  43. data/lib/pindo/module/xcode/xcode_build_helper.rb +19 -0
  44. data/lib/pindo/version.rb +1 -1
  45. metadata +10 -4
  46. data/lib/pindo/command/unity/apk.rb +0 -185
  47. data/lib/pindo/command/unity/ipa.rb +0 -198
  48. data/lib/pindo/command/unity/web.rb +0 -163
@@ -3,359 +3,183 @@ require_relative '../task_config'
3
3
 
4
4
  module Pindo
5
5
  module TaskSystem
6
+ # BuildTask 抽象基类
7
+ # 提供构建任务的通用框架和工厂方法
6
8
  class BuildTask < PindoTask
7
- attr_reader :platform, :build_type, :output_path
9
+ attr_reader :platform, :project_path, :output_path, :mode
8
10
 
11
+ # 任务类型
9
12
  def self.task_type
10
13
  :build
11
14
  end
12
15
 
13
- def self.execution_mode
14
- ExecutionMode::TYPE_EXCLUSIVE # 同类型串行(不能同时构建多个)
16
+ # 重试配置
17
+ def self.default_retry_mode
18
+ RetryMode::DELAYED
15
19
  end
16
20
 
17
- def self.execution_type
18
- ExecutionType::ASYNC # 在新线程异步执行
21
+ def self.default_retry_count
22
+ 0
19
23
  end
20
24
 
21
- def initialize(platform, options = {})
22
- @platform = platform
23
- @build_type = options[:build_type] || platform
24
- @project_path = options[:project_path] || Dir.pwd
25
- @build_config = options[:build_config] || {}
26
- @output_path = nil
27
-
28
- name = case platform
29
- when 'ios', 'ipa'
30
- "构建 IPA"
31
- when 'android', 'apk'
32
- "构建 APK"
33
- when 'web', 'html'
34
- "构建 HTML"
35
- else
36
- "构建 #{platform.upcase}"
37
- end
38
-
39
- super(name, options)
25
+ def self.default_retry_delay
26
+ 10
40
27
  end
41
28
 
42
- protected
43
-
44
- def do_work
45
- update_progress(0, "准备构建环境...")
46
-
47
- case @platform
48
- when 'ios', 'ipa'
49
- build_ios
50
- when 'android', 'apk'
51
- build_android
52
- when 'web', 'html'
53
- build_web
29
+ # ========== 静态工厂方法 ==========
30
+
31
+ # 根据 platform 和 mode 创建对应的构建任务实例
32
+ # @param platform [String, Symbol] 平台:ios/android/web
33
+ # @param mode [String, Symbol] 模式:dev/adhoc/release
34
+ # @param options [Hash] 构建选项
35
+ # @return [BuildTask] 对应的构建任务实例
36
+ def self.create_task(platform:, mode:, options: {})
37
+ normalized_platform = normalize_platform(platform)
38
+ normalized_mode = normalize_mode(mode)
39
+
40
+ case normalized_platform
41
+ when :ios
42
+ create_ios_task(normalized_mode, options)
43
+ when :android
44
+ create_android_task(normalized_mode, options)
45
+ when :web
46
+ create_web_task(normalized_mode, options)
54
47
  else
55
- raise "Unsupported platform: #{@platform}"
48
+ raise ArgumentError, "不支持的平台: #{platform}"
56
49
  end
57
50
  end
58
51
 
59
- private
60
-
61
- def build_ios
62
- update_progress(10, "配置 iOS 工程...")
63
-
64
- # 获取 Bundle ID
65
- mainapp_bundleid = @context[:bundle_id] || @build_config[:bundle_id]
66
-
67
- unless mainapp_bundleid
68
- raise "iOS Bundle ID 未指定"
52
+ # 标准化平台名称
53
+ def self.normalize_platform(platform)
54
+ platform_str = platform.to_s.downcase
55
+ case platform_str
56
+ when 'ios', 'ipa'
57
+ :ios
58
+ when 'android', 'apk', 'aab'
59
+ :android
60
+ when 'web', 'html', 'webgl'
61
+ :web
62
+ else
63
+ raise ArgumentError, "未知平台: #{platform}"
69
64
  end
65
+ end
70
66
 
71
- update_progress(20, "准备编译参数...")
72
-
73
- args_temp = []
74
- args_temp << "--bundleid=#{mainapp_bundleid}"
75
-
76
- # 不传递 upload 参数,只构建
77
- if @context[:project_name]
78
- args_temp << "--proj=#{@context[:project_name]}"
67
+ # 标准化构建模式
68
+ def self.normalize_mode(mode)
69
+ mode_str = mode.to_s.downcase
70
+ case mode_str
71
+ when 'dev', 'debug', 'development'
72
+ :dev
73
+ when 'adhoc', 'ad-hoc'
74
+ :adhoc
75
+ when 'release', 'production', 'deploy'
76
+ :release
77
+ else
78
+ raise ArgumentError, "未知构建模式: #{mode}"
79
79
  end
80
+ end
80
81
 
81
- update_progress(30, "执行 iOS 构建...")
82
-
83
- # 切换到 iOS 目录并执行构建
84
- ios_dir = File.join(@project_path, "GoodPlatform", "iOS")
85
- if File.directory?(ios_dir)
86
- Dir.chdir(ios_dir) do
87
- # 调用 iOS autobuild 命令(不包含上传)
88
- require 'pindo/command/ios/autobuild'
89
- Pindo::Command::Ios::Autobuild::run(args_temp)
90
- end
82
+ # 创建 iOS 构建任务
83
+ def self.create_ios_task(mode, options)
84
+ require_relative 'build/ios_dev_build_task'
85
+ require_relative 'build/ios_adhoc_build_task'
86
+ require_relative 'build/ios_release_build_task'
87
+
88
+ case mode
89
+ when :dev
90
+ IosDevBuildTask.new(options)
91
+ when :adhoc
92
+ IosAdhocBuildTask.new(options)
93
+ when :release
94
+ IosReleaseBuildTask.new(options)
91
95
  else
92
- # 如果没有 iOS 子目录,在当前目录执行
93
- require 'pindo/command/ios/autobuild'
94
- Pindo::Command::Ios::Autobuild::run(args_temp)
96
+ raise ArgumentError, "不支持的 iOS 构建模式: #{mode}"
95
97
  end
96
-
97
- update_progress(90, "iOS 构建完成")
98
-
99
- # TODO: 获取实际的输出路径
100
- @output_path = find_ipa_output_path
101
-
102
- update_progress(100, "IPA 构建成功")
103
- {
104
- success: true,
105
- platform: 'ios',
106
- output: @output_path
107
- }
108
98
  end
109
99
 
110
- def build_android
111
- update_progress(10, "配置 Android 工程...")
112
-
113
- # 获取 Bundle Name
114
- bundle_name = @context[:bundle_name] || @build_config[:bundle_name]
115
-
116
- update_progress(20, "准备编译参数...")
117
-
118
- args_temp = []
119
-
120
- if bundle_name
121
- args_temp << "--bundle_name=#{bundle_name}"
122
- end
100
+ # 创建 Android 构建任务
101
+ def self.create_android_task(mode, options)
102
+ require_relative 'build/android_dev_build_task'
103
+ require_relative 'build/android_release_build_task'
123
104
 
124
- # 不传递 upload 参数,只构建
125
- if @context[:project_name]
126
- args_temp << "--proj=#{@context[:project_name]}"
105
+ case mode
106
+ when :dev, :debug
107
+ AndroidDevBuildTask.new(options)
108
+ when :release
109
+ AndroidReleaseBuildTask.new(options)
110
+ else
111
+ raise ArgumentError, "不支持的 Android 构建模式: #{mode}"
127
112
  end
113
+ end
128
114
 
129
- update_progress(30, "执行 Android 构建...")
115
+ # 创建 Web 构建任务
116
+ def self.create_web_task(mode, options)
117
+ require_relative 'build/web_dev_build_task'
130
118
 
131
- # 切换到 Android 目录并执行构建
132
- android_dir = File.join(@project_path, "GoodPlatform", "Android")
133
- if File.directory?(android_dir)
134
- Dir.chdir(android_dir) do
135
- # 调用 Android autobuild 命令(不包含上传)
136
- require 'pindo/command/android/autobuild'
137
- Pindo::Command::Android::Autobuild::run(args_temp)
138
- end
119
+ case mode
120
+ when :dev, :debug, :release
121
+ # Web 目前只有一个构建任务类
122
+ WebDevBuildTask.new(options)
139
123
  else
140
- # 如果没有 Android 子目录,在当前目录执行
141
- require 'pindo/command/android/autobuild'
142
- Pindo::Command::Android::Autobuild::run(args_temp)
124
+ raise ArgumentError, "不支持的 Web 构建模式: #{mode}"
143
125
  end
144
-
145
- update_progress(90, "Android 构建完成")
146
-
147
- # TODO: 获取实际的输出路径
148
- @output_path = find_apk_output_path
149
-
150
- update_progress(100, "APK 构建成功")
151
- {
152
- success: true,
153
- platform: 'android',
154
- output: @output_path
155
- }
156
126
  end
157
127
 
158
- def build_web
159
- update_progress(10, "配置 Web 工程...")
160
-
161
- # 获取 Bundle Name (Web 也可能需要)
162
- bundle_name = @context[:bundle_name] || @build_config[:bundle_name]
128
+ # ========== 实例方法 ==========
163
129
 
164
- update_progress(20, "准备编译参数...")
165
-
166
- args_temp = []
130
+ def initialize(platform, mode, options = {})
131
+ @platform = platform
132
+ @mode = mode
133
+ @project_path = options[:project_path] || Dir.pwd
134
+ @output_path = nil
167
135
 
168
- if bundle_name
169
- args_temp << "--bundle_name=#{bundle_name}"
170
- end
136
+ # 构建任务名称
137
+ name = build_task_name
171
138
 
172
- # 不传递 upload 参数,只构建
173
- if @context[:project_name]
174
- args_temp << "--proj=#{@context[:project_name]}"
175
- end
139
+ super(name, options)
140
+ end
176
141
 
177
- update_progress(30, "执行 Web 构建...")
142
+ protected
178
143
 
179
- # 切换到 Web 目录并执行构建
180
- web_dir = File.join(@project_path, "GoodPlatform", "Web")
181
- if File.directory?(web_dir)
182
- Dir.chdir(web_dir) do
183
- # TODO: 需要确认 Web autobuild 命令的正确模块
184
- # 暂时使用 Android::Autobuild(根据原代码)
185
- require 'pindo/command/android/autobuild'
186
- Pindo::Command::Android::Autobuild::run(args_temp)
187
- end
188
- else
189
- # 如果没有 Web 子目录,在当前目录执行
190
- require 'pindo/command/android/autobuild'
191
- Pindo::Command::Android::Autobuild::run(args_temp)
192
- end
144
+ # 主执行流程
145
+ def do_work
146
+ # 准备构建
147
+ prepare_build
193
148
 
194
- update_progress(90, "Web 构建完成")
149
+ # 执行构建
150
+ execute_build
195
151
 
196
- # TODO: 获取实际的输出路径
197
- @output_path = find_web_output_path
152
+ # 查找输出文件
153
+ @output_path = find_output
198
154
 
199
- update_progress(100, "HTML 构建成功")
200
155
  {
201
156
  success: true,
202
- platform: 'web',
157
+ platform: @platform,
203
158
  output: @output_path
204
159
  }
205
160
  end
206
161
 
207
- # 查找 IPA 输出路径
208
- def find_ipa_output_path
209
- # 记录查找前的时间,用于过滤旧文件
210
- start_time = @started_at || Time.now - 60
211
-
212
- # 使用配置中的搜索路径
213
- search_paths = TaskConfig::BUILD_OUTPUT_PATTERNS[:ipa].map do |pattern|
214
- File.join(@project_path, pattern)
215
- end
216
-
217
- ipa_files = []
218
- search_paths.each do |pattern|
219
- files = Dir.glob(pattern)
220
- # 过滤出在构建开始后创建或修改的文件
221
- recent_files = files.select { |f| File.mtime(f) >= start_time }
222
- ipa_files.concat(recent_files)
223
- end
162
+ # ========== 子类必须实现的抽象方法 ==========
224
163
 
225
- if ipa_files.empty?
226
- # 如果没有找到新文件,查找最近的 IPA 文件
227
- search_paths.each do |pattern|
228
- ipa_files.concat(Dir.glob(pattern))
229
- end
230
- end
231
-
232
- # 返回最新的 IPA 文件
233
- if ipa_files.any?
234
- latest_ipa = ipa_files.max_by { |f| File.mtime(f) }
235
- puts " 找到 IPA 文件: #{latest_ipa}"
236
- latest_ipa
237
- else
238
- puts " 警告: 未找到 IPA 文件"
239
- nil
240
- end
164
+ # 构建任务名称
165
+ def build_task_name
166
+ raise NotImplementedError, "子类必须实现 build_task_name 方法"
241
167
  end
242
168
 
243
- # 查找 APK 输出路径
244
- def find_apk_output_path
245
- # 记录查找前的时间,用于过滤旧文件
246
- start_time = @started_at || Time.now - 60
247
-
248
- # 使用配置中的搜索路径
249
- search_paths = []
250
- search_paths.concat(TaskConfig::BUILD_OUTPUT_PATTERNS[:apk].map { |p| File.join(@project_path, p) })
251
- search_paths.concat(TaskConfig::BUILD_OUTPUT_PATTERNS[:aab].map { |p| File.join(@project_path, p) })
252
-
253
- package_files = []
254
- search_paths.each do |pattern|
255
- files = Dir.glob(pattern)
256
- # 过滤出在构建开始后创建或修改的文件
257
- recent_files = files.select { |f| File.mtime(f) >= start_time }
258
- package_files.concat(recent_files)
259
- end
260
-
261
- if package_files.empty?
262
- # 如果没有找到新文件,查找最近的包文件
263
- search_paths.each do |pattern|
264
- package_files.concat(Dir.glob(pattern))
265
- end
266
- end
267
-
268
- # 过滤掉测试包和未签名包
269
- package_files.reject! do |f|
270
- basename = File.basename(f).downcase
271
- TaskConfig::EXCLUDED_PATTERNS.any? { |pattern| basename.include?(pattern) }
272
- end
273
-
274
- # 返回最新的包文件(优先 AAB,其次 APK)
275
- if package_files.any?
276
- # 分类文件
277
- aab_files = package_files.select { |f| f.end_with?(".aab") }
278
- apk_files = package_files.select { |f| f.end_with?(".apk") }
279
-
280
- # 优先返回 AAB 文件
281
- latest_package = if aab_files.any?
282
- aab_files.max_by { |f| File.mtime(f) }
283
- elsif apk_files.any?
284
- apk_files.max_by { |f| File.mtime(f) }
285
- else
286
- package_files.max_by { |f| File.mtime(f) }
287
- end
288
-
289
- puts " 找到 Android 包文件: #{latest_package}"
290
- latest_package
291
- else
292
- puts " 警告: 未找到 APK/AAB 文件"
293
- nil
294
- end
169
+ # 准备构建环境
170
+ def prepare_build
171
+ raise NotImplementedError, "子类必须实现 prepare_build 方法"
295
172
  end
296
173
 
297
- # 查找 Web 输出路径
298
- def find_web_output_path
299
- # 记录查找前的时间,用于过滤旧文件
300
- start_time = @started_at || Time.now - 60
301
-
302
- # 可能的 Web 构建输出位置(通常是文件夹)
303
- search_paths = [
304
- File.join(@project_path, "build", "web"),
305
- File.join(@project_path, "build", "webgl"),
306
- File.join(@project_path, "output", "web"),
307
- File.join(@project_path, "output", "webgl"),
308
- File.join(@project_path, "GoodPlatform", "Web"),
309
- File.join(@project_path, "GoodPlatform", "WebGL"),
310
- File.join(@project_path, "Build", "WebGL"),
311
- File.join(@project_path, "dist"),
312
- File.join(@project_path, "public")
313
- ]
314
-
315
- # 查找存在且最近修改的文件夹
316
- web_dirs = search_paths.select do |path|
317
- File.directory?(path) && File.mtime(path) >= start_time
318
- end
319
-
320
- if web_dirs.empty?
321
- # 如果没有找到新文件夹,查找包含 index.html 的文件夹
322
- web_dirs = search_paths.select do |path|
323
- File.directory?(path) && File.exist?(File.join(path, "index.html"))
324
- end
325
- end
326
-
327
- # 如果还是没找到,查找 zip 文件
328
- if web_dirs.empty?
329
- zip_patterns = [
330
- File.join(@project_path, "build", "**", "*web*.zip"),
331
- File.join(@project_path, "output", "**", "*web*.zip"),
332
- File.join(@project_path, "**", "*webgl*.zip")
333
- ]
334
-
335
- zip_files = []
336
- zip_patterns.each do |pattern|
337
- files = Dir.glob(pattern)
338
- recent_files = files.select { |f| File.mtime(f) >= start_time }
339
- zip_files.concat(recent_files)
340
- end
341
-
342
- if zip_files.any?
343
- latest_zip = zip_files.max_by { |f| File.mtime(f) }
344
- puts " 找到 Web 压缩包: #{latest_zip}"
345
- return latest_zip
346
- end
347
- end
174
+ # 执行构建
175
+ def execute_build
176
+ raise NotImplementedError, "子类必须实现 execute_build 方法"
177
+ end
348
178
 
349
- # 返回最新的 Web 目录
350
- if web_dirs.any?
351
- latest_dir = web_dirs.max_by { |d| File.mtime(d) }
352
- puts " 找到 Web 输出目录: #{latest_dir}"
353
- latest_dir
354
- else
355
- puts " 警告: 未找到 Web 输出文件或目录"
356
- nil
357
- end
179
+ # 查找输出文件
180
+ def find_output
181
+ raise NotImplementedError, "子类必须实现 find_output 方法"
358
182
  end
359
183
  end
360
184
  end
361
- end
185
+ end
@@ -0,0 +1,80 @@
1
+ require_relative '../pindo_task'
2
+ require 'pindo/module/build/git_repo_helper'
3
+
4
+ module Pindo
5
+ module TaskSystem
6
+ # Git 标签任务
7
+ # 检查并创建 Git 标签
8
+ class GitTagTask < PindoTask
9
+ attr_reader :project_path
10
+
11
+ def self.task_type
12
+ :git_tag
13
+ end
14
+
15
+ # 重试配置
16
+ def self.default_retry_mode
17
+ RetryMode::IMMEDIATE # 立即重试
18
+ end
19
+
20
+ def self.default_retry_count
21
+ 0 # 可以重试 0 次
22
+ end
23
+
24
+ def self.default_retry_delay
25
+ 5 # 默认延迟 5 秒
26
+ end
27
+
28
+ # 初始化 Git 标签任务
29
+ # @param project_path [String] 项目路径
30
+ # @param options [Hash] 选项
31
+ def initialize(project_path, options = {})
32
+ @project_path = project_path
33
+
34
+ super("Git 标签检查", options)
35
+ end
36
+
37
+ def validate
38
+ # 验证项目路径是否存在
39
+ unless @project_path && Dir.exist?(@project_path)
40
+ @error = "项目路径不存在:#{@project_path}"
41
+ return false
42
+ end
43
+
44
+ true
45
+ end
46
+
47
+ protected
48
+
49
+ def do_work
50
+ git_repo_helper = Pindo::GitRepoHelper.share_instance
51
+
52
+ # 检查是否需要打标签
53
+ is_need_add_tag, tag_action_params = git_repo_helper.check_is_need_add_tag?(@project_path)
54
+
55
+ if is_need_add_tag
56
+ # 直接调用 GitRepoHelper 创建标签
57
+ git_repo_helper.create_and_push_tag(
58
+ project_dir: @project_path,
59
+ mode: 'minor',
60
+ force_retag: tag_action_params&.include?('--retag') || false,
61
+ custom_tag: nil,
62
+ release_branch: 'master'
63
+ )
64
+
65
+ {
66
+ success: true,
67
+ tag_added: true,
68
+ tag_params: tag_action_params
69
+ }
70
+ else
71
+ {
72
+ success: true,
73
+ tag_added: false,
74
+ message: "无需添加 Git 标签"
75
+ }
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end