pindo 5.12.2 → 5.13.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 (115) hide show
  1. checksums.yaml +4 -4
  2. data/lib/pindo/base/git_handler.rb +692 -0
  3. data/lib/pindo/command/android/autobuild.rb +2 -2
  4. data/lib/pindo/command/android.rb +0 -1
  5. data/lib/pindo/command/appstore/adhocbuild.rb +258 -311
  6. data/lib/pindo/command/appstore/autobuild.rb +203 -0
  7. data/lib/pindo/command/appstore/autoresign.rb +35 -17
  8. data/lib/pindo/command/appstore/bundleid.rb +120 -0
  9. data/lib/pindo/command/appstore/cert.rb +212 -0
  10. data/lib/pindo/command/appstore/configproj.rb +81 -0
  11. data/lib/pindo/command/{deploy → appstore}/getitcinfo.rb +76 -91
  12. data/lib/pindo/command/appstore/iap.rb +788 -24
  13. data/lib/pindo/command/appstore/initconfig.rb +105 -0
  14. data/lib/pindo/command/appstore/itcapp.rb +95 -13
  15. data/lib/pindo/command/{deploy → appstore}/itcinfo.rb +90 -118
  16. data/lib/pindo/command/appstore/pem.rb +136 -0
  17. data/lib/pindo/command/appstore/pullconfig.rb +99 -0
  18. data/lib/pindo/command/appstore/quswark.rb +87 -0
  19. data/lib/pindo/command/appstore/quswauth.rb +67 -0
  20. data/lib/pindo/command/appstore/tag.rb +77 -0
  21. data/lib/pindo/command/appstore.rb +13 -1
  22. data/lib/pindo/command/env/quarkenv.rb +11 -13
  23. data/lib/pindo/command/env/swarkenv.rb +11 -16
  24. data/lib/pindo/command/ios/autobuild.rb +64 -43
  25. data/lib/pindo/command/ios/autoresign.rb +34 -19
  26. data/lib/pindo/command/ios/build.rb +10 -7
  27. data/lib/pindo/command/ios/cert.rb +27 -20
  28. data/lib/pindo/command/jps/upload.rb +3 -3
  29. data/lib/pindo/command/unity/autobuild.rb +2 -2
  30. data/lib/pindo/command/utils/clearcert.rb +2 -17
  31. data/lib/pindo/command/{deploy → utils}/fabric.rb +13 -13
  32. data/lib/pindo/command/utils/renewcert.rb +62 -38
  33. data/lib/pindo/command/utils/renewproj.rb +0 -3
  34. data/lib/pindo/command/{deploy → utils}/updateconfig.rb +6 -7
  35. data/lib/pindo/command/utils.rb +2 -0
  36. data/lib/pindo/command/web/autobuild.rb +2 -2
  37. data/lib/pindo/command.rb +30 -3
  38. data/lib/pindo/config/build_info_manager.rb +176 -0
  39. data/lib/pindo/config/ios_config_parser.rb +404 -0
  40. data/lib/pindo/module/android/android_build_helper.rb +110 -0
  41. data/lib/pindo/module/android/android_config_helper.rb +9 -5
  42. data/lib/pindo/module/android/gradle_helper.rb +88 -0
  43. data/lib/pindo/module/appstore/bundleid_helper.rb +349 -0
  44. data/lib/pindo/module/appstore/itcapp_helper.rb +228 -0
  45. data/lib/pindo/module/build/build_helper.rb +12 -0
  46. data/lib/pindo/module/build/swark_helper.rb +116 -77
  47. data/lib/pindo/module/cert/cert_helper.rb +74 -0
  48. data/lib/pindo/module/cert/pem_helper.rb +72 -0
  49. data/lib/pindo/module/cert/{xcodecerthelper.rb → xcode_cert_helper.rb} +208 -6
  50. data/lib/pindo/module/task/model/appstore/appstore_task.rb +18 -0
  51. data/lib/pindo/module/task/model/appstore/appstore_upload_ipa_task.rb +151 -0
  52. data/lib/pindo/module/task/model/appstore/appstore_upload_metadata_task.rb +250 -0
  53. data/lib/pindo/module/task/model/appstore/appstore_upload_screenshot_task.rb +276 -0
  54. data/lib/pindo/module/task/model/build/android_build_adhoc_task.rb +210 -0
  55. data/lib/pindo/module/task/model/build/{android_dev_build_task.rb → android_build_dev_task.rb} +2 -2
  56. data/lib/pindo/module/task/model/build/android_build_gplay_task.rb +210 -0
  57. data/lib/pindo/module/task/model/build/android_build_task.rb +13 -0
  58. data/lib/pindo/module/task/model/build/ios_build_adhoc_task.rb +197 -0
  59. data/lib/pindo/module/task/model/build/ios_build_appstore_task.rb +367 -0
  60. data/lib/pindo/module/task/model/build/{ios_dev_build_task.rb → ios_build_dev_task.rb} +37 -27
  61. data/lib/pindo/module/task/model/build/ios_build_task.rb +13 -0
  62. data/lib/pindo/module/task/model/build/{web_dev_build_task.rb → web_build_dev_task.rb} +1 -1
  63. data/lib/pindo/module/task/model/build_task.rb +15 -12
  64. data/lib/pindo/module/task/model/jps_resign_task.rb +185 -0
  65. data/lib/pindo/module/task/model/{upload_task.rb → jps_upload_task.rb} +3 -3
  66. data/lib/pindo/module/task/model/unity_export_task.rb +3 -1
  67. data/lib/pindo/module/task/pindo_task.rb +19 -10
  68. data/lib/pindo/module/unity/unity_helper.rb +2 -1
  69. data/lib/pindo/module/xcode/ipa_resign_helper.rb +210 -0
  70. data/lib/pindo/module/xcode/{xcodeappconfig.rb → xcode_app_config.rb} +79 -0
  71. data/lib/pindo/module/xcode/xcode_build_config.rb +152 -17
  72. data/lib/pindo/module/xcode/xcode_build_helper.rb +151 -1
  73. data/lib/pindo/module/xcode/xcode_swark_helper.rb +341 -0
  74. data/lib/pindo/options/core/global_options_state.rb +268 -0
  75. data/lib/pindo/options/core/option_configuration.rb +206 -0
  76. data/lib/pindo/options/core/option_initializer.rb +51 -0
  77. data/lib/pindo/options/core/option_item.rb +144 -0
  78. data/lib/pindo/options/core/option_value_parser.rb +54 -0
  79. data/lib/pindo/options/groups/build_options.rb +60 -0
  80. data/lib/pindo/options/groups/jps_options.rb +70 -0
  81. data/lib/pindo/options/groups/option_group.rb +73 -0
  82. data/lib/pindo/options/helpers/bundleid_selector.rb +103 -0
  83. data/lib/pindo/options/options.rb +14 -0
  84. data/lib/pindo/version.rb +1 -1
  85. metadata +49 -41
  86. data/lib/pindo/command/android/build.rb +0 -186
  87. data/lib/pindo/command/appstore/import.rb +0 -259
  88. data/lib/pindo/command/deploy/build.rb +0 -250
  89. data/lib/pindo/command/deploy/bundleid.rb +0 -259
  90. data/lib/pindo/command/deploy/cert.rb +0 -202
  91. data/lib/pindo/command/deploy/check.rb +0 -93
  92. data/lib/pindo/command/deploy/configproj.rb +0 -120
  93. data/lib/pindo/command/deploy/confusecode.rb +0 -262
  94. data/lib/pindo/command/deploy/confuseproj.rb +0 -122
  95. data/lib/pindo/command/deploy/iap.rb +0 -826
  96. data/lib/pindo/command/deploy/initconfig.rb +0 -138
  97. data/lib/pindo/command/deploy/itcapp.rb +0 -146
  98. data/lib/pindo/command/deploy/pem.rb +0 -55
  99. data/lib/pindo/command/deploy/pullconfig.rb +0 -56
  100. data/lib/pindo/command/deploy/pushconfig.rb +0 -93
  101. data/lib/pindo/command/deploy/quswark.rb +0 -156
  102. data/lib/pindo/command/deploy/quswauth.rb +0 -76
  103. data/lib/pindo/command/deploy/reportbug.rb +0 -145
  104. data/lib/pindo/command/deploy/resign.rb +0 -300
  105. data/lib/pindo/command/deploy/tag.rb +0 -108
  106. data/lib/pindo/command/deploy/uploadipa.rb +0 -73
  107. data/lib/pindo/command/deploy.rb +0 -42
  108. data/lib/pindo/command/dev/autobuild.rb +0 -117
  109. data/lib/pindo/command/dev/build.rb +0 -94
  110. data/lib/pindo/command/dev/debug.rb +0 -112
  111. data/lib/pindo/module/task/model/build/android_release_build_task.rb +0 -29
  112. data/lib/pindo/module/task/model/build/ios_adhoc_build_task.rb +0 -53
  113. data/lib/pindo/module/task/model/build/ios_release_build_task.rb +0 -53
  114. data/lib/pindo/options/appconfigoptions.rb +0 -24
  115. data/lib/pindo/options/deployoptions.rb +0 -372
@@ -9,9 +9,6 @@ module Pindo
9
9
  class Utils < Command
10
10
  class Renewproj < Utils
11
11
 
12
- include Command::DeployOptions
13
-
14
-
15
12
  self.summary = '重新创建Xcode工程'
16
13
 
17
14
  self.description = <<-DESC
@@ -5,10 +5,9 @@ require 'highline'
5
5
 
6
6
  module Pindo
7
7
  class Command
8
- class Deploy < Command
9
- class Updateconfig < Deploy
10
-
11
- include Command::DeployOptions
8
+ class Utils < Command
9
+ class Updateconfig < Utils
10
+
12
11
  include Hashhelper
13
12
  include Appselect
14
13
 
@@ -22,9 +21,9 @@ module Pindo
22
21
  - 保留用户自定义配置的同时更新模板结构
23
22
 
24
23
  使用示例:
25
- pindo deploy updateconfig path/to/config.json
26
- pindo deploy updateconfig ./config.json
27
- pindo deploy updateconfig config.json
24
+ pindo utils updateconfig path/to/config.json
25
+ pindo utils updateconfig ./config.json
26
+ pindo utils updateconfig config.json
28
27
  DESC
29
28
 
30
29
  self.arguments = [
@@ -10,6 +10,8 @@ require 'pindo/command/utils/renewcert'
10
10
  require 'pindo/command/utils/repoinit'
11
11
  require 'pindo/command/utils/feishu'
12
12
  require 'pindo/command/utils/tag'
13
+ require 'pindo/command/utils/updateconfig'
14
+ require 'pindo/command/utils/fabric'
13
15
 
14
16
 
15
17
  module Pindo
@@ -9,7 +9,7 @@ require 'pindo/module/task/task_manager'
9
9
  require 'pindo/module/task/model/git_tag_task'
10
10
  require 'pindo/module/task/model/unity_export_task'
11
11
  require 'pindo/module/task/model/build_task'
12
- require 'pindo/module/task/model/upload_task'
12
+ require 'pindo/module/task/model/jps_upload_task'
13
13
 
14
14
  module Pindo
15
15
  class Command
@@ -234,7 +234,7 @@ module Pindo
234
234
  if @args_upload_flag
235
235
  # WebGL 输出路径: GoodPlatform/WebGL/build
236
236
  webgl_build_path = File.join(config[:project_path], 'GoodPlatform', 'WebGL', 'build')
237
- upload_task = Pindo::TaskSystem::UploadTask.new(
237
+ upload_task = Pindo::TaskSystem::JPSUploadTask.new(
238
238
  'html',
239
239
  webgl_build_path,
240
240
  nil,
data/lib/pindo/command.rb CHANGED
@@ -28,16 +28,15 @@ module Pindo
28
28
 
29
29
  require "fastlane"
30
30
  require "match"
31
- require 'pindo/options/deployoptions'
31
+ require 'pindo/options/options'
32
32
  require 'pindo/module/appselect'
33
33
  require 'pindo/module/build/confuse_xcodeproj'
34
- require 'pindo/module/cert/xcodecerthelper'
34
+ require 'pindo/module/cert/xcode_cert_helper'
35
35
 
36
36
  require 'pindo/module/appstore/appstore_in_app_purchase'
37
37
  require 'pindo/module/appstore/appstore_metadata_fastlane_helper'
38
38
  require 'pindo/module/appstore/appstore_metadata_connect_api_helper'
39
39
 
40
- require 'pindo/command/deploy'
41
40
  require 'pindo/command/env'
42
41
  require 'pindo/command/utils'
43
42
  require 'pindo/command/jps'
@@ -65,6 +64,34 @@ module Pindo
65
64
  # 注意:缓存组的定义已经移至 PindoContext#get_command_group 中统一管理
66
65
  # 使用命令名作为组名,如 ios:autobuild, and:autobuild, web:autobuild
67
66
 
67
+ # 自动推导命令名称
68
+ # @return [String] 命令名称,例如:Pindo::Command::Ios::Autobuild → 'ios:autobuild'
69
+ def self.command_name
70
+ @command_name ||= begin
71
+ # 从类名推导,例如:
72
+ # Pindo::Command::Ios::Autobuild → ['Ios', 'Autobuild'] → 'ios:autobuild'
73
+ parts = name.split('::').drop(2) # 去掉 'Pindo' 和 'Command'
74
+ parts.map(&:downcase).join(':')
75
+ end
76
+ end
77
+
78
+ # 初始化参数(便捷方法)
79
+ # @param argv [CLAide::ARGV] 命令行参数
80
+ # @param command_name [String, nil] 命令名称(可选,默认自动推导)
81
+ # @param directory [String, nil] 工作目录(可选,默认当前目录)
82
+ # @param enable_cache [Boolean, nil] 是否启用缓存(可选,默认使用 use_cache?)
83
+ # @return [OptionConfiguration] 参数配置对象
84
+ def initialize_options(argv, command_name: nil, directory: nil, enable_cache: nil)
85
+ Pindo::Options::OptionInitializer.initialize(
86
+ self,
87
+ argv,
88
+ self.class.option_items,
89
+ command_name || self.class.command_name,
90
+ directory: directory,
91
+ enable_cache: enable_cache.nil? ? self.class.use_cache? : enable_cache
92
+ )
93
+ end
94
+
68
95
 
69
96
  DEFAULT_ROOT_OPTIONS = [
70
97
  ['--version', '查看pindo版本'],
@@ -0,0 +1,176 @@
1
+ require 'singleton'
2
+ require 'fileutils'
3
+ require 'pindo/base/githelper'
4
+
5
+ module Pindo
6
+
7
+ # 构建信息管理器
8
+ # 负责管理应用配置仓库的拉取、更新等操作
9
+ class BuildInfoManager
10
+ include Singleton
11
+ include Pindo::Githelper
12
+
13
+ class << self
14
+ def share_instance
15
+ instance
16
+ end
17
+ end
18
+
19
+ # 拉取应用配置仓库
20
+ # @param repo_name [String] 仓库名称(Bundle ID)
21
+ # @param target_dir [String, nil] 目标目录(默认:当前目录)
22
+ # @return [Boolean] 是否成功拉取配置
23
+ def pull_appconfig_with_reponame(repo_name:, target_dir: nil)
24
+ return false if repo_name.nil? || repo_name.empty?
25
+
26
+ # 规范化仓库名称:去除尾部的 .* (通配符转换)
27
+ normalized_repo_name = repo_name.end_with?('.*') ? repo_name.chomp('.*') : repo_name
28
+
29
+ # 获取配置仓库目录
30
+ begin
31
+ app_config_dir = clong_buildconfig_repo(repo_name: normalized_repo_name)
32
+ rescue StandardError => e
33
+ puts "拉取配置仓库失败: #{e.message}"
34
+ return false
35
+ end
36
+
37
+ # 确定目标目录
38
+ target_dir ||= Dir.pwd
39
+
40
+ # 如果配置仓库目录与目标目录不同,且存在 config.json,则复制
41
+ if !app_config_dir.eql?(target_dir) && File.exist?(File.join(app_config_dir, 'config.json'))
42
+ begin
43
+ FileUtils.cp_r(File.join(app_config_dir, "config.json"), target_dir)
44
+ puts "✓ 配置文件已复制到: #{target_dir}"
45
+ return true
46
+ rescue StandardError => e
47
+ puts "复制配置文件失败: #{e.message}"
48
+ return false
49
+ end
50
+ elsif app_config_dir.eql?(target_dir)
51
+ puts "✓ 配置文件已存在于目标目录"
52
+ return true
53
+ else
54
+ puts "⚠ 配置仓库中未找到 config.json"
55
+ return false
56
+ end
57
+ end
58
+
59
+ # 创建应用配置仓库
60
+ # @param bundle_id [String] Bundle ID(作为仓库名称)
61
+ # @param test_flag [Boolean] 是否为测试环境(默认:false)
62
+ # @return [String, nil] 成功返回配置仓库目录路径,失败返回 nil
63
+ def create_appconfig_with_bundleid(bundle_id:, test_flag: false)
64
+ return nil if bundle_id.nil? || bundle_id.empty?
65
+
66
+ require_relative '../client/giteeclient'
67
+
68
+ # 获取 Gitee 客户端
69
+ gitee_client = GiteeClient.new(access_token: pindo_single_config.gitee_api_key)
70
+
71
+ # 获取模板配置仓库目录
72
+ demo_dir = clong_buildconfig_repo(repo_name: pindo_single_config.demo_bundle_id)
73
+
74
+ # 确定仓库公开类型和所有者组织
75
+ public_type = 0
76
+ owner_org = pindo_single_config.build_deploy_org
77
+ repo_name = bundle_id
78
+
79
+ if test_flag
80
+ owner_org = pindo_single_config.build_test_org
81
+ public_type = 2
82
+ end
83
+
84
+ # 创建 Gitee 仓库
85
+ success = gitee_client.gitee_create_repo(
86
+ owner: owner_org,
87
+ repo_name: repo_name,
88
+ public_type: public_type
89
+ )
90
+
91
+ if success
92
+ # 如果是测试环境,修改仓库设置
93
+ if test_flag
94
+ modify_repo_setting(repo_name: repo_name, owner_org: owner_org)
95
+ end
96
+
97
+ # 克隆配置仓库
98
+ app_config_dir = clong_buildconfig_repo(repo_name: repo_name)
99
+ system "open #{app_config_dir}"
100
+
101
+ if File.exist?(app_config_dir)
102
+ # 从模板仓库复制配置文件
103
+ update_appconfig_repo(
104
+ bundle_id: bundle_id,
105
+ demo_dir: demo_dir,
106
+ app_config_dir: app_config_dir
107
+ )
108
+ puts "✓ 配置仓库创建成功: #{app_config_dir}"
109
+ return app_config_dir
110
+ else
111
+ puts "❌ 创建应用配置仓库失败"
112
+ return nil
113
+ end
114
+ else
115
+ # 仓库已存在,直接打开
116
+ app_config_dir = clong_buildconfig_repo(repo_name: repo_name)
117
+ system "open #{app_config_dir}"
118
+ puts "⚠ 配置仓库已存在: #{app_config_dir}"
119
+ return app_config_dir
120
+ end
121
+ end
122
+
123
+ private
124
+
125
+ # 修改仓库设置(测试环境)
126
+ def modify_repo_setting(repo_name:, owner_org:)
127
+ pindo_setting_dir = pindo_single_config.pindo_env_configdir
128
+ git_repo_file = pindo_single_config.git_base_url_fullname
129
+ git_repo_json = JSON.parse(File.read(git_repo_file))
130
+ git_repo_json ||= {}
131
+
132
+ if git_repo_json
133
+ git_repo_json[repo_name] = owner_org
134
+ end
135
+
136
+ File.open(git_repo_file, "w") do |f|
137
+ f.write(JSON.pretty_generate(git_repo_json))
138
+ end
139
+
140
+ git_addpush_repo(path: pindo_setting_dir, message: "add #{repo_name}")
141
+ end
142
+
143
+ # 更新应用配置仓库(从模板复制)
144
+ def update_appconfig_repo(bundle_id:, demo_dir:, app_config_dir:)
145
+ return unless File.exist?(demo_dir)
146
+
147
+ # 复制 config.json
148
+ if File.exist?(File.join(demo_dir, "config.json"))
149
+ FileUtils.cp_r(File.join(demo_dir, "config.json"), app_config_dir)
150
+ end
151
+
152
+ # 复制 fastlane 目录
153
+ if File.exist?(File.join(demo_dir, "fastlane"))
154
+ FileUtils.cp_r(File.join(demo_dir, "fastlane"), app_config_dir)
155
+ end
156
+
157
+ # 复制 launch 目录
158
+ if File.exist?(File.join(demo_dir, "launch"))
159
+ FileUtils.cp_r(File.join(demo_dir, "launch"), app_config_dir)
160
+ end
161
+
162
+ # 替换 config.json 中的 Bundle ID
163
+ config_file = File.join(app_config_dir, "config.json")
164
+ if File.exist?(config_file)
165
+ text = File.read(config_file)
166
+ demo_bundle_id = pindo_single_config.demo_bundle_id
167
+ new_contents = text.gsub(/#{demo_bundle_id}/, bundle_id)
168
+ File.open(config_file, "w") { |file| file.puts new_contents }
169
+ end
170
+
171
+ # 提交并推送到远程仓库
172
+ git_addpush_repo(path: app_config_dir, message: "init app config")
173
+ end
174
+ end
175
+
176
+ end
@@ -0,0 +1,404 @@
1
+ require 'singleton'
2
+ require 'json'
3
+
4
+ module Pindo
5
+ # iOS 配置解析器(单例模式)
6
+ # 专门用于解析和处理 iOS 项目的 config.json 配置文件
7
+ #
8
+ # 使用示例:
9
+ # parser = Pindo::IosConfigParser.instance
10
+ # parser.load_config(config_file: 'path/to/config.json')
11
+ #
12
+ # # 获取 Apple ID
13
+ # apple_id = parser.apple_id
14
+ #
15
+ # # 获取 Bundle ID 映射
16
+ # bundle_id_map = parser.get_bundle_id_map
17
+ #
18
+ # # 命令行参数覆盖
19
+ # parser.override_apple_id('custom@apple.com')
20
+ # parser.override_bundle_id('com.example.app')
21
+ class IosConfigParser
22
+ include Singleton
23
+
24
+ attr_reader :config_file_path, :config_json
25
+
26
+ def initialize
27
+ @config_file_path = nil
28
+ @config_json = nil
29
+ @config_data = {}
30
+ @overrides = {}
31
+ end
32
+
33
+ # 加载配置文件
34
+ # @param config_file [String] config.json 文件路径
35
+ # @raise [StandardError] 文件不存在或格式错误
36
+ def load_config(config_file:)
37
+ unless File.exist?(config_file)
38
+ raise "配置文件不存在: #{config_file}"
39
+ end
40
+
41
+ @config_file_path = File.expand_path(config_file)
42
+
43
+ begin
44
+ @config_json = JSON.parse(File.read(@config_file_path))
45
+ rescue JSON::ParserError => e
46
+ raise "配置文件格式错误: #{e.message}"
47
+ end
48
+
49
+ # 解析配置
50
+ parse_config_data
51
+
52
+ # 检查配置版本
53
+ check_config_version
54
+
55
+ self
56
+ end
57
+
58
+ # 重新加载配置文件
59
+ def reload
60
+ return unless @config_file_path
61
+ load_config(config_file: @config_file_path)
62
+ end
63
+
64
+ # 清除所有配置和覆盖
65
+ def clear
66
+ @config_file_path = nil
67
+ @config_json = nil
68
+ @config_data = {}
69
+ @overrides = {}
70
+ end
71
+
72
+ # ==================== 账号信息 ====================
73
+
74
+ # 获取 Apple ID
75
+ # @return [String, nil] Apple 账号 ID
76
+ def apple_id
77
+ @overrides[:apple_id] || @config_data[:apple_id]
78
+ end
79
+
80
+ # 覆盖 Apple ID(命令行参数)
81
+ # @param value [String] Apple ID
82
+ def override_apple_id(value)
83
+ @overrides[:apple_id] = value if value && !value.empty?
84
+ end
85
+
86
+ # 获取公司名称
87
+ # @return [String, nil] 公司名称
88
+ def company_name
89
+ @overrides[:company_name] || @config_data[:company_name]
90
+ end
91
+
92
+ # 覆盖公司名称
93
+ # @param value [String] 公司名称
94
+ def override_company_name(value)
95
+ @overrides[:company_name] = value if value && !value.empty?
96
+ end
97
+
98
+ # ==================== Bundle ID 信息 ====================
99
+
100
+ # 获取主应用 Bundle ID
101
+ # @return [String, nil] Bundle ID
102
+ def bundle_id
103
+ @overrides[:bundle_id] || @config_data[:bundle_id]
104
+ end
105
+
106
+ # 覆盖主应用 Bundle ID(命令行参数)
107
+ # @param value [String] Bundle ID
108
+ def override_bundle_id(value)
109
+ @overrides[:bundle_id] = value if value && !value.empty?
110
+ end
111
+
112
+ # 获取 Push Content Extension Bundle ID
113
+ # @return [String, nil]
114
+ def bundle_id_pushcontent
115
+ @config_data[:bundle_id_pushcontent]
116
+ end
117
+
118
+ # 获取 Push Service Extension Bundle ID
119
+ # @return [String, nil]
120
+ def bundle_id_pushservice
121
+ @config_data[:bundle_id_pushservice]
122
+ end
123
+
124
+ # 获取 Keyboard Extension Bundle ID
125
+ # @return [String, nil]
126
+ def bundle_id_keyboard
127
+ @config_data[:bundle_id_keyboard]
128
+ end
129
+
130
+ # 获取 iMessage Extension Bundle ID
131
+ # @return [String, nil]
132
+ def bundle_id_imessage
133
+ @config_data[:bundle_id_imessage]
134
+ end
135
+
136
+ # 获取通用 Extension Bundle ID
137
+ # @return [String, nil]
138
+ def bundle_id_extension
139
+ @config_data[:bundle_id_extension]
140
+ end
141
+
142
+ # 获取 Siri Extension Bundle ID
143
+ # @return [String, nil]
144
+ def bundle_id_siri
145
+ @config_data[:bundle_id_siri]
146
+ end
147
+
148
+ # 获取 Siri UI Extension Bundle ID
149
+ # @return [String, nil]
150
+ def bundle_id_siriui
151
+ @config_data[:bundle_id_siriui]
152
+ end
153
+
154
+ # 获取 Widget Extension Bundle ID
155
+ # @return [String, nil]
156
+ def bundle_id_widget
157
+ @config_data[:bundle_id_widget]
158
+ end
159
+
160
+ # 获取广告 Extension Bundle ID
161
+ # @return [String, nil]
162
+ def bundle_id_extensionad
163
+ @config_data[:bundle_id_extensionad]
164
+ end
165
+
166
+ # 获取 Porn Extension Bundle ID
167
+ # @return [String, nil]
168
+ def bundle_id_extensionporn
169
+ @config_data[:bundle_id_extensionporn]
170
+ end
171
+
172
+ # 获取 Watch App Bundle ID
173
+ # @return [String, nil]
174
+ def bundle_id_watchapp
175
+ @config_data[:bundle_id_watchapp]
176
+ end
177
+
178
+ # 获取 Watch App Extension Bundle ID
179
+ # @return [String, nil]
180
+ def bundle_id_watchapp_extension
181
+ @config_data[:bundle_id_watchapp_extension]
182
+ end
183
+
184
+ # 获取所有 Bundle ID 的映射
185
+ # @return [Hash] Bundle ID 映射,键为类型,值为 Bundle ID
186
+ # @example
187
+ # {
188
+ # "bundle_id" => "com.example.app",
189
+ # "bundle_id_pushcontent" => "com.example.app.content",
190
+ # "bundle_id_pushservice" => "com.example.app.service",
191
+ # ...
192
+ # }
193
+ def get_bundle_id_map
194
+ {
195
+ "bundle_id" => bundle_id,
196
+ "bundle_id_pushcontent" => bundle_id_pushcontent,
197
+ "bundle_id_pushservice" => bundle_id_pushservice,
198
+ "bundle_id_keyboard" => bundle_id_keyboard,
199
+ "bundle_id_imessage" => bundle_id_imessage,
200
+ "bundle_id_extension" => bundle_id_extension,
201
+ "bundle_id_siri" => bundle_id_siri,
202
+ "bundle_id_siriui" => bundle_id_siriui,
203
+ "bundle_id_widget" => bundle_id_widget,
204
+ "bundle_id_extensionad" => bundle_id_extensionad,
205
+ "bundle_id_extensionporn" => bundle_id_extensionporn,
206
+ "bundle_id_watchapp" => bundle_id_watchapp,
207
+ "bundle_id_watchapp_extension" => bundle_id_watchapp_extension
208
+ }.compact
209
+ end
210
+
211
+ # 获取 Bundle ID 数组(去除 nil 值)
212
+ # @return [Array<String>] Bundle ID 数组
213
+ def get_bundle_id_array
214
+ get_bundle_id_map.values
215
+ end
216
+
217
+ # ==================== App Group 和 iCloud ====================
218
+
219
+ # 获取 App Group ID
220
+ # @return [String, nil]
221
+ def group_id
222
+ @config_data[:group_id]
223
+ end
224
+
225
+ # 获取 iCloud Container ID
226
+ # @return [String, nil]
227
+ def icloud_id
228
+ @config_data[:icloud_id]
229
+ end
230
+
231
+ # ==================== 应用信息 ====================
232
+
233
+ # 获取应用名称
234
+ # @return [String, nil]
235
+ def app_name
236
+ @config_data[:app_name]
237
+ end
238
+
239
+ # 获取应用版本
240
+ # @return [String, nil]
241
+ def app_version
242
+ @config_data[:app_version]
243
+ end
244
+
245
+ # 获取 App ID (Apple Store Connect)
246
+ # @return [String, nil]
247
+ def app_id_ios
248
+ @config_data[:app_id_ios]
249
+ end
250
+
251
+ # ==================== 项目信息 ====================
252
+
253
+ # 获取项目名称
254
+ # @return [String, nil]
255
+ def project_name
256
+ @config_data[:project_name]
257
+ end
258
+
259
+ # 获取 Xcode 构建类型
260
+ # @return [String, nil]
261
+ def xcode_build_type
262
+ @config_data[:xcode_build_type]
263
+ end
264
+
265
+ # 获取 iOS 部署目标版本
266
+ # @return [String, nil]
267
+ def ios_deployment_target
268
+ @config_data[:ios_deployment_target]
269
+ end
270
+
271
+ # ==================== 配置验证 ====================
272
+
273
+ # 验证必需的配置项是否存在
274
+ # @param required_keys [Array<Symbol>] 必需的配置键
275
+ # @raise [StandardError] 缺少必需配置
276
+ def validate_required(*required_keys)
277
+ missing_keys = []
278
+
279
+ required_keys.each do |key|
280
+ value = send(key) rescue nil
281
+ missing_keys << key if value.nil? || (value.respond_to?(:empty?) && value.empty?)
282
+ end
283
+
284
+ unless missing_keys.empty?
285
+ raise "配置文件缺少必需项: #{missing_keys.join(', ')}"
286
+ end
287
+ end
288
+
289
+ # 检查配置是否已加载
290
+ # @return [Boolean]
291
+ def loaded?
292
+ !@config_json.nil?
293
+ end
294
+
295
+ # ==================== 调试和信息 ====================
296
+
297
+ # 打印所有配置信息(用于调试)
298
+ def print_config_info
299
+ puts "\n" + "=" * 80
300
+ puts "iOS 配置信息"
301
+ puts "=" * 80
302
+ puts "配置文件: #{@config_file_path}"
303
+ puts
304
+ puts "【账号信息】"
305
+ puts " Apple ID: #{apple_id}"
306
+ puts " Company Name: #{company_name}"
307
+ puts
308
+ puts "【应用信息】"
309
+ puts " App Name: #{app_name}"
310
+ puts " App Version: #{app_version}"
311
+ puts " App ID (iOS): #{app_id_ios}"
312
+ puts
313
+ puts "【Bundle ID】"
314
+ puts " Main App: #{bundle_id}"
315
+ get_bundle_id_map.each do |type, id|
316
+ next if type == "bundle_id"
317
+ puts " #{type}: #{id}"
318
+ end
319
+ puts
320
+ puts "【App Group & iCloud】"
321
+ puts " Group ID: #{group_id}"
322
+ puts " iCloud ID: #{icloud_id}"
323
+ puts
324
+ puts "【项目信息】"
325
+ puts " Project Name: #{project_name}"
326
+ puts " Xcode Build Type: #{xcode_build_type}"
327
+ puts " iOS Deployment Target: #{ios_deployment_target}"
328
+ puts "=" * 80
329
+ puts
330
+ end
331
+
332
+ private
333
+
334
+ # 解析配置数据
335
+ def parse_config_data
336
+ return unless @config_json
337
+
338
+ # 解析账号信息
339
+ if @config_json['account_info']
340
+ account_info = @config_json['account_info']
341
+ @config_data[:apple_id] = account_info['apple_acount_id']
342
+ @config_data[:company_name] = account_info['acount_company_name']
343
+ end
344
+
345
+ # 解析应用信息
346
+ if @config_json['app_info']
347
+ app_info = @config_json['app_info']
348
+ @config_data[:bundle_id] = app_info['app_identifier']
349
+ @config_data[:bundle_id_pushcontent] = app_info['app_identifier_pushcontent']
350
+ @config_data[:bundle_id_pushservice] = app_info['app_identifier_pushservice']
351
+ @config_data[:bundle_id_keyboard] = app_info['app_identifier_keyboard']
352
+ @config_data[:bundle_id_imessage] = app_info['app_identifier_imessage']
353
+ @config_data[:bundle_id_extension] = app_info['app_identifier_extension']
354
+ @config_data[:bundle_id_siri] = app_info['app_identifier_siri']
355
+ @config_data[:bundle_id_siriui] = app_info['app_identifier_siriui']
356
+ @config_data[:bundle_id_widget] = app_info['app_identifier_widget']
357
+ @config_data[:bundle_id_extensionad] = app_info['app_identifier_extensionad']
358
+ @config_data[:bundle_id_extensionporn] = app_info['app_identifier_extensionporn']
359
+ @config_data[:bundle_id_watchapp] = app_info['app_identifier_watchapp']
360
+ @config_data[:bundle_id_watchapp_extension] = app_info['app_identifier_watchapp_extension']
361
+
362
+ @config_data[:app_name] = app_info['app_name']
363
+ @config_data[:app_version] = app_info['app_version']
364
+ @config_data[:app_id_ios] = app_info['app_id_ios']
365
+ end
366
+
367
+ # 解析 App Setting
368
+ if @config_json['app_setting']
369
+ app_setting = @config_json['app_setting']
370
+ @config_data[:group_id] = app_setting['app_group_id']
371
+ @config_data[:icloud_id] = app_setting['app_icloud_id']
372
+ end
373
+
374
+ # 解析项目信息
375
+ if @config_json['project_info']
376
+ project_info = @config_json['project_info']
377
+ @config_data[:project_name] = project_info['project_name']
378
+ @config_data[:xcode_build_type] = project_info['xcode_build_type']
379
+ @config_data[:ios_deployment_target] = project_info['ios_deployment_targe'] # 注意:原配置中是 targe 而不是 target
380
+ end
381
+ end
382
+
383
+ # 检查配置版本
384
+ def check_config_version
385
+ return unless @config_json
386
+ return unless @config_json['app_info']
387
+ return unless @config_json['app_info']['app_config_version']
388
+
389
+ require_relative '../base/funlog'
390
+
391
+ remote_version = @config_json['app_info']['app_config_version'].to_s
392
+ project_dir = File.dirname(@config_file_path)
393
+ local_version_file = File.join(project_dir, ".app_config_version")
394
+
395
+ if File.exist?(local_version_file)
396
+ local_version = File.read(local_version_file).strip
397
+ if local_version != remote_version
398
+ Funlog.instance.fancyinfo_warning("配置版本不匹配!本地: #{local_version}, 远程: #{remote_version}")
399
+ end
400
+ end
401
+ end
402
+
403
+ end
404
+ end