pindo 5.17.5 → 5.18.4

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 (60) hide show
  1. checksums.yaml +4 -4
  2. data/lib/pindo/base/git_handler.rb +120 -38
  3. data/lib/pindo/command/android/autobuild.rb +81 -40
  4. data/lib/pindo/command/appstore/adhocbuild.rb +1 -1
  5. data/lib/pindo/command/appstore/autobuild.rb +1 -1
  6. data/lib/pindo/command/appstore/autoresign.rb +1 -1
  7. data/lib/pindo/command/appstore/updateid.rb +229 -0
  8. data/lib/pindo/command/appstore.rb +1 -0
  9. data/lib/pindo/command/ios/autobuild.rb +70 -33
  10. data/lib/pindo/command/ios/podpush.rb +1 -1
  11. data/lib/pindo/command/jps/apptest.rb +2 -2
  12. data/lib/pindo/command/jps/bind.rb +1 -1
  13. data/lib/pindo/command/jps/media.rb +1 -1
  14. data/lib/pindo/command/jps/upload.rb +52 -22
  15. data/lib/pindo/command/unity/autobuild.rb +61 -44
  16. data/lib/pindo/command/utils/allcopyconfig.rb +144 -0
  17. data/lib/pindo/command/utils/copyconfig.rb +207 -0
  18. data/lib/pindo/command/utils/icon.rb +2 -2
  19. data/lib/pindo/command/utils/renewbundleid.rb +199 -0
  20. data/lib/pindo/command/utils/renewcert.rb +56 -54
  21. data/lib/pindo/command/utils.rb +3 -0
  22. data/lib/pindo/command/web/autobuild.rb +32 -26
  23. data/lib/pindo/config/build_info_manager.rb +1 -3
  24. data/lib/pindo/module/android/android_build_helper.rb +193 -33
  25. data/lib/pindo/module/android/android_config_helper.rb +305 -88
  26. data/lib/pindo/module/android/android_project_helper.rb +69 -14
  27. data/lib/pindo/module/android/android_res_helper.rb +349 -51
  28. data/lib/pindo/module/android/keystore_helper.rb +611 -295
  29. data/lib/pindo/module/android/workflow_gradle_injector.rb +702 -0
  30. data/lib/pindo/module/appselect.rb +4 -4
  31. data/lib/pindo/module/appstore/bundleid_helper.rb +204 -14
  32. data/lib/pindo/module/build/build_helper.rb +110 -10
  33. data/lib/pindo/module/build/git_repo_helper.rb +4 -4
  34. data/lib/pindo/module/cert/mode/base_cert_operator.rb +8 -6
  35. data/lib/pindo/module/pgyer/pgyerhelper.rb +105 -42
  36. data/lib/pindo/module/task/core/task_executor.rb +2 -0
  37. data/lib/pindo/module/task/model/build/android_build_dev_task.rb +64 -6
  38. data/lib/pindo/module/task/model/git/git_commit_task.rb +70 -54
  39. data/lib/pindo/module/task/model/git/git_tag_task.rb +13 -9
  40. data/lib/pindo/module/task/model/jps/jps_upload_task.rb +104 -1
  41. data/lib/pindo/module/task/model/unity/unity_export_task.rb +2 -1
  42. data/lib/pindo/module/task/model/unity/unity_update_task.rb +2 -1
  43. data/lib/pindo/module/task/model/unity/unity_yoo_asset_task.rb +2 -1
  44. data/lib/pindo/module/task/model/unity_task.rb +2 -1
  45. data/lib/pindo/module/task/task_manager.rb +8 -0
  46. data/lib/pindo/module/unity/unity_helper.rb +13 -10
  47. data/lib/pindo/module/unity/unity_proc_helper.rb +27 -2
  48. data/lib/pindo/module/xcode/applovin_xcode_helper.rb +4 -2
  49. data/lib/pindo/module/xcode/res/xcode_res_constant.rb +72 -0
  50. data/lib/pindo/module/xcode/xcode_build_config.rb +36 -17
  51. data/lib/pindo/module/xcode/xcode_build_helper.rb +180 -23
  52. data/lib/pindo/module/xcode/xcode_project_helper.rb +1 -1
  53. data/lib/pindo/module/xcode/xcode_res_helper.rb +32 -16
  54. data/lib/pindo/options/groups/build_options.rb +16 -5
  55. data/lib/pindo/options/groups/git_options.rb +7 -5
  56. data/lib/pindo/options/groups/unity_options.rb +11 -0
  57. data/lib/pindo/options/helpers/bundleid_selector.rb +25 -0
  58. data/lib/pindo/options/helpers/git_constants.rb +7 -6
  59. data/lib/pindo/version.rb +2 -2
  60. metadata +10 -5
@@ -113,10 +113,10 @@ module Pindo
113
113
  end
114
114
  end
115
115
 
116
- # 获取 Target 名称映射
116
+ # 获取扩展 Target 名称后缀到 config key 的映射
117
+ # 注意:MainTarget 不在此映射中,主应用 target 直接使用 app_identifier
117
118
  def get_target_name_map
118
119
  return {
119
- "MainTarget" => "bundle_id",
120
120
  "Content" => "bundle_id_pushcontent",
121
121
  "Service" => "bundle_id_pushservice",
122
122
  "Keyboard" => "bundle_id_keyboard",
@@ -150,19 +150,40 @@ module Pindo
150
150
  exe_binary_name = exe_binary_name.gsub(/ /, '');
151
151
  exe_binary_name = exe_binary_name.gsub(/\'/, '');
152
152
 
153
+ target_name_map = get_target_name_map
154
+
155
+ # 根据 target 类型确定 bundle_id
156
+ target_bundle_id = nil
157
+ if target.product_type.include?(Xcodeproj::Constants::PRODUCT_TYPE_UTI[:application])
158
+ # 主应用 target:使用 app_identifier
159
+ target_bundle_id = config_json['app_info']['app_identifier']
160
+ else
161
+ # 扩展 target:从 target_name_map 查找对应的 bundle_id
162
+ # target_name_map 的 value 是 bundle_id_* 格式,config_json 中实际字段名是 app_identifier_*
163
+ target_name_map.each do |suffix, config_key|
164
+ if target.name.to_s.end_with?(suffix)
165
+ json_key = config_key.sub('bundle_id_', 'app_identifier_')
166
+ target_bundle_id = config_json['app_info'][json_key]
167
+ break
168
+ end
169
+ end
170
+ end
171
+
153
172
  target.build_configurations.each do |config|
154
173
  config.build_settings['CURRENT_PROJECT_VERSION'] = config_json['app_info']['app_build_version']
155
174
  config.build_settings['MARKETING_VERSION'] = config_json['app_info']['app_version']
156
175
  config.build_settings['INFOPLIST_KEY_CFBundleDisplayName'] = config_json['app_info']['app_display_name']
176
+ if target_bundle_id && !target_bundle_id.empty? && !target_bundle_id.include?("*")
177
+ config.build_settings['PRODUCT_BUNDLE_IDENTIFIER'] = target_bundle_id
178
+ end
157
179
  end
158
180
 
159
- target_name_map = get_target_name_map
160
- if target.product_type.include?(Xcodeproj::Constants::PRODUCT_TYPE_UTI[:application])
181
+ if target.product_type.include?(Xcodeproj::Constants::PRODUCT_TYPE_UTI[:application])
161
182
  target.build_configurations.each do |config|
162
183
  config.build_settings['PRODUCT_NAME'] = exe_binary_name
163
184
  config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = ios_deployment_targe
164
185
  end
165
- elsif
186
+ else
166
187
  target_name_map.each do |k, v|
167
188
  if target.name.to_s.end_with?(k)
168
189
  target.build_configurations.each do |config|
@@ -323,35 +344,171 @@ module Pindo
323
344
 
324
345
  end
325
346
 
347
+ # 解析 INFOPLIST_FILE 路径,处理 Xcode 变量和引号
348
+ # @param project_dir [String] 项目目录
349
+ # @param raw_path [String] INFOPLIST_FILE 原始值
350
+ # @param target [Xcodeproj::Project::Object::PBXNativeTarget, nil] target(用于解析 TARGET_NAME 等变量)
351
+ # @return [String, nil] 解析后的绝对路径,文件不存在时返回 nil
352
+ def resolve_info_plist_path(project_dir, raw_path, target: nil)
353
+ return nil if raw_path.nil? || raw_path.empty?
354
+
355
+ # 去除首尾引号
356
+ resolved = raw_path.gsub(/\A["']|["']\z/, '')
357
+
358
+ # 解析 Xcode 变量
359
+ resolved = resolved
360
+ .gsub('$(SRCROOT)', project_dir)
361
+ .gsub('${SRCROOT}', project_dir)
362
+ .gsub('$(PROJECT_DIR)', project_dir)
363
+ .gsub('${PROJECT_DIR}', project_dir)
364
+
365
+ # 解析 target 相关变量
366
+ if target
367
+ target_name = target.name.to_s
368
+ resolved = resolved
369
+ .gsub('$(TARGET_NAME)', target_name)
370
+ .gsub('${TARGET_NAME}', target_name)
371
+
372
+ # PRODUCT_NAME 从 build settings 获取,降级到 TARGET_NAME
373
+ product_name = target.build_configurations.first.build_settings['PRODUCT_NAME'] || target_name
374
+ # PRODUCT_NAME 本身可能是 $(TARGET_NAME),递归替换
375
+ product_name = product_name
376
+ .gsub('$(TARGET_NAME)', target_name)
377
+ .gsub('${TARGET_NAME}', target_name)
378
+ resolved = resolved
379
+ .gsub('$(PRODUCT_NAME)', product_name)
380
+ .gsub('${PRODUCT_NAME}', product_name)
381
+ end
382
+
383
+ # 如果仍有未解析的变量,尝试去掉变量部分做 glob 匹配
384
+ if resolved.match?(/\$[\({]/)
385
+ # 将未解析的变量替换为通配符,尝试 glob 查找
386
+ glob_pattern = resolved.gsub(/\$[\({][^)\}]+[\)}]/, '*')
387
+ glob_path = File.absolute_path?(glob_pattern) ? glob_pattern : File.join(project_dir, glob_pattern)
388
+ matches = Dir.glob(glob_path)
389
+ return matches.first if matches.size == 1
390
+ if matches.size > 1
391
+ Funlog.instance.fancyinfo_warning("INFOPLIST_FILE 变量解析后匹配到多个文件: #{matches.join(', ')},跳过")
392
+ end
393
+ return nil
394
+ end
395
+
396
+ # 如果是相对路径,拼接 project_dir
397
+ abs_path = File.absolute_path?(resolved) ? resolved : File.join(project_dir, resolved)
398
+ File.exist?(abs_path) ? abs_path : nil
399
+ end
400
+
401
+ # 确保 Info.plist 文件存在,不存在时自动创建并配置 Xcode 工程
402
+ # 仅在需要写入复杂配置(URL Schemes 等)时调用
403
+ # @param project_dir [String] 项目目录
404
+ # @param target [Xcodeproj::Project::Object::PBXNativeTarget] Xcode target
405
+ # @param project_obj [Xcodeproj::Project] Xcode 工程对象
406
+ # @return [String, nil] Info.plist 文件路径
407
+ def ensure_info_plist_exists(project_dir:, target:, project_obj:)
408
+ # 1. 尝试从 build settings 获取已有路径
409
+ raw_path = target.build_configurations.first.build_settings['INFOPLIST_FILE']
410
+ existing_path = resolve_info_plist_path(project_dir, raw_path, target: target)
411
+ return existing_path if existing_path
412
+
413
+ # 2. 检查是否是 GENERATE_INFOPLIST_FILE=YES 的合法模式
414
+ generate_flag = target.build_configurations.first.build_settings['GENERATE_INFOPLIST_FILE']
415
+ is_generated_mode = (generate_flag.to_s.upcase == 'YES') && (raw_path.nil? || raw_path.empty?)
416
+
417
+ # 3. 创建 Info.plist(GENERATE_INFOPLIST_FILE 模式下作为补充 plist,Xcode 会合并)
418
+ target_name = target.name.to_s
419
+ # 优先使用原始 INFOPLIST_FILE 路径(用已知变量替换后)创建,避免覆盖工程配置
420
+ if raw_path && !raw_path.empty?
421
+ # 用 resolve 中相同的变量替换逻辑解析路径(不检查文件是否存在)
422
+ resolved_raw = raw_path
423
+ .gsub(/\A["']|["']\z/, '')
424
+ .gsub('$(SRCROOT)', project_dir).gsub('${SRCROOT}', project_dir)
425
+ .gsub('$(PROJECT_DIR)', project_dir).gsub('${PROJECT_DIR}', project_dir)
426
+ .gsub('$(TARGET_NAME)', target_name).gsub('${TARGET_NAME}', target_name)
427
+ product_name = target.build_configurations.first.build_settings['PRODUCT_NAME'] || target_name
428
+ product_name = product_name.gsub('$(TARGET_NAME)', target_name).gsub('${TARGET_NAME}', target_name)
429
+ resolved_raw = resolved_raw.gsub('$(PRODUCT_NAME)', product_name).gsub('${PRODUCT_NAME}', product_name)
430
+
431
+ # 如果仍含未解析变量,降级到 target_name/Info.plist
432
+ if resolved_raw.match?(/\$[\({]/)
433
+ relative_plist_path = "#{target_name}/Info.plist"
434
+ elsif File.absolute_path?(resolved_raw)
435
+ # 绝对路径转为相对路径
436
+ relative_plist_path = resolved_raw.sub(/\A#{Regexp.escape(project_dir)}\//, '')
437
+ else
438
+ relative_plist_path = resolved_raw
439
+ end
440
+ else
441
+ relative_plist_path = "#{target_name}/Info.plist"
442
+ end
443
+ info_plist_path = File.join(project_dir, relative_plist_path)
444
+
445
+ if is_generated_mode
446
+ Funlog.instance.fancyinfo_warning("Target #{target_name} 使用 GENERATE_INFOPLIST_FILE 模式,创建补充 Info.plist 用于复杂配置")
447
+ else
448
+ Funlog.instance.fancyinfo_warning("Target #{target_name} 未找到 Info.plist,自动创建: #{relative_plist_path}")
449
+ end
450
+
451
+ # 3.1 创建目录和最小 Info.plist
452
+ FileUtils.mkdir_p(File.dirname(info_plist_path))
453
+ empty_plist = {}
454
+ Xcodeproj::Plist.write_to_path(empty_plist, info_plist_path)
455
+
456
+ # 3.2 设置 build settings
457
+ target.build_configurations.each do |config|
458
+ config.build_settings['INFOPLIST_FILE'] = relative_plist_path
459
+ # 保持 GENERATE_INFOPLIST_FILE = YES,Xcode 自动合并
460
+ config.build_settings['GENERATE_INFOPLIST_FILE'] ||= 'YES'
461
+ end
462
+
463
+ # 3.3 将文件添加到 Xcode 工程引用,保持与实际相对路径一致
464
+ plist_dir = File.dirname(relative_plist_path)
465
+ plist_name = File.basename(relative_plist_path)
466
+ target_group =
467
+ if plist_dir == "." || plist_dir.empty?
468
+ project_obj.main_group.find_subpath(target_name, true)
469
+ else
470
+ project_obj.main_group.find_subpath(plist_dir, true)
471
+ end
472
+
473
+ unless target_group.files.any? { |f| f.path == plist_name } ||
474
+ project_obj.files.any? { |f| f.path == relative_plist_path }
475
+ target_group.new_file(plist_name)
476
+ Funlog.instance.fancyinfo_success("已将 Info.plist 添加到 Xcode 工程: #{relative_plist_path}")
477
+ end
478
+
479
+ project_obj.save
480
+
481
+ info_plist_path
482
+ end
483
+
326
484
  def modify_info_plist(project_dir:nil, proj_name:nil, config_json:nil)
327
485
 
328
486
  ## Main Info.plist
329
487
  proj_fullname = File.join(project_dir, proj_name) + ".xcodeproj"
330
488
  project_obj = Xcodeproj::Project.open(proj_fullname)
331
489
 
332
- info_plist_path = nil
333
490
  project_obj.targets.each do |target|
334
491
  # 跳过非 native target(如 PBXAggregateTarget)
335
492
  next unless target.respond_to?(:product_type)
336
- temp_info = target.build_configurations.first.build_settings['INFOPLIST_FILE']
337
- if !temp_info.nil?
338
- info_plist_path = File.join(project_dir, temp_info)
339
- end
340
-
341
- # if target.product_type.to_s.eql?("com.apple.product-type.application") && !File.exist?(info_plist_path)
342
- # info_plist_array = Dir.glob(File.join(project_dir, target.name, "**", "Info.plist"))
343
- # if info_plist_array.size == 1
344
- # info_plist_path = info_plist_array.first
345
- # else
346
- # raise Informative, "Missing Target #{target.name.to_s} Info.plist !!! Modify Info.plist Error !!!"
347
- # end
348
- # end
349
-
350
- if target.product_type.to_s.eql?("com.apple.product-type.application") && (info_plist_path.nil? || !File.exist?(info_plist_path))
351
- raise Informative, "Target #{target.name.to_s} 没有找到Info.plist, 修改Info.plist出错了!!"
493
+
494
+ is_app_target = target.product_type.to_s.eql?("com.apple.product-type.application")
495
+
496
+ # 主应用 target:确保 Info.plist 存在(需要写入 URL Schemes 等复杂配置)
497
+ # 非主应用 target:仅在已有 Info.plist 时修改,不强制创建
498
+ if is_app_target
499
+ info_plist_path = ensure_info_plist_exists(
500
+ project_dir: project_dir,
501
+ target: target,
502
+ project_obj: project_obj
503
+ )
504
+ else
505
+ raw_path = target.build_configurations.first.build_settings['INFOPLIST_FILE']
506
+ info_plist_path = resolve_info_plist_path(project_dir, raw_path, target: target)
352
507
  end
353
508
 
354
- if target.product_type.to_s.eql?("com.apple.product-type.application") then
509
+ next if info_plist_path.nil?
510
+
511
+ if is_app_target
355
512
  modify_maintarget_info_plist(plist_file_name:info_plist_path, config_json:config_json, target_name:proj_name)
356
513
  end
357
514
 
@@ -125,7 +125,7 @@ module Pindo
125
125
  config.build_settings['CODE_SIGN_IDENTITY'] = "iPhone Developer"
126
126
  config.build_settings['DEVELOPMENT_TEAM'] = "9WX2E4VC26"
127
127
  config.build_settings['PROVISIONING_PROFILE'] = ""
128
- config.build_settings['PRODUCT_BUNDLE_IDENTIFIER'] = bundle_id unless bundle_id.eql?("com.heroneverdie101.*")
128
+ config.build_settings['PRODUCT_BUNDLE_IDENTIFIER'] = bundle_id unless bundle_id.include?("*")
129
129
  config.build_settings['PROVISIONING_PROFILE_SPECIFIER'] = "match Development " + bundle_id
130
130
  # config.build_settings['TARGETED_DEVICE_FAMILY'] = "1,2"
131
131
  if is_extention
@@ -8,23 +8,38 @@ module Pindo
8
8
 
9
9
  class XcodeResHelper
10
10
  class << self
11
+ def macos_project?(proj_dir)
12
+ xcodeproj_file_name = Dir.glob(File.join(proj_dir, "*.xcodeproj")).max_by { |f| File.mtime(f) }
13
+ return false if xcodeproj_file_name.nil?
14
+
15
+ project_obj = Xcodeproj::Project.open(xcodeproj_file_name)
16
+ sdkroot_settings = project_obj.root_object.build_configuration_list.get_setting("SDKROOT")
17
+ sdkroot = sdkroot_settings.values.compact.first
18
+ !sdkroot.nil? && sdkroot.eql?("macosx")
19
+ end
20
+
21
+ def image_dimensions(image_data)
22
+ width, height = image_data.fetch("size").split("x").map(&:to_f)
23
+ scale = image_data.fetch("scale", "1x").to_s.chomp("x").to_f
24
+
25
+ [
26
+ (width * scale).to_i,
27
+ (height * scale).to_i
28
+ ]
29
+ end
11
30
 
12
31
  def create_icon(icon_name:nil, new_icon_dir:nil, xcode_icon_json:nil)
13
32
  if !File.exist?(icon_name)
14
33
  raise Informative, "文件不存在:#{icon_name}"
15
34
  end
16
35
  xcode_icon_json["images"].each do |image_data|
17
- width,height = image_data["size"].split("x")
36
+ width, height = image_dimensions(image_data)
18
37
  image_name = image_data["filename"]
19
- iNum = image_name.split("@").last.chomp(".png").chomp("x").to_f
20
- width = width.to_f * iNum
21
- height = height.to_f * iNum
22
-
23
- width = width.to_i
24
- height = height.to_i
38
+ next if image_name.nil? || image_name.empty?
25
39
 
26
40
  command = [
27
41
  'sips',
42
+ '-s', 'format', 'png',
28
43
  '--matchTo', '/System/Library/ColorSync/Profiles/sRGB Profile.icc',
29
44
  '-z', width.to_s, height.to_s,
30
45
  icon_name,
@@ -48,14 +63,9 @@ module Pindo
48
63
  raise Informative, "文件不存在:#{image_icon_name} or #{icon_name}"
49
64
  end
50
65
  xcode_icon_json["images"].each do |image_data|
51
-
52
- height, width = image_data["size"].split("x")
66
+ width, height = image_dimensions(image_data)
53
67
  image_name = image_data["filename"]
54
- iNum = image_name.split("@").last.chomp(".png").chomp("x").to_f
55
- width = width.to_f * iNum
56
- height = height.to_f * iNum
57
- width = width.to_i
58
- height = height.to_i
68
+ next if image_name.nil? || image_name.empty?
59
69
 
60
70
  icon_ori_name = image_icon_name
61
71
  if width.to_s.eql?(height.to_s)
@@ -64,6 +74,7 @@ module Pindo
64
74
 
65
75
  command = [
66
76
  'sips',
77
+ '-s', 'format', 'png',
67
78
  '--matchTo', '/System/Library/ColorSync/Profiles/sRGB Profile.icc',
68
79
  '-z', width.to_s, height.to_s,
69
80
  icon_ori_name,
@@ -77,13 +88,18 @@ module Pindo
77
88
  end
78
89
  end
79
90
 
80
- def create_icons(icon_name:nil, new_icon_dir:nil)
91
+ def create_icons(icon_name:nil, new_icon_dir:nil, proj_dir:nil)
81
92
  begin
82
93
  FileUtils.mkdir_p(new_icon_dir)
83
94
  rescue => e
84
95
  puts e
85
96
  end
86
- create_icon(icon_name: icon_name, new_icon_dir: new_icon_dir, xcode_icon_json: XcodoeResConst.xcode_ios_icon_json)
97
+ xcode_icon_json = if !proj_dir.nil? && macos_project?(proj_dir)
98
+ XcodoeResConst.xcode_macos_icon_json
99
+ else
100
+ XcodoeResConst.xcode_ios_icon_json
101
+ end
102
+ create_icon(icon_name: icon_name, new_icon_dir: new_icon_dir, xcode_icon_json: xcode_icon_json)
87
103
  icon_dir = File.dirname(icon_name)
88
104
  imessage_icon = File.join(icon_dir, "icon1024_768.png")
89
105
  new_imessage_icon_dir = File.join(new_icon_dir, "imessage")
@@ -19,13 +19,13 @@ module Pindo
19
19
  type: String,
20
20
  env_name: 'PINDO_BUNDLE_ID',
21
21
  optional: true,
22
- cacheable: true, # 存入文件缓存
22
+ cacheable: false, # 从 JPSBuildConfig.json 获取,不需要缓存
23
23
  verify_block: proc do |value|
24
24
  unless value =~ /^[a-zA-Z0-9\-\.\*]+$/
25
25
  raise "Bundle ID 格式错误: #{value},应该类似 com.example.app"
26
26
  end
27
27
  end,
28
- example: 'pindo ios autobuild --bundleid=com.example.app'
28
+ example: 'pindo ios autobuild --bundleid=com.example.*'
29
29
  ),
30
30
 
31
31
  bundle_name: OptionItem.new(
@@ -35,14 +35,14 @@ module Pindo
35
35
  type: String,
36
36
  env_name: 'PINDO_BUNDLE_NAME',
37
37
  optional: true,
38
- cacheable: true, # 存入文件缓存
38
+ cacheable: false, # 从 JPSBuildConfig.json 获取,不需要缓存
39
39
  verify_block: proc do |value|
40
40
  # 允许标准格式(com.example.app)或带通配符格式(com.example.*)
41
- unless value =~ /^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_\*]*)+$/i
41
+ unless value =~ /\A[a-z][a-z0-9_]*(\.(?:[a-z][a-z0-9_]*|\*))+\z/i
42
42
  raise "Package Name 格式错误: #{value},应该类似 com.example.app 或 com.example.*"
43
43
  end
44
44
  end,
45
- example: 'pindo android autobuild --bundle_name=com.example.app'
45
+ example: 'pindo android autobuild --bundle_name=com.example.*'
46
46
  ),
47
47
 
48
48
  build_type: OptionItem.new(
@@ -71,6 +71,17 @@ module Pindo
71
71
  env_name: 'PINDO_SCHEME',
72
72
  optional: true,
73
73
  example: 'pindo ios autobuild --scheme=MyAppScheme'
74
+ ),
75
+
76
+ types: OptionItem.new(
77
+ key: :types,
78
+ name: '包类型',
79
+ description: '指定包类型,逗号分隔(ipa,apk,html,mac,exe)',
80
+ type: String,
81
+ env_name: 'PINDO_BUILD_TYPES',
82
+ optional: true,
83
+ cacheable: false,
84
+ example: 'pindo unity autobuild --types=ipa,apk'
74
85
  )
75
86
  }
76
87
  end
@@ -81,19 +81,19 @@ module Pindo
81
81
  git_commit: OptionItem.new(
82
82
  key: :git_commit,
83
83
  name: 'Git 未提交处理方式',
84
- description: '处理未提交文件的方式: commit/skip/reset/stash/exit',
84
+ description: '处理未提交文件的方式: commit/skip-with-tag/skip-without-tag/reset/stash/exit',
85
85
  type: String,
86
86
  env_name: 'PINDO_GIT_COMMIT',
87
87
  default_value: nil,
88
88
  optional: true,
89
89
  cacheable: false,
90
90
  verify_block: proc do |value|
91
- allowed = %w[commit skip reset delete stash exit resetde discard]
91
+ allowed = %w[commit skip-with-tag skip-without-tag reset delete stash exit resetde discard]
92
92
  unless allowed.include?(value.to_s.downcase)
93
93
  raise "git_commit 值错误: #{value}, 必须是 #{allowed.join(', ')} 之一"
94
94
  end
95
95
  end,
96
- example: 'pindo unity autobuild --git-commit=skip'
96
+ example: 'pindo unity autobuild --git-commit=skip-with-tag'
97
97
  )
98
98
  }
99
99
  end
@@ -122,8 +122,10 @@ module Pindo
122
122
  case value.to_s.downcase
123
123
  when 'commit', 'comm'
124
124
  Pindo::UncommittedFilesProcessType::COMMIT
125
- when 'skip', 'none', 'dont'
126
- Pindo::UncommittedFilesProcessType::SKIP
125
+ when 'skip-with-tag'
126
+ Pindo::UncommittedFilesProcessType::SKIP_WITH_TAG
127
+ when 'skip-without-tag'
128
+ Pindo::UncommittedFilesProcessType::SKIP_WITHOUT_TAG
127
129
  when 'reset', 'delete', 'resetde', 'discard'
128
130
  Pindo::UncommittedFilesProcessType::RESET
129
131
  when 'stash'
@@ -41,6 +41,17 @@ module Pindo
41
41
  default_value: false,
42
42
  optional: true,
43
43
  example: 'pindo unity autobuild --skipyoo'
44
+ ),
45
+
46
+ kill_unity: OptionItem.new(
47
+ key: :kill_unity,
48
+ name: '自动关闭Unity',
49
+ description: '自动关闭检测到的Unity进程(不询问用户确认)',
50
+ type: OptionItem::Boolean,
51
+ env_name: 'PINDO_KILL_UNITY',
52
+ default_value: false,
53
+ optional: true,
54
+ example: 'pindo unity autobuild --kill-unity'
44
55
  )
45
56
  }
46
57
  end
@@ -94,6 +94,31 @@ module Pindo
94
94
  bundleids
95
95
  end
96
96
 
97
+ # 加载 all_bundleid_group 映射表
98
+ # @return [Hash] group_name => bundle_id 的映射,如 {"fancyapptest" => "com.heroneverdie101.fancyapptest"}
99
+ def self.load_bundleid_group
100
+ config_file = File.join(pindo_env_configdir, 'bundleid_config.json')
101
+ return {} unless File.exist?(config_file)
102
+
103
+ begin
104
+ config = JSON.parse(File.read(config_file))
105
+ config['all_bundleid_group'] || {}
106
+ rescue => e
107
+ puts "加载 bundleid_config.json 失败: #{e.message}"
108
+ {}
109
+ end
110
+ end
111
+
112
+ # 通过 group name 查找真实 bundle ID
113
+ # @param group_name [String] 组名(如 "fancyapptest"、"wildcarddevelopmentid")
114
+ # @return [String, nil] 真实 bundle ID(如 "com.heroneverdie101.fancyapptest"),未找到返回 nil
115
+ def self.resolve_bundleid_by_group_name(group_name)
116
+ return nil if group_name.nil? || group_name.empty?
117
+
118
+ group = load_bundleid_group
119
+ group[group_name]
120
+ end
121
+
97
122
  # 获取 pindo 环境配置目录
98
123
  def self.pindo_env_configdir
99
124
  require_relative '../../config/pindoconfig'
@@ -18,11 +18,12 @@ module Pindo
18
18
  # Git 未提交文件处理类型枚举
19
19
  # 用于控制构建前未提交文件的处理方式
20
20
  module UncommittedFilesProcessType
21
- COMMIT = :commit # 自动提交所有更改
22
- SKIP = :skip # 跳过处理,继续执行
23
- RESET = :reset # 丢弃所有更改,重置到远程
24
- STASH = :stash # 保存到 stash 区域
25
- EXIT = :exit # 退出程序,要求手动处理
26
- NONE = :none # 没有需要提交的内容
21
+ COMMIT = :commit # 自动提交所有更改
22
+ SKIP_WITH_TAG = :skip_with_tag # 不提交,但合并分支并打 tag
23
+ SKIP_WITHOUT_TAG = :skip_without_tag # 不提交,不合并分支,不打 tag
24
+ RESET = :reset # 丢弃所有更改,重置到远程
25
+ STASH = :stash # 保存到 stash 区域
26
+ EXIT = :exit # 退出程序,要求手动处理
27
+ NONE = :none # 没有需要提交的内容
27
28
  end
28
29
  end
data/lib/pindo/version.rb CHANGED
@@ -6,13 +6,13 @@ require 'time'
6
6
 
7
7
  module Pindo
8
8
 
9
- VERSION = "5.17.5"
9
+ VERSION = "5.18.4"
10
10
 
11
11
  class VersionCheck
12
12
  RUBYGEMS_API = 'https://rubygems.org/api/v1/gems/pindo.json'
13
13
  VERSION_INFO_FILE = File.expand_path('~/.pindo/version_info.yml')
14
14
  CHECK_INTERVAL = 5 * 60 * 60 # 5小时检查一次
15
- CONFIG_MIN_VERSION = '1.5.0' # 硬编码的配置版本要求
15
+ CONFIG_MIN_VERSION = '1.8.0' # 硬编码的配置版本要求
16
16
 
17
17
  class << self
18
18
  # 主版本检查方法(保持向后兼容)
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.17.5
4
+ version: 5.18.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - wade
@@ -109,20 +109,20 @@ dependencies:
109
109
  requirements:
110
110
  - - "~>"
111
111
  - !ruby/object:Gem::Version
112
- version: '2.0'
112
+ version: '2.2'
113
113
  - - ">="
114
114
  - !ruby/object:Gem::Version
115
- version: 2.0.4
115
+ version: 2.2.0
116
116
  type: :runtime
117
117
  prerelease: false
118
118
  version_requirements: !ruby/object:Gem::Requirement
119
119
  requirements:
120
120
  - - "~>"
121
121
  - !ruby/object:Gem::Version
122
- version: '2.0'
122
+ version: '2.2'
123
123
  - - ">="
124
124
  - !ruby/object:Gem::Version
125
- version: 2.0.4
125
+ version: 2.2.0
126
126
  - !ruby/object:Gem::Dependency
127
127
  name: rqrcode
128
128
  requirement: !ruby/object:Gem::Requirement
@@ -304,6 +304,7 @@ files:
304
304
  - lib/pindo/command/appstore/quswauth.rb
305
305
  - lib/pindo/command/appstore/screenshots.rb
306
306
  - lib/pindo/command/appstore/tag.rb
307
+ - lib/pindo/command/appstore/updateid.rb
307
308
  - lib/pindo/command/appstore/upload.rb
308
309
  - lib/pindo/command/env.rb
309
310
  - lib/pindo/command/env/dreamstudio.rb
@@ -348,14 +349,17 @@ files:
348
349
  - lib/pindo/command/unity/packinit.rb
349
350
  - lib/pindo/command/unity/packpush.rb
350
351
  - lib/pindo/command/utils.rb
352
+ - lib/pindo/command/utils/allcopyconfig.rb
351
353
  - lib/pindo/command/utils/boss.rb
352
354
  - lib/pindo/command/utils/clearcert.rb
355
+ - lib/pindo/command/utils/copyconfig.rb
353
356
  - lib/pindo/command/utils/decrypt.rb
354
357
  - lib/pindo/command/utils/device.rb
355
358
  - lib/pindo/command/utils/encrypt.rb
356
359
  - lib/pindo/command/utils/fabric.rb
357
360
  - lib/pindo/command/utils/icon.rb
358
361
  - lib/pindo/command/utils/installskills.rb
362
+ - lib/pindo/command/utils/renewbundleid.rb
359
363
  - lib/pindo/command/utils/renewcert.rb
360
364
  - lib/pindo/command/utils/renewproj.rb
361
365
  - lib/pindo/command/utils/repoinit.rb
@@ -379,6 +383,7 @@ files:
379
383
  - lib/pindo/module/android/gradle_helper.rb
380
384
  - lib/pindo/module/android/java_env_helper.rb
381
385
  - lib/pindo/module/android/keystore_helper.rb
386
+ - lib/pindo/module/android/workflow_gradle_injector.rb
382
387
  - lib/pindo/module/appselect.rb
383
388
  - lib/pindo/module/appstore/appstore_iap_tier.json
384
389
  - lib/pindo/module/appstore/appstore_in_app_purchase.rb