pindo 5.14.8 → 5.15.1

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 (36) hide show
  1. checksums.yaml +4 -4
  2. data/lib/pindo/base/aeshelper.rb +53 -22
  3. data/lib/pindo/base/git_handler.rb +243 -22
  4. data/lib/pindo/client/giteeclient.rb +15 -6
  5. data/lib/pindo/command/android/autobuild.rb +7 -5
  6. data/lib/pindo/command/appstore/autobuild.rb +5 -2
  7. data/lib/pindo/command/appstore/cert.rb +6 -0
  8. data/lib/pindo/command/ios/autobuild.rb +31 -21
  9. data/lib/pindo/command/ios/cert.rb +1 -1
  10. data/lib/pindo/command/jps/apptest.rb +1 -1
  11. data/lib/pindo/command/jps/bind.rb +6 -2
  12. data/lib/pindo/command/jps/media.rb +6 -2
  13. data/lib/pindo/command/jps/upload.rb +6 -4
  14. data/lib/pindo/command/repo/clone.rb +110 -32
  15. data/lib/pindo/command/repo/subgit.rb +91 -0
  16. data/lib/pindo/command/repo.rb +1 -0
  17. data/lib/pindo/command/unity/autobuild.rb +6 -4
  18. data/lib/pindo/command/unity/packbuild.rb +14 -7
  19. data/lib/pindo/command/utils/tag.rb +5 -3
  20. data/lib/pindo/command/web/autobuild.rb +7 -5
  21. data/lib/pindo/command.rb +1 -1
  22. data/lib/pindo/config/build_info_manager.rb +37 -14
  23. data/lib/pindo/module/appstore/bundleid_helper.rb +7 -3
  24. data/lib/pindo/module/build/git_repo_helper.rb +1 -14
  25. data/lib/pindo/module/cert/cert_helper.rb +33 -9
  26. data/lib/pindo/module/cert/xcode_cert_helper.rb +17 -7
  27. data/lib/pindo/module/pgyer/pgyerhelper.rb +110 -22
  28. data/lib/pindo/module/task/model/git/git_commit_task.rb +106 -6
  29. data/lib/pindo/module/task/model/git/git_tag_task.rb +17 -7
  30. data/lib/pindo/module/task/model/jps/jps_upload_media_task.rb +13 -8
  31. data/lib/pindo/module/task/model/resign/ipa_local_resign_task.rb +5 -0
  32. data/lib/pindo/options/core/option_item.rb +4 -5
  33. data/lib/pindo/options/groups/git_options.rb +40 -0
  34. data/lib/pindo/options/helpers/git_constants.rb +28 -0
  35. data/lib/pindo/version.rb +1 -1
  36. metadata +7 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 68b5c0f86d764eff85c26932fb2ba31d4e7078adca1d216b9c6e40901cc5408c
4
- data.tar.gz: 1389910ee8c9fd7f571634001816e8e5776ead59cf59c65fb806edf57b1eb020
3
+ metadata.gz: f82f06b4cb4ed845ca62c0db43e4684b141d87b8271b705a2a4de0e0a9c8355b
4
+ data.tar.gz: 7b77b67d53e8f1ca601f32190d8d6534d146837a302facce49c7afa782ecd47f
5
5
  SHA512:
6
- metadata.gz: 3cd5a36c2d7fc0a607c53f47595cf129c05129066bc77894d984b1e4e639ba017f0045edf224efd1b12352521bf200a9001a7cef1ebb9852d81f3087b3e4b72a
7
- data.tar.gz: 66c0fe689c8bd6ba4c51b4db4f75486fd33941a73d9f47396699e32adcb8e96d13b0dd6e427c653e255bdf54d9c1907504a8059bba605e05cdc91eb1b962d487
6
+ metadata.gz: f051bfa4fb0aa773eaf7d7a20d489e17d065bdede2c78652dd172ec2a3da5f95ae19700d05e6748f3442487fd96e18a7b3040043a4f906e257415353b73a392a
7
+ data.tar.gz: f056935b0928d4e48dd0e6bb88f321d64a2b118eee92ef3ce767697c039868ebc987394e36deacbbcb6ba1111cc8486b93bbc99c71a6f4c71a297bc9a4f3f575
@@ -21,26 +21,7 @@ module Pindo
21
21
  unless password
22
22
  puts "\e[33m[DEBUG] Keychain中未找到密码,需要用户输入: #{server_name}\e[0m" if ENV['PINDO_DEBUG']
23
23
  password = FastlaneCore::Helper.ask_password(message: "请输入证书仓库的加密密码: ", confirm: true)
24
-
25
- # 尝试添加密码到Keychain
26
- begin
27
- # 先尝试删除旧密码(如果存在)
28
- begin
29
- Security::InternetPassword.delete(server: server_name)
30
- puts "\e[33m[DEBUG] 删除Keychain中的旧密码项: #{server_name}\e[0m" if ENV['PINDO_DEBUG']
31
- rescue => delete_error
32
- # 如果不存在就忽略删除错误
33
- puts "\e[33m[DEBUG] 旧密码项不存在或删除失败: #{delete_error.message}\e[0m" if ENV['PINDO_DEBUG']
34
- end
35
-
36
- # 添加新密码到Keychain
37
- Security::InternetPassword.add(server_name, "", password)
38
- puts "\e[32m✓ 密码已保存到Keychain: #{server_name}\e[0m"
39
- rescue => e
40
- # 如果保存失败,警告用户但不影响继续使用
41
- puts "\e[31m⚠ 密码保存到Keychain失败: #{e.message}\e[0m"
42
- puts "\e[31m⚠ 下次使用时需要重新输入密码\e[0m"
43
- end
24
+ puts "\e[33m[DEBUG] 用户输入密码成功,等待验证后保存到Keychain\e[0m" if ENV['PINDO_DEBUG']
44
25
  else
45
26
  puts "\e[32m[DEBUG] 从Keychain获取密码成功: #{server_name}\e[0m" if ENV['PINDO_DEBUG']
46
27
  end
@@ -50,12 +31,62 @@ module Pindo
50
31
 
51
32
  def self.delete_password(keychain_name:nil)
52
33
  server_name = ["match", keychain_name].join("_")
53
- Security::InternetPassword.delete(server:server_name)
34
+ # 静默删除密码,不输出 keychain 详细信息(文件描述符级别重定向)
35
+ begin
36
+ old_stdout = STDOUT.dup
37
+ old_stderr = STDERR.dup
38
+ STDOUT.reopen(File::NULL, 'w')
39
+ STDERR.reopen(File::NULL, 'w')
40
+
41
+ Security::InternetPassword.delete(server:server_name)
42
+ ensure
43
+ STDOUT.reopen(old_stdout)
44
+ STDERR.reopen(old_stderr)
45
+ old_stdout.close
46
+ old_stderr.close
47
+ end
48
+ rescue => e
49
+ # 忽略错误(密码可能不存在)
54
50
  end
55
51
 
56
52
  def self.store_password(keychain_name:nil, password:nil)
57
53
  server_name = ["match", keychain_name].join("_")
58
- Security::InternetPassword.add(server_name, "", password)
54
+
55
+ # 先检查密码是否已存在,如果存在则无需重复保存
56
+ begin
57
+ item = Security::InternetPassword.find(server: server_name)
58
+ if item && item.password == password
59
+ puts "\e[33m[DEBUG] 密码已存在于Keychain,跳过保存: #{server_name}\e[0m" if ENV['PINDO_DEBUG']
60
+ return
61
+ end
62
+ rescue => e
63
+ # 密码不存在,继续保存流程
64
+ end
65
+
66
+ # 静默操作,避免输出错误信息(文件描述符级别重定向)
67
+ begin
68
+ old_stdout = STDOUT.dup
69
+ old_stderr = STDERR.dup
70
+ STDOUT.reopen(File::NULL, 'w')
71
+ STDERR.reopen(File::NULL, 'w')
72
+
73
+ # 先尝试删除旧密码(如果存在但不同)
74
+ begin
75
+ Security::InternetPassword.delete(server:server_name)
76
+ rescue => e
77
+ # 忽略删除错误(密码可能不存在)
78
+ end
79
+
80
+ # 添加新密码
81
+ Security::InternetPassword.add(server_name, "", password)
82
+ ensure
83
+ STDOUT.reopen(old_stdout)
84
+ STDERR.reopen(old_stderr)
85
+ old_stdout.close
86
+ old_stderr.close
87
+ end
88
+ rescue => e
89
+ # 忽略错误,不影响主流程
59
90
  end
60
91
 
61
92
 
@@ -4,6 +4,7 @@ require 'pindo/base/informative'
4
4
  require 'etc'
5
5
  require 'claide'
6
6
  require 'highline/import'
7
+ require 'pindo/options/helpers/git_constants'
7
8
 
8
9
  module Pindo
9
10
  # Git 操作处理类
@@ -517,17 +518,20 @@ module Pindo
517
518
 
518
519
  # 根据 process_type 执行对应操作
519
520
  case process_type
520
- when 'commit', '全部提交'
521
+ when Pindo::UncommittedFilesProcessType::NONE
522
+ # 没有未提交的文件,无需处理
523
+ return
524
+ when Pindo::UncommittedFilesProcessType::COMMIT
521
525
  handle_commit_all(current_project_dir, coding_branch, commit_message)
522
- when 'reset', '全部丢弃更改,回滚代码', 'delete', '全部删除'
526
+ when Pindo::UncommittedFilesProcessType::RESET
523
527
  handle_delete_all(current_project_dir, coding_branch)
524
- when 'stash', '保存到stash区域', '保存到stash区域(改代码本次不生效)'
528
+ when Pindo::UncommittedFilesProcessType::STASH
525
529
  handle_stash(current_project_dir, coding_branch)
526
- when 'skip', '跳过'
530
+ when Pindo::UncommittedFilesProcessType::SKIP
527
531
  # 跳过处理,不做任何操作
528
532
  Funlog.instance.warning("跳过提交,可能导致 build number 与代码仓库不一致")
529
533
  return
530
- when 'exit', '先退出,手动来处理退出'
534
+ when Pindo::UncommittedFilesProcessType::EXIT
531
535
  raise Informative, "请手动处理未提交的文件!!!"
532
536
  else
533
537
  raise ArgumentError, "不支持的处理方式: #{process_type}"
@@ -620,13 +624,16 @@ module Pindo
620
624
 
621
625
  # 生成 stash 信息,包含分支名和时间戳
622
626
  timestamp = Time.now.strftime("%Y-%m-%d %H:%M:%S")
623
- stash_message = "pindo on #{branch} - #{timestamp}"
627
+ # 统一使用 pindo_stash 前缀,方便查找和还原
628
+ stash_name = "pindo_stash_#{Time.now.strftime('%Y%m%d%H%M%S')}_#{rand(1000)}"
629
+ stash_message = "#{stash_name} (on #{branch} at #{timestamp})"
624
630
 
625
631
  # 保存到 stash(包含未追踪的文件)
626
632
  git!(%W(-C #{project_dir} stash push -u -m #{stash_message}))
627
633
 
628
634
  Funlog.instance.fancyinfo_success("文件已保存到 stash 区域!")
629
- Funlog.instance.fancyinfo_success("stash 信息: #{stash_message}")
635
+ Funlog.instance.fancyinfo_success("stash key: #{stash_name}")
636
+ Funlog.instance.fancyinfo_success("stash msg: #{stash_message}")
630
637
 
631
638
  # 显示当前 stash 列表
632
639
  stash_list = git!(%W(-C #{project_dir} stash list))
@@ -689,7 +696,7 @@ module Pindo
689
696
  parts = head_info.split('|', 3)
690
697
  result[:commit_id] = parts[0]
691
698
  result[:commit_time] = parts[1]
692
- result[:commit_desc] = parts[2]
699
+ result[:commit_desc] = parts[2]&.force_encoding('UTF-8')&.scrub('')
693
700
  puts " HEAD 存在版本 tag,使用 HEAD commit: #{result[:commit_desc]}"
694
701
  return result
695
702
  end
@@ -709,7 +716,7 @@ module Pindo
709
716
  {
710
717
  commit_id: parts[0],
711
718
  commit_time: parts[1],
712
- commit_desc: parts[2]
719
+ commit_desc: parts[2]&.force_encoding('UTF-8')&.scrub('')
713
720
  }
714
721
  end
715
722
 
@@ -861,26 +868,26 @@ module Pindo
861
868
  # 获取未提交文件的处理方式
862
869
  # @param project_dir [String] 项目目录路径
863
870
  # @param interactive [Boolean] 是否允许交互,默认为 true
864
- # @param default_process_type [String] 当没有未提交文件时的默认处理方式,默认为 'skip'
865
- # @return [String] 处理方式 ('commit', 'skip', 'reset', 'stash', 'exit')
866
- def get_uncommitted_files_process_type(project_dir:, interactive: true, default_process_type: 'skip')
871
+ # @param default_process_type [Symbol, nil] 非交互模式下的默认处理方式,默认为 nil
872
+ # @return [Symbol] UncommittedFilesProcessType 枚举常量
873
+ def get_uncommitted_files_process_type(project_dir:, interactive: true, default_process_type: nil)
867
874
  # 1. 检查是否有未提交的文件
868
875
  uncommitted_files = get_uncommitted_files(project_dir: project_dir)
869
876
 
870
- # 2. 如果没有未提交的文件,返回默认处理方式
877
+ # 2. 如果没有未提交的文件,返回 NONE 类型
871
878
  return default_process_type if uncommitted_files.nil? || uncommitted_files.empty?
872
879
 
873
- # 3. 如果非交互模式,默认跳过
880
+ # 3. 如果非交互模式,使用指定的处理方式
874
881
  unless interactive
875
- Funlog.instance.info("当前处于非交互模式,默认跳过未提交文件的处理。")
876
- return 'skip'
882
+ Funlog.instance.info("当前处于非交互模式,使用指定的处理方式: #{default_process_type}")
883
+ return default_process_type
877
884
  end
878
885
 
879
886
  # 4. 获取 Git 根目录并获取当前分支名
880
887
  git_root = git_root_directory(local_repo_dir: project_dir)
881
888
  unless git_root
882
889
  Funlog.instance.fancyinfo_warning("当前目录不是Git仓库")
883
- return 'skip'
890
+ return Pindo::UncommittedFilesProcessType::SKIP
884
891
  end
885
892
 
886
893
  current_branch = git!(%W(-C #{git_root} rev-parse --abbrev-ref HEAD)).strip
@@ -891,11 +898,11 @@ module Pindo
891
898
  cli.choose do |menu|
892
899
  menu.header = "仓库有未提交的修改,请选择处理方式"
893
900
  menu.prompt = "请输入选项(1/2/3/4/5):"
894
- menu.choice("(commit) 全部提交") { 'commit' }
895
- menu.choice("(skip) 跳过并且不作任何修改,继续执行") { 'skip' }
896
- menu.choice("(reset) 全部丢弃更改,回滚代码") { 'reset' }
897
- menu.choice("(stash) 保存到stash区域(该部分代码本次不生效)") { 'stash' }
898
- menu.choice("(exit) 先退出,手动来处理退出") { 'exit' }
901
+ menu.choice("(commit) 全部提交") { Pindo::UncommittedFilesProcessType::COMMIT }
902
+ menu.choice("(skip) 跳过并且不作任何修改,继续执行") { Pindo::UncommittedFilesProcessType::SKIP }
903
+ menu.choice("(reset) 全部丢弃更改,回滚代码") { Pindo::UncommittedFilesProcessType::RESET }
904
+ menu.choice("(stash) 保存到stash区域(该部分代码本次不生效)") { Pindo::UncommittedFilesProcessType::STASH }
905
+ menu.choice("(exit) 先退出,手动来处理退出") { Pindo::UncommittedFilesProcessType::EXIT }
899
906
  end
900
907
  end
901
908
 
@@ -1109,6 +1116,220 @@ module Pindo
1109
1116
  end
1110
1117
  end
1111
1118
 
1119
+ # 初始化并递归更新所有 Git 子模块
1120
+ # @param local_repo_dir [String] Git 仓库目录路径
1121
+ # @param force [Boolean] 是否强制重新初始化子模块
1122
+ # @return [Boolean] 成功返回 true,无子模块返回 false
1123
+ def update_submodules(local_repo_dir:, force: false)
1124
+ raise ArgumentError, "项目目录不能为空" if local_repo_dir.nil?
1125
+
1126
+ # 验证是否是 Git 仓库
1127
+ unless is_git_directory?(local_repo_dir: local_repo_dir)
1128
+ raise Informative, "#{local_repo_dir} 不是有效的 Git 仓库!"
1129
+ end
1130
+
1131
+ # 检查 .gitmodules 文件是否存在
1132
+ git_root = git_root_directory(local_repo_dir: local_repo_dir)
1133
+ gitmodules_path = File.join(git_root, '.gitmodules')
1134
+
1135
+ unless File.exist?(gitmodules_path)
1136
+ Funlog.instance.fancyinfo_warning("当前仓库没有子模块配置文件 (.gitmodules)")
1137
+ return false
1138
+ end
1139
+
1140
+ # 显示子模块信息
1141
+ Funlog.instance.fancyinfo_start("正在读取子模块配置...")
1142
+ submodules_count = parse_gitmodules_count(gitmodules_path)
1143
+ Funlog.instance.fancyinfo_success("检测到 #{submodules_count} 个子模块")
1144
+
1145
+ # 检查子模块路径冲突
1146
+ conflicts = check_submodule_path_conflicts(git_root, gitmodules_path)
1147
+ unless conflicts.empty?
1148
+ if force
1149
+ # 使用 --force 参数,自动清理冲突路径
1150
+ Funlog.instance.fancyinfo_start("检测到 #{conflicts.size} 个路径冲突,正在清理...")
1151
+ conflicts.each do |conflict|
1152
+ begin
1153
+ FileUtils.rm_rf(conflict[:full_path])
1154
+ Funlog.instance.fancyinfo_success("已清理: #{conflict[:path]} (包含 #{conflict[:files]} 个文件)")
1155
+ rescue => e
1156
+ Funlog.instance.fancyinfo_error("清理失败: #{conflict[:path]} - #{e.message}")
1157
+ raise Informative, "无法清理路径冲突:#{conflict[:path]}\n#{e.message}"
1158
+ end
1159
+ end
1160
+ else
1161
+ # 不使用 --force,报错退出
1162
+ Funlog.instance.fancyinfo_error("检测到 #{conflicts.size} 个子模块路径冲突")
1163
+ puts "\n以下路径已存在但不是 Git 仓库,其中的文件会污染子模块:\n".yellow
1164
+ conflicts.each do |conflict|
1165
+ puts " • #{conflict[:path]} (包含 #{conflict[:files]} 个文件)".yellow
1166
+ end
1167
+ puts "\n请选择处理方式:".yellow
1168
+ puts " 1. 使用 --force 参数自动清理这些目录".yellow
1169
+ puts " 2. 手动删除或备份这些目录后重新执行\n".yellow
1170
+ raise Informative, "子模块路径冲突!请处理后重试。"
1171
+ end
1172
+ end
1173
+
1174
+ # 构建 git submodule update 命令
1175
+ args = %W(-C #{git_root} submodule update --init --recursive)
1176
+ args << '--force' if force
1177
+ args << '--progress'
1178
+
1179
+ # 执行子模块更新
1180
+ begin
1181
+ Funlog.instance.fancyinfo_start("正在初始化和更新所有子模块...")
1182
+ output = git!(args)
1183
+ Funlog.instance.fancyinfo_success("子模块更新完成!")
1184
+
1185
+ # 显示子模块状态
1186
+ display_submodules_status(git_root)
1187
+
1188
+ # 检查子模块是否有未提交的内容
1189
+ check_submodule_dirty_state(git_root, gitmodules_path)
1190
+
1191
+ true
1192
+ rescue StandardError => e
1193
+ Funlog.instance.fancyinfo_error("子模块更新失败: #{e.message}")
1194
+ raise Informative, "子模块更新失败!\n详细错误:#{e.message}"
1195
+ end
1196
+ end
1197
+
1198
+ # 解析 .gitmodules 文件,统计子模块数量
1199
+ # @param gitmodules_path [String] .gitmodules 文件路径
1200
+ # @return [Integer] 子模块数量
1201
+ def parse_gitmodules_count(gitmodules_path)
1202
+ return 0 unless File.exist?(gitmodules_path)
1203
+
1204
+ content = File.read(gitmodules_path)
1205
+ # 统计 [submodule "xxx"] 的数量
1206
+ content.scan(/\[submodule\s+"[^"]+"\]/).count
1207
+ end
1208
+
1209
+ # 显示子模块状态信息
1210
+ # @param git_root [String] Git 根目录
1211
+ def display_submodules_status(git_root)
1212
+ begin
1213
+ status_output = git!(%W(-C #{git_root} submodule status --recursive))
1214
+
1215
+ unless status_output.nil? || status_output.strip.empty?
1216
+ puts "\n" + "═" * 60
1217
+ puts "子模块状态".center(60)
1218
+ puts "═" * 60
1219
+ puts status_output
1220
+ puts "═" * 60 + "\n"
1221
+ end
1222
+ rescue StandardError => e
1223
+ Funlog.instance.warning("无法获取子模块状态: #{e.message}")
1224
+ end
1225
+ end
1226
+
1227
+ # 解析 .gitmodules 文件,提取所有子模块路径
1228
+ # @param gitmodules_path [String] .gitmodules 文件路径
1229
+ # @return [Array<String>] 子模块路径列表
1230
+ def parse_submodule_paths(gitmodules_path)
1231
+ return [] unless File.exist?(gitmodules_path)
1232
+
1233
+ content = File.read(gitmodules_path)
1234
+ paths = []
1235
+
1236
+ # 匹配 path = xxx 行
1237
+ content.scan(/^\s*path\s*=\s*(.+)$/) do |match|
1238
+ paths << match[0].strip
1239
+ end
1240
+
1241
+ paths
1242
+ end
1243
+
1244
+ # 检查子模块路径冲突
1245
+ # @param git_root [String] Git 根目录
1246
+ # @param gitmodules_path [String] .gitmodules 文件路径
1247
+ # @return [Array<Hash>] 冲突列表,每个元素包含 :path, :full_path, :files
1248
+ def check_submodule_path_conflicts(git_root, gitmodules_path)
1249
+ submodule_paths = parse_submodule_paths(gitmodules_path)
1250
+ conflicts = []
1251
+
1252
+ submodule_paths.each do |path|
1253
+ full_path = File.join(git_root, path)
1254
+
1255
+ # 检查:路径存在 && 不是 git 仓库 && 不为空
1256
+ if File.exist?(full_path) && File.directory?(full_path)
1257
+ git_dir = File.join(full_path, '.git')
1258
+
1259
+ unless File.exist?(git_dir)
1260
+ # 检查目录是否为空
1261
+ unless Dir.empty?(full_path)
1262
+ conflicts << {
1263
+ path: path,
1264
+ full_path: full_path,
1265
+ files: Dir.children(full_path).size
1266
+ }
1267
+ end
1268
+ end
1269
+ end
1270
+ end
1271
+
1272
+ conflicts
1273
+ end
1274
+
1275
+ # 检查子模块是否有未提交的内容(脏数据)
1276
+ # @param git_root [String] Git 根目录
1277
+ # @param gitmodules_path [String] .gitmodules 文件路径
1278
+ def check_submodule_dirty_state(git_root, gitmodules_path)
1279
+ submodule_paths = parse_submodule_paths(gitmodules_path)
1280
+ dirty_submodules = []
1281
+
1282
+ submodule_paths.each do |path|
1283
+ full_path = File.join(git_root, path)
1284
+ next unless File.exist?(full_path)
1285
+
1286
+ begin
1287
+ # 使用 git status --porcelain 检查是否有修改
1288
+ status = git!(%W(-C #{full_path} status --porcelain))
1289
+
1290
+ unless status.nil? || status.strip.empty?
1291
+ # 统计修改类型
1292
+ lines = status.strip.split("\n")
1293
+ untracked = lines.count { |line| line.start_with?('??') }
1294
+ modified = lines.count { |line| line.start_with?(' M', 'M ', 'MM') }
1295
+ added = lines.count { |line| line.start_with?('A ') }
1296
+ deleted = lines.count { |line| line.start_with?(' D', 'D ') }
1297
+
1298
+ dirty_submodules << {
1299
+ path: path,
1300
+ untracked: untracked,
1301
+ modified: modified,
1302
+ added: added,
1303
+ deleted: deleted,
1304
+ total: lines.size
1305
+ }
1306
+ end
1307
+ rescue => e
1308
+ # 忽略错误,继续检查其他子模块
1309
+ end
1310
+ end
1311
+
1312
+ # 如果有脏数据,显示警告
1313
+ unless dirty_submodules.empty?
1314
+ puts "\n"
1315
+ Funlog.instance.fancyinfo_warning("⚠️ 检测到 #{dirty_submodules.size} 个子模块包含未提交的内容")
1316
+ puts "\n以下子模块有未提交的修改:\n".yellow
1317
+
1318
+ dirty_submodules.each do |submodule|
1319
+ puts " • #{submodule[:path]}:".yellow
1320
+ puts " - 未跟踪文件: #{submodule[:untracked]} 个".yellow if submodule[:untracked] > 0
1321
+ puts " - 已修改文件: #{submodule[:modified]} 个".yellow if submodule[:modified] > 0
1322
+ puts " - 已添加文件: #{submodule[:added]} 个".yellow if submodule[:added] > 0
1323
+ puts " - 已删除文件: #{submodule[:deleted]} 个".yellow if submodule[:deleted] > 0
1324
+ end
1325
+
1326
+ puts "\n建议处理方式:".yellow
1327
+ puts " 1. 进入子模块目录处理这些修改:cd #{dirty_submodules.first[:path]}".yellow
1328
+ puts " 2. 在子模块中提交修改:git add . && git commit -m 'message'".yellow
1329
+ puts " 3. 或者丢弃修改:git clean -fd && git reset --hard\n".yellow
1330
+ end
1331
+ end
1332
+
1112
1333
  end
1113
1334
  end
1114
1335
  end
@@ -41,21 +41,30 @@ module Pindo
41
41
 
42
42
  # puts body_str
43
43
 
44
- con = Faraday.new
44
+ con = Faraday.new
45
45
  res = con.post do |req|
46
46
  req.url url
47
47
  req.headers['Content-Type'] = 'application/json'
48
48
  req.body = body_str
49
49
  end
50
50
 
51
- # puts res.body
51
+ # 解析 JSON 响应
52
+ begin
53
+ response_body = JSON.parse(res.body)
54
+ rescue JSON::ParserError => e
55
+ puts "[❌] Gitee API 响应解析失败: #{e.message}"
56
+ puts "响应内容: #{res.body}"
57
+ return false
58
+ end
52
59
 
53
- # puts res.body
54
- if res.body['error'].nil?
55
- puts "Create success !!!"
60
+ # 检查响应状态
61
+ if response_body['error'].nil? && response_body['message'].nil?
62
+ puts "[✔] Gitee 仓库创建成功: #{repo_name}"
56
63
  return true
57
64
  else
58
- puts res.body['error']
65
+ error_msg = response_body['error'] || response_body['message'] || '未知错误'
66
+ puts "[❌] Gitee 仓库创建失败: #{error_msg}"
67
+ puts "响应详情: #{response_body}" if ENV['PINDO_DEBUG']
59
68
  return false
60
69
  end
61
70
  end
@@ -130,9 +130,10 @@ module Pindo
130
130
 
131
131
  # Git 参数
132
132
  @args_release_branch = @options[:release_branch] || 'master'
133
- @args_ver_inc = Pindo::Options::GitOptions.parse_version_increase_type(@options[:ver_inc] || 'mini')
134
- @args_tag_type = Pindo::Options::GitOptions.parse_create_tag_type(@options[:tag_type] || 'new')
133
+ @args_ver_inc = Pindo::Options::GitOptions.parse_version_increase_type(@options[:ver_inc])
134
+ @args_tag_type = Pindo::Options::GitOptions.parse_create_tag_type(@options[:tag_type])
135
135
  @args_tag_pre = @options[:tag_pre] || 'v'
136
+ @args_git_commit = Pindo::Options::GitOptions.parse_git_commit_type(@options[:git_commit])
136
137
 
137
138
  super
138
139
  @additional_args = argv.remainder!
@@ -212,7 +213,8 @@ module Pindo
212
213
  # 提前询问用户如何处理未提交的文件
213
214
  process_type = Pindo::GitHandler.get_uncommitted_files_process_type(
214
215
  project_dir: config[:project_path],
215
- interactive: true
216
+ interactive: @args_git_commit.nil?,
217
+ default_process_type: @args_git_commit
216
218
  )
217
219
  git_commit_task = Pindo::TaskSystem::GitCommitTask.new(
218
220
  config[:project_path],
@@ -341,7 +343,7 @@ module Pindo
341
343
  git_app_info_obj, git_workflow_info = PgyerHelper.share_instace.prepare_upload(
342
344
  working_directory: config[:project_path],
343
345
  proj_name: @args_proj_name,
344
- package_type: "", # package_type 在 manage_type=git 时会被忽略
346
+ package_type: nil, # package_type 在 manage_type=git 时会被忽略
345
347
  manage_type: "git"
346
348
  )
347
349
 
@@ -379,7 +381,7 @@ module Pindo
379
381
  git_app_info_obj, git_workflow_info = PgyerHelper.share_instace.prepare_upload(
380
382
  working_directory: config[:project_path],
381
383
  proj_name: @args_proj_name,
382
- package_type: "",
384
+ package_type: nil,
383
385
  manage_type: "git"
384
386
  )
385
387
 
@@ -6,6 +6,7 @@ require 'fileutils'
6
6
  require 'pindo/base/executable'
7
7
  require 'pindo/base/git_handler'
8
8
  require 'pindo/module/build/build_helper'
9
+ require 'pindo/options/helpers/git_constants'
9
10
  require 'pindo/module/xcode/xcode_app_config'
10
11
  require 'pindo/module/xcode/xcode_build_helper'
11
12
  require 'pindo/config/ios_config_parser'
@@ -94,8 +95,9 @@ module Pindo
94
95
 
95
96
  # Git 参数
96
97
  @args_release_branch = @options[:release_branch] || 'master'
97
- @args_tag_type = Pindo::Options::GitOptions.parse_create_tag_type(@options[:tag_type] || 'new')
98
+ @args_tag_type = Pindo::Options::GitOptions.parse_create_tag_type(@options[:tag_type])
98
99
  @args_tag_pre = @options[:tag_pre] || 'ios_release_'
100
+ @args_git_commit = Pindo::Options::GitOptions.parse_git_commit_type(@options[:git_commit]) || Pindo::UncommittedFilesProcessType::SKIP
99
101
 
100
102
  super(argv)
101
103
  end
@@ -191,7 +193,8 @@ module Pindo
191
193
  # 提前询问用户如何处理未提交的文件
192
194
  process_type = Pindo::GitHandler.get_uncommitted_files_process_type(
193
195
  project_dir: project_path,
194
- interactive: true
196
+ interactive: false,
197
+ default_process_type: @args_git_commit
195
198
  )
196
199
 
197
200
  git_commit_task = Pindo::TaskSystem::GitCommitTask.new(
@@ -166,7 +166,13 @@ module Pindo
166
166
  renew_flag: @renew_cert_flag
167
167
  )
168
168
  config = FastlaneCore::Configuration.create(Match::Options.available_options, values)
169
+
170
+ # 注意:fastlane match 可能会产生 '--template_name' 弃用警告
171
+ # 这是 fastlane 内部使用的参数,在 App Store Connect API v3.8.0 中已弃用
172
+ # 该警告不影响功能,fastlane 会自动处理兼容性
173
+ # 参考:https://docs.fastlane.tools/actions/match/#managed-capabilities
169
174
  Match::Runner.new.run(config)
175
+
170
176
  provisioning_info_array = Pindo::XcodeCertHelper.create_provisioning_info_array(
171
177
  build_type: @cert_type,
172
178
  platform_type: @platform_type
@@ -144,9 +144,10 @@ module Pindo
144
144
 
145
145
  # Git 参数
146
146
  @args_release_branch = @options[:release_branch] || 'master'
147
- @args_ver_inc = Pindo::Options::GitOptions.parse_version_increase_type(@options[:ver_inc] || 'mini')
148
- @args_tag_type = Pindo::Options::GitOptions.parse_create_tag_type(@options[:tag_type] || 'new')
147
+ @args_ver_inc = Pindo::Options::GitOptions.parse_version_increase_type(@options[:ver_inc])
148
+ @args_tag_type = Pindo::Options::GitOptions.parse_create_tag_type(@options[:tag_type])
149
149
  @args_tag_pre = @options[:tag_pre] || 'v'
150
+ @args_git_commit = Pindo::Options::GitOptions.parse_git_commit_type(@options[:git_commit])
150
151
 
151
152
  super
152
153
  @additional_args = argv.remainder!
@@ -222,7 +223,8 @@ module Pindo
222
223
  # 提前询问用户如何处理未提交的文件
223
224
  process_type = Pindo::GitHandler.get_uncommitted_files_process_type(
224
225
  project_dir: config[:project_path],
225
- interactive: true
226
+ interactive: @args_git_commit.nil?,
227
+ default_process_type: @args_git_commit
226
228
  )
227
229
  git_commit_task = Pindo::TaskSystem::GitCommitTask.new(
228
230
  config[:project_path],
@@ -348,24 +350,32 @@ module Pindo
348
350
 
349
351
  # 6. 创建媒体附件上传任务(如果需要,只依赖 Git 提交任务)
350
352
  if @args_media_flag
351
- # 获取 Git 管理类型的工作流(不同于 IPA 上传的工作流)
352
- git_app_info_obj, git_workflow_info = PgyerHelper.share_instace.prepare_upload(
353
- working_directory: config[:project_path],
354
- proj_name: @args_proj_name,
355
- package_type: "", # package_type 在 manage_type=git 时会被忽略
356
- manage_type: "git"
357
- )
353
+ begin
354
+ # 获取 Git 管理类型的工作流(不同于 IPA 上传的工作流)
355
+ git_app_info_obj, git_workflow_info = PgyerHelper.share_instace.prepare_upload(
356
+ working_directory: config[:project_path],
357
+ proj_name: @args_proj_name,
358
+ package_type: nil, # package_type 在 manage_type=git 时会被忽略
359
+ manage_type: "git"
360
+ )
358
361
 
359
- media_upload_task = Pindo::TaskSystem::JPSUploadMediaTask.new(
360
- [], # 空数组,自动从 JPSMedia/ 目录查找
361
- config[:project_path], # upload_path
362
- app_info_obj: git_app_info_obj,
363
- workflow_info: git_workflow_info,
364
- project_name: @args_proj_name
365
- )
366
- # 依赖 Git 提交任务
367
- media_upload_task.dependencies << git_commit_task.id
368
- tasks << media_upload_task
362
+ media_upload_task = Pindo::TaskSystem::JPSUploadMediaTask.new(
363
+ [], # 空数组,自动从 JPSMedia/ 目录查找
364
+ config[:project_path], # upload_path
365
+ app_info_obj: git_app_info_obj,
366
+ workflow_info: git_workflow_info,
367
+ project_name: @args_proj_name
368
+ )
369
+ # 依赖 Git 提交任务
370
+ media_upload_task.dependencies << git_commit_task.id
371
+ tasks << media_upload_task
372
+ rescue => e
373
+ puts ""
374
+ puts " ⚠️ 跳过媒体上传任务: #{e.message}"
375
+ puts " 💡 提示: 请在 JPS 后台配置 Git 类型的工作流(manage_type: git)"
376
+ puts ""
377
+ # 不抛出异常,继续执行后续任务
378
+ end
369
379
  end
370
380
 
371
381
  # 7. 创建 Git Commit 绑定任务(如果需要,依赖上传任务)
@@ -390,7 +400,7 @@ module Pindo
390
400
  git_app_info_obj, git_workflow_info = PgyerHelper.share_instace.prepare_upload(
391
401
  working_directory: config[:project_path],
392
402
  proj_name: @args_proj_name,
393
- package_type: "",
403
+ package_type: nil,
394
404
  manage_type: "git"
395
405
  )
396
406
 
@@ -109,7 +109,7 @@ module Pindo
109
109
  project_dir: project_dir
110
110
  )
111
111
 
112
- puts "证书安装和配置完成!"
112
+ Funlog.instance.fancyinfo_success("证书安装和配置完成!")
113
113
  end
114
114
 
115
115