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.
- checksums.yaml +4 -4
- data/lib/pindo/base/aeshelper.rb +53 -22
- data/lib/pindo/base/git_handler.rb +243 -22
- data/lib/pindo/client/giteeclient.rb +15 -6
- data/lib/pindo/command/android/autobuild.rb +7 -5
- data/lib/pindo/command/appstore/autobuild.rb +5 -2
- data/lib/pindo/command/appstore/cert.rb +6 -0
- data/lib/pindo/command/ios/autobuild.rb +31 -21
- data/lib/pindo/command/ios/cert.rb +1 -1
- data/lib/pindo/command/jps/apptest.rb +1 -1
- data/lib/pindo/command/jps/bind.rb +6 -2
- data/lib/pindo/command/jps/media.rb +6 -2
- data/lib/pindo/command/jps/upload.rb +6 -4
- data/lib/pindo/command/repo/clone.rb +110 -32
- data/lib/pindo/command/repo/subgit.rb +91 -0
- data/lib/pindo/command/repo.rb +1 -0
- data/lib/pindo/command/unity/autobuild.rb +6 -4
- data/lib/pindo/command/unity/packbuild.rb +14 -7
- data/lib/pindo/command/utils/tag.rb +5 -3
- data/lib/pindo/command/web/autobuild.rb +7 -5
- data/lib/pindo/command.rb +1 -1
- data/lib/pindo/config/build_info_manager.rb +37 -14
- data/lib/pindo/module/appstore/bundleid_helper.rb +7 -3
- data/lib/pindo/module/build/git_repo_helper.rb +1 -14
- data/lib/pindo/module/cert/cert_helper.rb +33 -9
- data/lib/pindo/module/cert/xcode_cert_helper.rb +17 -7
- data/lib/pindo/module/pgyer/pgyerhelper.rb +110 -22
- data/lib/pindo/module/task/model/git/git_commit_task.rb +106 -6
- data/lib/pindo/module/task/model/git/git_tag_task.rb +17 -7
- data/lib/pindo/module/task/model/jps/jps_upload_media_task.rb +13 -8
- data/lib/pindo/module/task/model/resign/ipa_local_resign_task.rb +5 -0
- data/lib/pindo/options/core/option_item.rb +4 -5
- data/lib/pindo/options/groups/git_options.rb +40 -0
- data/lib/pindo/options/helpers/git_constants.rb +28 -0
- data/lib/pindo/version.rb +1 -1
- metadata +7 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f82f06b4cb4ed845ca62c0db43e4684b141d87b8271b705a2a4de0e0a9c8355b
|
|
4
|
+
data.tar.gz: 7b77b67d53e8f1ca601f32190d8d6534d146837a302facce49c7afa782ecd47f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f051bfa4fb0aa773eaf7d7a20d489e17d065bdede2c78652dd172ec2a3da5f95ae19700d05e6748f3442487fd96e18a7b3040043a4f906e257415353b73a392a
|
|
7
|
+
data.tar.gz: f056935b0928d4e48dd0e6bb88f321d64a2b118eee92ef3ce767697c039868ebc987394e36deacbbcb6ba1111cc8486b93bbc99c71a6f4c71a297bc9a4f3f575
|
data/lib/pindo/base/aeshelper.rb
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
526
|
+
when Pindo::UncommittedFilesProcessType::RESET
|
|
523
527
|
handle_delete_all(current_project_dir, coding_branch)
|
|
524
|
-
when
|
|
528
|
+
when Pindo::UncommittedFilesProcessType::STASH
|
|
525
529
|
handle_stash(current_project_dir, coding_branch)
|
|
526
|
-
when
|
|
530
|
+
when Pindo::UncommittedFilesProcessType::SKIP
|
|
527
531
|
# 跳过处理,不做任何操作
|
|
528
532
|
Funlog.instance.warning("跳过提交,可能导致 build number 与代码仓库不一致")
|
|
529
533
|
return
|
|
530
|
-
when
|
|
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
|
-
|
|
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
|
|
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 [
|
|
865
|
-
# @return [
|
|
866
|
-
def get_uncommitted_files_process_type(project_dir:, interactive: true, default_process_type:
|
|
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
|
|
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
|
|
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) 全部提交") {
|
|
895
|
-
menu.choice("(skip) 跳过并且不作任何修改,继续执行") {
|
|
896
|
-
menu.choice("(reset) 全部丢弃更改,回滚代码") {
|
|
897
|
-
menu.choice("(stash) 保存到stash区域(该部分代码本次不生效)") {
|
|
898
|
-
menu.choice("(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
|
-
#
|
|
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
|
-
#
|
|
54
|
-
if
|
|
55
|
-
puts "
|
|
60
|
+
# 检查响应状态
|
|
61
|
+
if response_body['error'].nil? && response_body['message'].nil?
|
|
62
|
+
puts "[✔] Gitee 仓库创建成功: #{repo_name}"
|
|
56
63
|
return true
|
|
57
64
|
else
|
|
58
|
-
|
|
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]
|
|
134
|
-
@args_tag_type = Pindo::Options::GitOptions.parse_create_tag_type(@options[:tag_type]
|
|
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:
|
|
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:
|
|
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]
|
|
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:
|
|
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]
|
|
148
|
-
@args_tag_type = Pindo::Options::GitOptions.parse_create_tag_type(@options[:tag_type]
|
|
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:
|
|
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
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
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
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
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
|
|