pindo 5.13.4 → 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.
@@ -21,13 +21,14 @@ module Pindo
21
21
  class IosConfigParser
22
22
  include Singleton
23
23
 
24
- attr_reader :config_file_path, :config_json
24
+ attr_reader :config_file_path, :config_json, :app_repo_name
25
25
 
26
26
  def initialize
27
27
  @config_file_path = nil
28
28
  @config_json = nil
29
29
  @config_data = {}
30
30
  @overrides = {}
31
+ @app_repo_name = nil # 保存原始 Bundle ID,用于配置仓库路径
31
32
  end
32
33
 
33
34
  # 加载配置文件
@@ -67,6 +68,7 @@ module Pindo
67
68
  @config_json = nil
68
69
  @config_data = {}
69
70
  @overrides = {}
71
+ @app_repo_name = nil
70
72
  end
71
73
 
72
74
  # ==================== 账号信息 ====================
@@ -292,6 +294,155 @@ module Pindo
292
294
  !@config_json.nil?
293
295
  end
294
296
 
297
+ # ==================== AdHoc 发布配置合并 ====================
298
+
299
+ # 合并 AdHoc 发布配置
300
+ # 1. 替换所有 Bundle ID 为 AdHoc 配置的 Bundle ID(确保使用 AdHoc 证书)
301
+ # 2. 合并第三方 SDK 配置(Facebook、AdMob、AppLovin)
302
+ # 3. 合并 app_setting(排除主机和 URL 配置)
303
+ # @param adhoc_config_dir [String] AdHoc 配置仓库目录
304
+ # @return [Boolean] 是否成功
305
+ def modify_config_with_adhoc(adhoc_config_dir:)
306
+ return false unless loaded?
307
+ return false if adhoc_config_dir.nil? || adhoc_config_dir.empty?
308
+
309
+ begin
310
+ # 1. 读取 AdHoc 配置
311
+ adhoc_config_file = File.join(adhoc_config_dir, "config.json")
312
+ unless File.exist?(adhoc_config_file)
313
+ puts "AdHoc 配置文件不存在: #{adhoc_config_file}"
314
+ return false
315
+ end
316
+
317
+ adhoc_config_json = JSON.parse(File.read(adhoc_config_file))
318
+
319
+ # 1.5 替换 Apple ID(使用 AdHoc 配置的 Apple ID)
320
+ if adhoc_config_json.dig('account_info', 'apple_acount_id')
321
+ original_apple_id = @config_json.dig('account_info', 'apple_acount_id')
322
+ adhoc_apple_id = adhoc_config_json['account_info']['apple_acount_id']
323
+
324
+ if original_apple_id && adhoc_apple_id && original_apple_id != adhoc_apple_id
325
+ @config_json['account_info']['apple_acount_id'] = adhoc_apple_id
326
+ puts " 替换 Apple ID:"
327
+ puts " 原始 Apple ID: #{original_apple_id}"
328
+ puts " AdHoc Apple ID: #{adhoc_apple_id}"
329
+ end
330
+
331
+ # 如果 AdHoc 配置包含 company_name,也一并替换
332
+ if adhoc_config_json.dig('account_info', 'acount_company_name')
333
+ @config_json['account_info']['acount_company_name'] = adhoc_config_json['account_info']['acount_company_name']
334
+ end
335
+ end
336
+
337
+ # 2. 替换所有 Bundle ID(使用 AdHoc 配置的 Bundle ID)
338
+ original_bundle_id = @config_json.dig('app_info', 'app_identifier')
339
+ adhoc_bundle_id = adhoc_config_json.dig('app_info', 'app_identifier')
340
+
341
+ if original_bundle_id && adhoc_bundle_id && original_bundle_id != adhoc_bundle_id
342
+ puts " 替换 Bundle ID 前缀:"
343
+ puts " 原始前缀: #{original_bundle_id}"
344
+ puts " AdHoc 前缀: #{adhoc_bundle_id}"
345
+
346
+ # Bundle ID 字段映射表
347
+ bundle_id_keys = [
348
+ 'app_identifier', # 主应用
349
+ 'app_identifier_pushcontent', # Push Content Extension
350
+ 'app_identifier_pushservice', # Push Service Extension
351
+ 'app_identifier_keyboard', # Keyboard Extension
352
+ 'app_identifier_imessage', # iMessage Extension
353
+ 'app_identifier_extension', # 通用 Extension
354
+ 'app_identifier_siri', # Siri Extension
355
+ 'app_identifier_siriui', # Siri UI Extension
356
+ 'app_identifier_widget', # Widget Extension
357
+ 'app_identifier_extensionad', # 广告 Extension
358
+ 'app_identifier_extensionporn',# Porn Extension
359
+ 'app_identifier_watchapp', # Watch App
360
+ 'app_identifier_watchapp_extension' # Watch App Extension
361
+ ]
362
+
363
+ # 遍历所有 Bundle ID 字段并替换前缀
364
+ bundle_id_keys.each do |key|
365
+ current_value = @config_json.dig('app_info', key)
366
+ next if current_value.nil? || current_value.empty?
367
+
368
+ # 如果当前值以原始主 Bundle ID 开头,替换为 AdHoc Bundle ID
369
+ if current_value.start_with?(original_bundle_id)
370
+ # 计算后缀部分(如 .extension、.keyboard 等)
371
+ suffix = current_value[original_bundle_id.length..-1]
372
+ new_value = adhoc_bundle_id + suffix
373
+
374
+ @config_json['app_info'][key] = new_value
375
+ puts " ✓ #{key}: #{current_value} → #{new_value}"
376
+ end
377
+ end
378
+ end
379
+
380
+ # 2.5 替换 App Group ID 和 iCloud Container ID
381
+ if original_bundle_id && adhoc_bundle_id && original_bundle_id != adhoc_bundle_id
382
+ # 处理 app_group_id (格式: group.com.example.app)
383
+ if @config_json.dig('app_setting', 'app_group_id')
384
+ current_group_id = @config_json['app_setting']['app_group_id']
385
+ if current_group_id.include?(original_bundle_id)
386
+ new_group_id = current_group_id.gsub(original_bundle_id, adhoc_bundle_id)
387
+ @config_json['app_setting']['app_group_id'] = new_group_id
388
+ puts " ✓ app_group_id: #{current_group_id} → #{new_group_id}"
389
+ end
390
+ end
391
+
392
+ # 处理 app_icloud_id (格式: iCloud.com.example.app)
393
+ if @config_json.dig('app_setting', 'app_icloud_id')
394
+ current_icloud_id = @config_json['app_setting']['app_icloud_id']
395
+ if current_icloud_id.include?(original_bundle_id)
396
+ new_icloud_id = current_icloud_id.gsub(original_bundle_id, adhoc_bundle_id)
397
+ @config_json['app_setting']['app_icloud_id'] = new_icloud_id
398
+ puts " ✓ app_icloud_id: #{current_icloud_id} → #{new_icloud_id}"
399
+ end
400
+ end
401
+ end
402
+
403
+ # 3. 合并第三方 SDK 配置
404
+ # Facebook 配置
405
+ if adhoc_config_json.dig('app_info', 'facebook_app_id')
406
+ @config_json['app_info']['facebook_app_id'] = adhoc_config_json['app_info']['facebook_app_id']
407
+ @config_json['app_info']['facebook_client_token'] = adhoc_config_json['app_info']['facebook_client_token']
408
+ end
409
+
410
+ # AdMob 配置
411
+ if @config_json.dig('app_info', 'admob_app_id') && adhoc_config_json.dig('app_info', 'admob_app_id')
412
+ @config_json['app_info']['admob_app_id'] = adhoc_config_json['app_info']['admob_app_id']
413
+ end
414
+
415
+ # AppLovin 配置
416
+ if @config_json.dig('app_info', 'applovin_app_id') && adhoc_config_json.dig('app_info', 'applovin_app_id')
417
+ @config_json['app_info']['applovin_app_id'] = adhoc_config_json['app_info']['applovin_app_id']
418
+ end
419
+
420
+ # 4. 合并 app_setting(排除主机和 URL 配置)
421
+ excluded_keys = ['kGUKeyAppClientHost', 'kGUKeyResBaseUrl', 'app_web_host', 'app_client_url']
422
+
423
+ if @config_json['app_setting'] && adhoc_config_json['app_setting']
424
+ @config_json['app_setting'].each_key do |key|
425
+ next if excluded_keys.include?(key)
426
+
427
+ if adhoc_config_json['app_setting'][key]
428
+ @config_json['app_setting'][key] = adhoc_config_json['app_setting'][key]
429
+ end
430
+ end
431
+ end
432
+
433
+ # 5. 重新解析配置数据,同步更新 @config_data
434
+ # 这样 bundle_id、bundle_id_pushcontent 等属性方法才能返回替换后的值
435
+ parse_config_data
436
+
437
+ puts "✅ 已替换 Apple ID、Bundle ID 并合并 AdHoc 配置(Facebook、AdMob、AppLovin)"
438
+ true
439
+ rescue => e
440
+ puts "合并发布配置失败: #{e.message}"
441
+ puts e.backtrace.join("\n")
442
+ false
443
+ end
444
+ end
445
+
295
446
  # ==================== 调试和信息 ====================
296
447
 
297
448
  # 打印所有配置信息(用于调试)
@@ -346,6 +497,17 @@ module Pindo
346
497
  if @config_json['app_info']
347
498
  app_info = @config_json['app_info']
348
499
  @config_data[:bundle_id] = app_info['app_identifier']
500
+
501
+ # 保存原始 Bundle ID 作为配置仓库名称(只在第一次设置,后续不覆盖)
502
+ if @app_repo_name.nil? && app_info['app_identifier']
503
+ repo_name = app_info['app_identifier']
504
+ # 如果是通配符 Bundle ID(以 .* 结尾),去除通配符后缀
505
+ if repo_name.end_with?('.*')
506
+ repo_name = repo_name[0...-2] # 去除最后的 .*
507
+ end
508
+ @app_repo_name = repo_name
509
+ end
510
+
349
511
  @config_data[:bundle_id_pushcontent] = app_info['app_identifier_pushcontent']
350
512
  @config_data[:bundle_id_pushservice] = app_info['app_identifier_pushservice']
351
513
  @config_data[:bundle_id_keyboard] = app_info['app_identifier_keyboard']
@@ -259,7 +259,7 @@ module Pindo
259
259
  provision_start_name = "Development"
260
260
  cert_sub_dir = cert_type.downcase
261
261
  elsif cert_type.downcase.include?("adhoc")
262
- provision_start_name = "Adhoc"
262
+ provision_start_name = "AdHoc"
263
263
  cert_sub_dir = "adhoc"
264
264
  else
265
265
  provision_start_name = "AppStore"
@@ -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?
@@ -46,6 +46,38 @@ module Pindo
46
46
  # 1. 加载配置到 IosConfigParser 单例
47
47
  load_ios_config
48
48
 
49
+ # 1.5 获取 AdHoc 配置目录并合并配置
50
+ adhoc_config_dir = nil
51
+ begin
52
+ config_parser = Pindo::IosConfigParser.instance
53
+ config_json = config_parser.config_json
54
+
55
+ # 根据 app_type 获取 AdHoc 配置仓库
56
+ if config_json && config_json['project_info'] && config_json['project_info']['app_type']
57
+ require 'pindo/config/build_info_manager'
58
+
59
+ build_info_manager = Pindo::BuildInfoManager.share_instance
60
+ adhoc_repo_name = build_info_manager.get_deploy_repo_with_modul_name(
61
+ module_name: config_json['project_info']['app_type']
62
+ )
63
+
64
+ if adhoc_repo_name.nil? || adhoc_repo_name.empty?
65
+ raise Informative, "config.json app_type is error!!!"
66
+ end
67
+
68
+ # 克隆 AdHoc 配置仓库
69
+ adhoc_config_dir = build_info_manager.clong_buildconfig_repo(repo_name: adhoc_repo_name)
70
+
71
+ # 合并 AdHoc 配置
72
+ config_parser.modify_config_with_adhoc(adhoc_config_dir: adhoc_config_dir)
73
+ else
74
+ puts "配置中缺少 app_type,跳过 AdHoc 配置合并"
75
+ end
76
+ rescue => e
77
+ puts "获取 AdHoc 配置失败: #{e.message}"
78
+ adhoc_config_dir = nil
79
+ end
80
+
49
81
  # 2. 自动增加版本号
50
82
  auto_increase_buildnumber
51
83
 
@@ -55,6 +87,16 @@ module Pindo
55
87
  # 4. 配置 Xcode 项目
56
88
  configure_xcode_project
57
89
 
90
+ # 4.5 安装 Firebase 配置 - 使用 AdHoc 配置仓库目录
91
+ if adhoc_config_dir && File.directory?(adhoc_config_dir)
92
+ require 'pindo/module/xcode/xcode_build_helper'
93
+
94
+ Pindo::XcodeBuildHelper.install_google_plist(
95
+ project_dir: @project_path,
96
+ app_config_dir: adhoc_config_dir
97
+ )
98
+ end
99
+
58
100
  # 5. Quark/Swark 处理
59
101
  handle_quswark_processing
60
102
 
@@ -84,6 +126,58 @@ module Pindo
84
126
  end
85
127
  end
86
128
 
129
+ # 处理 CocoaPods 依赖(AdHoc 构建)
130
+ def handle_cocoapods
131
+ return unless File.exist?(File.join(@project_path, "Podfile"))
132
+
133
+ require 'pindo/module/xcode/cocoapods_helper'
134
+ require 'pindo/config/pindoconfig'
135
+
136
+ config_parser = Pindo::IosConfigParser.instance
137
+
138
+ # 使用原始 Bundle ID(app_repo_name)作为配置仓库名称
139
+ # 注意:config_parser.bundle_id 在 AdHoc 构建中已被替换为 AdHoc Bundle ID
140
+ # 而 app_repo_name 保留了原始值,用于定位正确的配置仓库
141
+ app_repo_name = config_parser.app_repo_name
142
+ app_version = config_parser.app_version || "unknown"
143
+
144
+ # 获取配置仓库目录(用于备份)
145
+ # 使用 clong_buildconfig_repo 确保仓库已克隆或更新
146
+ require 'pindo/base/git_handler'
147
+ app_config_dir = Pindo::GitHandler.clong_buildconfig_repo(repo_name: app_repo_name)
148
+
149
+ # 1. 清理 Pods 和 Podfile.lock(强制清理)
150
+ puts "\n🧹 清理 Pods 和 Podfile.lock..."
151
+ pods_dir = File.join(@project_path, "Pods")
152
+ podfile_lock = File.join(@project_path, "Podfile.lock")
153
+ FileUtils.rm_rf(pods_dir) if File.exist?(pods_dir)
154
+ FileUtils.rm_rf(podfile_lock) if File.exist?(podfile_lock)
155
+
156
+ # 2. 更新私有索引库
157
+ Pindo::CocoaPodsHelper.update_pod_repo(
158
+ install: false,
159
+ project_dir: @project_path
160
+ )
161
+
162
+ # 3. 移除测试 Pod 模块
163
+ Pindo::CocoaPodsHelper.remove_test_pods(@project_path)
164
+
165
+ # 4. 清理并重新安装(删除 Podfile.lock)
166
+ Pindo::CocoaPodsHelper.deintegrate_and_install(@project_path, clean_podfile_lock: true)
167
+
168
+ # 5. 备份 Podfile.lock 到配置仓库
169
+ unless app_config_dir && File.directory?(app_config_dir)
170
+ raise Informative, "配置仓库目录不存在或无法访问,无法备份 Podfile.lock: #{app_config_dir}"
171
+ end
172
+
173
+ puts "\n💾 备份 Podfile.lock 到配置仓库: #{app_config_dir}"
174
+ Pindo::CocoaPodsHelper.backup_podfile_lock(
175
+ @project_path,
176
+ app_config_dir,
177
+ app_version
178
+ )
179
+ end
180
+
87
181
  private
88
182
 
89
183
  # 加载 iOS 配置
@@ -291,21 +385,6 @@ module Pindo
291
385
  end
292
386
  end
293
387
 
294
-
295
- # 处理 CocoaPods
296
- def handle_cocoapods
297
- return unless File.exist?(File.join(@project_path, "Podfile"))
298
-
299
- # 1. 更新私有索引库
300
- Pindo::CocoaPodsHelper.update_pod_repo(
301
- install: false,
302
- project_dir: @project_path
303
- )
304
-
305
- # 2. 清理并重新安装
306
- Pindo::CocoaPodsHelper.deintegrate_and_install(@project_path)
307
- end
308
-
309
388
  # 检测是否为 macOS 平台
310
389
  def detect_macos_platform
311
390
  project_fullname = Dir.glob(File.join(@project_path, "/*.xcodeproj")).max_by { |f| File.mtime(f) }
@@ -84,6 +84,47 @@ module Pindo
84
84
  end
85
85
  end
86
86
 
87
+ # 处理 CocoaPods 依赖(AppStore 构建)
88
+ def handle_cocoapods
89
+ return unless File.exist?(File.join(@project_path, "Podfile"))
90
+
91
+ require 'pindo/module/xcode/cocoapods_helper'
92
+ require 'pindo/config/pindoconfig'
93
+
94
+ config_parser = Pindo::IosConfigParser.instance
95
+
96
+ # 使用原始 Bundle ID(app_repo_name)作为配置仓库名称
97
+ app_repo_name = config_parser.app_repo_name
98
+
99
+ # 获取配置仓库目录
100
+ pindo_dir = File.expand_path(Pindo::Pindoconfig.instance.pindo_dir)
101
+ app_config_dir = File.join(pindo_dir, app_repo_name)
102
+
103
+ # 1. 清理 Pods 和 Podfile.lock(强制清理)
104
+ puts "\n🧹 清理 Pods 和 Podfile.lock..."
105
+ pods_dir = File.join(@project_path, "Pods")
106
+ podfile_lock = File.join(@project_path, "Podfile.lock")
107
+ FileUtils.rm_rf(pods_dir) if File.exist?(pods_dir)
108
+ FileUtils.rm_rf(podfile_lock) if File.exist?(podfile_lock)
109
+
110
+ # 2. 更新私有索引库
111
+ Pindo::CocoaPodsHelper.update_pod_repo(
112
+ install: false,
113
+ project_dir: @project_path
114
+ )
115
+
116
+ # 3. 移除测试 Pod 模块
117
+ Pindo::CocoaPodsHelper.remove_test_pods(@project_path)
118
+
119
+ # 4. 从配置仓库拷贝 Podfile.lock
120
+ if File.directory?(app_config_dir)
121
+ Pindo::CocoaPodsHelper.copy_podfile_lock_from_config(@project_path, app_config_dir)
122
+ end
123
+
124
+ # 5. 清理并重新安装(不删除 Podfile.lock)
125
+ Pindo::CocoaPodsHelper.deintegrate_and_install(@project_path, clean_podfile_lock: false)
126
+ end
127
+
87
128
  private
88
129
 
89
130
  # 加载 iOS 配置
@@ -291,20 +332,6 @@ module Pindo
291
332
  end
292
333
  end
293
334
 
294
- # 处理 CocoaPods
295
- def handle_cocoapods
296
- return unless File.exist?(File.join(@project_path, "Podfile"))
297
-
298
- # 1. 更新私有索引库
299
- Pindo::CocoaPodsHelper.update_pod_repo(
300
- install: false,
301
- project_dir: @project_path
302
- )
303
-
304
- # 2. 清理并重新安装
305
- Pindo::CocoaPodsHelper.deintegrate_and_install(@project_path)
306
- end
307
-
308
335
  # 检测是否为 macOS 平台
309
336
  def detect_macos_platform
310
337
  project_fullname = Dir.glob(File.join(@project_path, "/*.xcodeproj")).max_by { |f| File.mtime(f) }
@@ -70,6 +70,29 @@ module Pindo
70
70
  end
71
71
  end
72
72
 
73
+ # 处理 CocoaPods 依赖(Dev 构建)
74
+ def handle_cocoapods
75
+ return unless File.exist?(File.join(@project_path, "Podfile"))
76
+
77
+ require 'pindo/module/xcode/cocoapods_helper'
78
+
79
+ # 1. 清理 Pods 和 Podfile.lock(强制清理)
80
+ puts "\n🧹 清理 Pods 和 Podfile.lock..."
81
+ pods_dir = File.join(@project_path, "Pods")
82
+ podfile_lock = File.join(@project_path, "Podfile.lock")
83
+ FileUtils.rm_rf(pods_dir) if File.exist?(pods_dir)
84
+ FileUtils.rm_rf(podfile_lock) if File.exist?(podfile_lock)
85
+
86
+ # 2. 更新私有索引库
87
+ Pindo::CocoaPodsHelper.update_pod_repo(
88
+ install: false,
89
+ project_dir: @project_path
90
+ )
91
+
92
+ # 3. 清理并重新安装(删除 Podfile.lock)
93
+ Pindo::CocoaPodsHelper.deintegrate_and_install(@project_path, clean_podfile_lock: true)
94
+ end
95
+
73
96
  private
74
97
 
75
98
  # Git 标签检查
@@ -158,20 +181,6 @@ module Pindo
158
181
  )
159
182
  end
160
183
 
161
- # 处理 CocoaPods
162
- def handle_cocoapods
163
- return unless File.exist?(File.join(@project_path, "Podfile"))
164
-
165
- # 1. 更新私有索引库
166
- Pindo::CocoaPodsHelper.update_pod_repo(
167
- install: false,
168
- project_dir: @project_path
169
- )
170
-
171
- # 2. 清理并重新安装
172
- Pindo::CocoaPodsHelper.deintegrate_and_install(@project_path)
173
- end
174
-
175
184
  # 配置证书
176
185
  def configure_certificate
177
186
  # 检查是否是 macOS 工程