pindo 5.18.20 → 5.19.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.
- checksums.yaml +4 -4
- data/lib/pindo/base/git_handler.rb +38 -60
- data/lib/pindo/command/android/autobuild.rb +8 -4
- data/lib/pindo/command/ios/autobuild.rb +8 -4
- data/lib/pindo/command/repo/create.rb +1 -1
- data/lib/pindo/command/repo/search.rb +1 -1
- data/lib/pindo/command/unity/autobuild.rb +1 -1
- data/lib/pindo/command/utils/installskills.rb +2 -2
- data/lib/pindo/command/utils/renewproj.rb +0 -1
- data/lib/pindo/command/web/autobuild.rb +8 -4
- data/lib/pindo/module/android/android_build_helper.rb +39 -16
- data/lib/pindo/module/android/gradle_helper.rb +8 -14
- data/lib/pindo/module/cert/cert_helper.rb +2 -2
- data/lib/pindo/module/pgyer/pgyerhelper.rb +18 -20
- data/lib/pindo/module/task/model/build/android_build_dev_task.rb +1 -37
- data/lib/pindo/module/task/model/build/android_build_task.rb +40 -0
- data/lib/pindo/module/task/model/build/ios_build_adhoc_task.rb +5 -5
- data/lib/pindo/module/task/model/build/ios_build_appstore_task.rb +5 -5
- data/lib/pindo/module/task/model/build/ios_build_dev_task.rb +2 -2
- data/lib/pindo/module/task/model/build_task.rb +2 -0
- data/lib/pindo/module/task/model/git/git_commit_task.rb +11 -1
- data/lib/pindo/module/task/model/git/git_tag_task.rb +2 -2
- data/lib/pindo/module/task/model/jps/jps_workflow_message_task.rb +8 -10
- data/lib/pindo/module/task/pindo_task.rb +2 -2
- data/lib/pindo/module/task/task_manager.rb +14 -6
- data/lib/pindo/module/unity/nuget_helper.rb +5 -20
- data/lib/pindo/module/utils/git_repo_helper.rb +40 -45
- data/lib/pindo/module/xcode/ipa_resign_helper.rb +33 -62
- data/lib/pindo/options/groups/unity_options.rb +1 -1
- data/lib/pindo/version.rb +1 -1
- metadata +1 -1
|
@@ -710,14 +710,10 @@ module Pindo
|
|
|
710
710
|
FileUtils.rm_rf(ipa_file_upload)
|
|
711
711
|
end
|
|
712
712
|
|
|
713
|
-
current_dir = Dir.pwd
|
|
714
713
|
if File.exist?(ipa_base_dir)
|
|
715
|
-
Dir.chdir(ipa_base_dir)
|
|
716
714
|
base_name = File.basename(mac_app_path)
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
system command
|
|
720
|
-
Dir.chdir(current_dir)
|
|
715
|
+
# 数组参数 + chdir,避免 shell 注入与 cwd 污染
|
|
716
|
+
system("zip", "-qry", ipa_file_upload, base_name, chdir: ipa_base_dir)
|
|
721
717
|
end
|
|
722
718
|
end
|
|
723
719
|
if !ipa_file_upload.nil? &&
|
|
@@ -738,19 +734,18 @@ module Pindo
|
|
|
738
734
|
rescue StandardError => e
|
|
739
735
|
|
|
740
736
|
end
|
|
741
|
-
current_project_dir = Dir.pwd
|
|
742
737
|
web_build_name = web_build_name.downcase.strip.gsub(/[\s\-_]/, '')
|
|
743
738
|
web_res_zip_fullname = File.join(web_zip_dir, web_build_name + "_" + web_build_version + ".zip")
|
|
744
739
|
if File.exist?(web_res_zip_fullname)
|
|
745
740
|
FileUtils.rm_rf(web_res_zip_fullname)
|
|
746
741
|
end
|
|
747
742
|
Zip::File.open(web_res_zip_fullname, Zip::File::CREATE) do |zipfile|
|
|
748
|
-
Dir.chdir
|
|
749
|
-
|
|
750
|
-
|
|
743
|
+
Dir.chdir(web_res_path) do
|
|
744
|
+
Dir.glob("**/*").reject {|fn| File.directory?(fn) }.each do |file|
|
|
745
|
+
zipfile.add(file.sub(web_res_path + '/', ''), file)
|
|
746
|
+
end
|
|
751
747
|
end
|
|
752
748
|
end
|
|
753
|
-
Dir.chdir(current_project_dir)
|
|
754
749
|
ipa_file_upload = web_res_zip_fullname
|
|
755
750
|
end
|
|
756
751
|
if !ipa_file_upload.nil? &&
|
|
@@ -767,12 +762,11 @@ module Pindo
|
|
|
767
762
|
FileUtils.rm_rf(windows_zip_fullname)
|
|
768
763
|
end
|
|
769
764
|
|
|
770
|
-
current_project_dir = Dir.pwd
|
|
771
765
|
Zip::File.open(windows_zip_fullname, Zip::File::CREATE) do |zipfile|
|
|
772
|
-
Dir.chdir
|
|
773
|
-
|
|
766
|
+
Dir.chdir(windows_build_dir) do
|
|
767
|
+
zipfile.add(windows_exe_name, windows_exe_name)
|
|
768
|
+
end
|
|
774
769
|
end
|
|
775
|
-
Dir.chdir(current_project_dir)
|
|
776
770
|
ipa_file_upload = windows_zip_fullname
|
|
777
771
|
end
|
|
778
772
|
unless !ipa_file_upload.nil? && File.exist?(ipa_file_upload)
|
|
@@ -800,14 +794,14 @@ module Pindo
|
|
|
800
794
|
end
|
|
801
795
|
|
|
802
796
|
Zip::File.open(server_zipfile_name, Zip::File::CREATE) do |zipfile|
|
|
803
|
-
Dir.chdir
|
|
804
|
-
|
|
805
|
-
|
|
797
|
+
Dir.chdir(server_file_directory) do
|
|
798
|
+
Dir.glob("**/*").reject {|fn| File.directory?(fn) }.each do |file|
|
|
799
|
+
zipfile.add(file.sub(server_file_directory + '/', ''), file)
|
|
800
|
+
end
|
|
806
801
|
end
|
|
807
802
|
end
|
|
808
803
|
|
|
809
804
|
addtach_file = server_zipfile_name
|
|
810
|
-
Dir.chdir current_project_dir
|
|
811
805
|
end
|
|
812
806
|
|
|
813
807
|
puts
|
|
@@ -1539,6 +1533,7 @@ module Pindo
|
|
|
1539
1533
|
if git_cliff_installed
|
|
1540
1534
|
temp_dir = Dir.pwd
|
|
1541
1535
|
Dir.chdir(current_git_root_path)
|
|
1536
|
+
begin
|
|
1542
1537
|
|
|
1543
1538
|
# 检查 HEAD 是否有 tag
|
|
1544
1539
|
head_tag = `git describe --exact-match --tags HEAD 2>/dev/null`.strip
|
|
@@ -1605,7 +1600,10 @@ module Pindo
|
|
|
1605
1600
|
description = "版本更新"
|
|
1606
1601
|
end
|
|
1607
1602
|
|
|
1608
|
-
|
|
1603
|
+
ensure
|
|
1604
|
+
# ensure 保证异常路径也还原 cwd,避免污染全局工作目录
|
|
1605
|
+
Dir.chdir(temp_dir)
|
|
1606
|
+
end
|
|
1609
1607
|
else
|
|
1610
1608
|
# git-cliff 未安装,提示用户并退出
|
|
1611
1609
|
puts "\n\e[31m错误: git-cliff 未安装\e[0m"
|
|
@@ -58,43 +58,7 @@ module Pindo
|
|
|
58
58
|
build_android_project
|
|
59
59
|
end
|
|
60
60
|
|
|
61
|
-
|
|
62
|
-
# 搜索 APK 和 AAB 文件
|
|
63
|
-
search_paths = []
|
|
64
|
-
search_paths.concat(TaskConfig::BUILD_OUTPUT_PATTERNS[:apk].map { |p| File.join(@project_path, p) })
|
|
65
|
-
search_paths.concat(TaskConfig::BUILD_OUTPUT_PATTERNS[:aab].map { |p| File.join(@project_path, p) })
|
|
66
|
-
|
|
67
|
-
package_files = []
|
|
68
|
-
search_paths.each do |pattern|
|
|
69
|
-
package_files.concat(Dir.glob(pattern))
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
# 过滤测试包和未签名包
|
|
73
|
-
package_files.reject! do |f|
|
|
74
|
-
basename = File.basename(f).downcase
|
|
75
|
-
TaskConfig::EXCLUDED_PATTERNS.any? { |pattern| basename.include?(pattern) }
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
if package_files.any?
|
|
79
|
-
# 优先返回 AAB,其次 APK
|
|
80
|
-
aab_files = package_files.select { |f| f.end_with?(".aab") }
|
|
81
|
-
apk_files = package_files.select { |f| f.end_with?(".apk") }
|
|
82
|
-
|
|
83
|
-
latest_package = if aab_files.any?
|
|
84
|
-
aab_files.max_by { |f| File.mtime(f) }
|
|
85
|
-
elsif apk_files.any?
|
|
86
|
-
apk_files.max_by { |f| File.mtime(f) }
|
|
87
|
-
else
|
|
88
|
-
package_files.max_by { |f| File.mtime(f) }
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
puts " 找到 Android 包文件: #{latest_package}"
|
|
92
|
-
latest_package
|
|
93
|
-
else
|
|
94
|
-
puts " 警告: 未找到 APK/AAB 文件"
|
|
95
|
-
nil
|
|
96
|
-
end
|
|
97
|
-
end
|
|
61
|
+
# find_output 已上移到 AndroidBuildTask 基类(dev/adhoc/gplay 共用)
|
|
98
62
|
|
|
99
63
|
private
|
|
100
64
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require_relative '../build_task'
|
|
2
|
+
require_relative '../../task_config'
|
|
2
3
|
|
|
3
4
|
module Pindo
|
|
4
5
|
module TaskSystem
|
|
@@ -12,6 +13,45 @@ module Pindo
|
|
|
12
13
|
def required_resources
|
|
13
14
|
(super || []) + [{ type: :gradle, directory: @project_path }]
|
|
14
15
|
end
|
|
16
|
+
|
|
17
|
+
# 查找构建产物(APK/AAB),三个子类(dev/adhoc/gplay)共用同一套 glob 逻辑
|
|
18
|
+
# 搜索路径与过滤规则与 pindo jps upload 查找 Android 包保持一致;优先 AAB,其次 APK
|
|
19
|
+
def find_output
|
|
20
|
+
search_paths = []
|
|
21
|
+
search_paths.concat(TaskConfig::BUILD_OUTPUT_PATTERNS[:apk].map { |p| File.join(@project_path, p) })
|
|
22
|
+
search_paths.concat(TaskConfig::BUILD_OUTPUT_PATTERNS[:aab].map { |p| File.join(@project_path, p) })
|
|
23
|
+
|
|
24
|
+
package_files = []
|
|
25
|
+
search_paths.each do |pattern|
|
|
26
|
+
package_files.concat(Dir.glob(pattern))
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# 过滤测试包和未签名包
|
|
30
|
+
package_files.reject! do |f|
|
|
31
|
+
basename = File.basename(f).downcase
|
|
32
|
+
TaskConfig::EXCLUDED_PATTERNS.any? { |pattern| basename.include?(pattern) }
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
if package_files.any?
|
|
36
|
+
# 优先返回 AAB,其次 APK
|
|
37
|
+
aab_files = package_files.select { |f| f.end_with?(".aab") }
|
|
38
|
+
apk_files = package_files.select { |f| f.end_with?(".apk") }
|
|
39
|
+
|
|
40
|
+
latest_package = if aab_files.any?
|
|
41
|
+
aab_files.max_by { |f| File.mtime(f) }
|
|
42
|
+
elsif apk_files.any?
|
|
43
|
+
apk_files.max_by { |f| File.mtime(f) }
|
|
44
|
+
else
|
|
45
|
+
package_files.max_by { |f| File.mtime(f) }
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
puts " 找到 Android 包文件: #{latest_package}"
|
|
49
|
+
latest_package
|
|
50
|
+
else
|
|
51
|
+
# 构建已执行但产物缺失,视为失败,不静默返回 nil
|
|
52
|
+
raise Informative, "构建完成但未找到 APK/AAB 文件"
|
|
53
|
+
end
|
|
54
|
+
end
|
|
15
55
|
end
|
|
16
56
|
end
|
|
17
57
|
end
|
|
@@ -41,7 +41,7 @@ module Pindo
|
|
|
41
41
|
|
|
42
42
|
#准备构建
|
|
43
43
|
def prepare_build
|
|
44
|
-
Dir.chdir(@project_path)
|
|
44
|
+
# Dir.chdir(@project_path) # Removed for thread safety(下游均传入绝对路径)
|
|
45
45
|
|
|
46
46
|
# 0. 清理 Firebase Shell Script
|
|
47
47
|
cleanup_firebase_shell
|
|
@@ -98,8 +98,8 @@ module Pindo
|
|
|
98
98
|
puts " 找到 IPA 文件: #{latest_ipa}"
|
|
99
99
|
latest_ipa
|
|
100
100
|
else
|
|
101
|
-
|
|
102
|
-
|
|
101
|
+
# 构建已执行但产物缺失,视为失败,不静默返回 nil
|
|
102
|
+
raise Informative, "构建完成但未找到 IPA 文件 (#{build_path})"
|
|
103
103
|
end
|
|
104
104
|
end
|
|
105
105
|
|
|
@@ -289,7 +289,7 @@ module Pindo
|
|
|
289
289
|
puts " 处理 Quark/Swark..."
|
|
290
290
|
|
|
291
291
|
require 'pindo/module/xcode/xcode_swark_helper'
|
|
292
|
-
Dir.chdir(@project_path)
|
|
292
|
+
# Dir.chdir(@project_path) # Removed for thread safety(下游均传入绝对路径)
|
|
293
293
|
|
|
294
294
|
if xcode_build_type.include?("quark")
|
|
295
295
|
Pindo::XcodeSwarkHelper.quark_run(
|
|
@@ -377,7 +377,7 @@ module Pindo
|
|
|
377
377
|
|
|
378
378
|
# 编译 iOS 工程
|
|
379
379
|
def build_ios_project
|
|
380
|
-
Dir.chdir(@project_path)
|
|
380
|
+
# Dir.chdir(@project_path) # Removed for thread safety(下游均传入绝对路径)
|
|
381
381
|
|
|
382
382
|
# 使用 XcodeBuildHelper 进行构建
|
|
383
383
|
ipa_file = Pindo::XcodeBuildHelper.build_project(
|
|
@@ -40,7 +40,7 @@ module Pindo
|
|
|
40
40
|
|
|
41
41
|
#准备构建
|
|
42
42
|
def prepare_build
|
|
43
|
-
Dir.chdir(@project_path)
|
|
43
|
+
# Dir.chdir(@project_path) # Removed for thread safety(下游均传入绝对路径)
|
|
44
44
|
|
|
45
45
|
# 0. 清理 Firebase Shell Script
|
|
46
46
|
cleanup_firebase_shell
|
|
@@ -81,8 +81,8 @@ module Pindo
|
|
|
81
81
|
puts " 找到 IPA 文件: #{latest_ipa}"
|
|
82
82
|
latest_ipa
|
|
83
83
|
else
|
|
84
|
-
|
|
85
|
-
|
|
84
|
+
# 构建已执行但产物缺失,视为失败,不静默返回 nil
|
|
85
|
+
raise Informative, "构建完成但未找到 IPA 文件 (#{build_path})"
|
|
86
86
|
end
|
|
87
87
|
end
|
|
88
88
|
|
|
@@ -262,7 +262,7 @@ module Pindo
|
|
|
262
262
|
puts " 处理 Quark/Swark..."
|
|
263
263
|
|
|
264
264
|
require 'pindo/module/xcode/xcode_swark_helper'
|
|
265
|
-
Dir.chdir(@project_path)
|
|
265
|
+
# Dir.chdir(@project_path) # Removed for thread safety(下游均传入绝对路径)
|
|
266
266
|
|
|
267
267
|
if xcode_build_type.include?("quark")
|
|
268
268
|
Pindo::XcodeSwarkHelper.quark_run(
|
|
@@ -350,7 +350,7 @@ module Pindo
|
|
|
350
350
|
|
|
351
351
|
# 编译 iOS 工程
|
|
352
352
|
def build_ios_project
|
|
353
|
-
Dir.chdir(@project_path)
|
|
353
|
+
# Dir.chdir(@project_path) # Removed for thread safety(下游均传入绝对路径)
|
|
354
354
|
|
|
355
355
|
# 使用 XcodeBuildHelper 进行构建
|
|
356
356
|
ipa_file = Pindo::XcodeBuildHelper.build_project(
|
|
@@ -13,7 +13,7 @@ module Pindo
|
|
|
13
13
|
:git_commit
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
-
# 重试配置:失败后重试
|
|
16
|
+
# 重试配置:失败后重试 2 次(共 3 次尝试)
|
|
17
17
|
def self.default_retry_count
|
|
18
18
|
2
|
|
19
19
|
end
|
|
@@ -69,6 +69,16 @@ module Pindo
|
|
|
69
69
|
root_dir = @git_root_path
|
|
70
70
|
git_repo_helper = Pindo::GitRepoHelper.share_instance
|
|
71
71
|
|
|
72
|
+
# 0. 先拉取远程 tag,确保本地 tag 列表是最新的
|
|
73
|
+
# 否则本地 tag 落后于远程时(如远程被 CI/其他机器打了更高 tag),
|
|
74
|
+
# 后续基于本地 tag 计算的版本会偏低,导致本该新建的版本 tag 与远程已有 tag 冲突而被漏打。
|
|
75
|
+
# 尽力而为:网络不可达时仅告警,用本地 tag 降级计算,不阻断构建。
|
|
76
|
+
begin
|
|
77
|
+
Pindo::GitHandler.git_remote!(root_dir, %W(-C #{root_dir} fetch --tags --force origin))
|
|
78
|
+
rescue => e
|
|
79
|
+
Funlog.instance.fancyinfo_warning("拉取远程 tag 失败,将使用本地 tag 计算版本: #{e.message.lines.first&.strip}")
|
|
80
|
+
end
|
|
81
|
+
|
|
72
82
|
# 1. 如果 fixed_version 为 nil,尝试从 HEAD 的 tag 获取版本
|
|
73
83
|
# 必须在 check_gitignore 之前检查,因为 check_gitignore 可能创建提交导致 HEAD 移动
|
|
74
84
|
if (@fixed_version.nil? || @fixed_version.empty?) &&
|
|
@@ -12,7 +12,7 @@ module Pindo
|
|
|
12
12
|
:git_tag
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
-
# 重试配置:失败后重试
|
|
15
|
+
# 重试配置:失败后重试 2 次(共 3 次尝试)
|
|
16
16
|
def self.default_retry_count
|
|
17
17
|
2
|
|
18
18
|
end
|
|
@@ -39,7 +39,7 @@ module Pindo
|
|
|
39
39
|
protected
|
|
40
40
|
|
|
41
41
|
def do_work
|
|
42
|
-
# 检查是否为 Git
|
|
42
|
+
# 检查是否为 Git 仓库,如果不是则直接成功返回(非 Git 仓库目录下跳过打 tag)
|
|
43
43
|
unless valid_git_repository?
|
|
44
44
|
Funlog.instance.fancyinfo_warning("当前目录不是Git仓库,跳过Git打Tag")
|
|
45
45
|
return {
|
|
@@ -15,6 +15,11 @@ module Pindo
|
|
|
15
15
|
:jps_workflow_message
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
+
# 工作流消息失败重试 1 次(覆盖 JPSTask 默认的重试 3 次)
|
|
19
|
+
def self.default_retry_count
|
|
20
|
+
1
|
|
21
|
+
end
|
|
22
|
+
|
|
18
23
|
# 初始化工作流消息发送任务
|
|
19
24
|
# @param options [Hash] 选项
|
|
20
25
|
# @option options [String] :git_commit_id Git commit SHA(可选,从依赖任务获取)
|
|
@@ -70,17 +75,10 @@ module Pindo
|
|
|
70
75
|
workflow_id: @workflow_id
|
|
71
76
|
}
|
|
72
77
|
rescue => e
|
|
73
|
-
#
|
|
74
|
-
puts "
|
|
78
|
+
# 工作流消息是终端任务,失败需明确报错中断(不再伪造 success)
|
|
79
|
+
puts " ❌ JPS 工作流消息发送失败: #{e.message}"
|
|
75
80
|
puts " ⚠️ 错误堆栈: #{e.backtrace.first(3).join("\n ")}" if ENV['PINDO_DEBUG'] && e.backtrace
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
# 消息发送失败不抛出异常,返回成功但带警告
|
|
79
|
-
{
|
|
80
|
-
success: true,
|
|
81
|
-
warning: e.message,
|
|
82
|
-
git_commit_id: @git_commit_id
|
|
83
|
-
}
|
|
81
|
+
raise Informative, "JPS 工作流消息发送失败: #{e.message}"
|
|
84
82
|
end
|
|
85
83
|
end
|
|
86
84
|
|
|
@@ -118,12 +118,19 @@ module Pindo
|
|
|
118
118
|
# 输出执行摘要
|
|
119
119
|
@reporter.print_execution_summary
|
|
120
120
|
|
|
121
|
-
#
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
121
|
+
# 失败闸门:FAILED / CANCELLED / 残留 PENDING(依赖阻塞等导致从未执行)都视为
|
|
122
|
+
# 「未成功完成」,统一抛异常确保进程非零退出码,避免任务没跑成却静默成功
|
|
123
|
+
completed = @queue.completed_snapshot
|
|
124
|
+
failed_tasks = completed.select { |t| t.status == TaskStatus::FAILED }
|
|
125
|
+
cancelled_tasks = completed.select { |t| t.status == TaskStatus::CANCELLED }
|
|
126
|
+
unfinished_tasks = @queue.pending_snapshot
|
|
127
|
+
|
|
128
|
+
if failed_tasks.any? || cancelled_tasks.any? || unfinished_tasks.any?
|
|
129
|
+
parts = []
|
|
130
|
+
parts << "失败: #{failed_tasks.map(&:name).join(', ')}" if failed_tasks.any?
|
|
131
|
+
parts << "取消: #{cancelled_tasks.map(&:name).join(', ')}" if cancelled_tasks.any?
|
|
132
|
+
parts << "未执行: #{unfinished_tasks.map(&:name).join(', ')}" if unfinished_tasks.any?
|
|
133
|
+
raise Informative, "存在未成功完成的任务 —— #{parts.join(';')}"
|
|
127
134
|
end
|
|
128
135
|
end
|
|
129
136
|
|
|
@@ -138,6 +145,7 @@ module Pindo
|
|
|
138
145
|
completed: completed.count,
|
|
139
146
|
success: completed.count { |t| t.status == TaskStatus::SUCCESS },
|
|
140
147
|
failed: completed.count { |t| t.status == TaskStatus::FAILED },
|
|
148
|
+
cancelled: completed.count { |t| t.status == TaskStatus::CANCELLED },
|
|
141
149
|
tasks: (pending + completed).map do |task|
|
|
142
150
|
{
|
|
143
151
|
id: task.id,
|
|
@@ -505,9 +505,8 @@ module Pindo
|
|
|
505
505
|
puts " 使用工具: nuget pack"
|
|
506
506
|
puts
|
|
507
507
|
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
success = system(cmd)
|
|
508
|
+
# 数组参数 + chdir,避免路径中的 shell 元字符注入
|
|
509
|
+
success = system("nuget", "pack", File.basename(nuspec_file), "-OutputDirectory", output_dir, chdir: package_dir)
|
|
511
510
|
|
|
512
511
|
unless success
|
|
513
512
|
raise Informative, "NuGet 打包失败,请检查 .nuspec 文件格式"
|
|
@@ -573,10 +572,8 @@ module Pindo
|
|
|
573
572
|
begin
|
|
574
573
|
# 查找是否存在与 nuspec 版本匹配的 tag(支持 v1.1.3, V1.1.3, 1.1.3 等格式)
|
|
575
574
|
matching_tag = nil
|
|
576
|
-
temp_dir = Dir.pwd
|
|
577
|
-
Dir.chdir(git_root)
|
|
578
575
|
|
|
579
|
-
# 获取所有 tags
|
|
576
|
+
# 获取所有 tags(命令已带 -C,无需 chdir)
|
|
580
577
|
all_tags = Pindo::GitHandler.git!(%W(-C #{git_root} tag -l)).split("\n")
|
|
581
578
|
|
|
582
579
|
# 查找匹配当前版本的 tag(不区分大小写,支持 v 前缀)
|
|
@@ -585,17 +582,12 @@ module Pindo
|
|
|
585
582
|
tag_version.downcase == nuspec_version.downcase
|
|
586
583
|
end
|
|
587
584
|
|
|
588
|
-
Dir.chdir(temp_dir)
|
|
589
|
-
|
|
590
585
|
# 确定获取哪个范围的 commits
|
|
591
586
|
git_range = if matching_tag
|
|
592
587
|
# 找到匹配的 tag,获取前一个 tag 到这个 tag 的 commits
|
|
593
588
|
puts "📝 找到版本 tag: #{matching_tag},生成该版本的 release notes..."
|
|
594
589
|
|
|
595
|
-
|
|
596
|
-
Dir.chdir(git_root)
|
|
597
|
-
|
|
598
|
-
# 获取所有 tags 并按版本号排序
|
|
590
|
+
# 获取所有 tags 并按版本号排序(命令已带 -C,无需 chdir)
|
|
599
591
|
all_tags = Pindo::GitHandler.git!(%W(-C #{git_root} tag -l)).split("\n")
|
|
600
592
|
sorted_tags = all_tags.sort_by do |tag|
|
|
601
593
|
version_str = tag.gsub(/^(v|V|release[\s_-]*)/i, '')
|
|
@@ -609,8 +601,6 @@ module Pindo
|
|
|
609
601
|
prev_tag = sorted_tags[tag_index - 1]
|
|
610
602
|
end
|
|
611
603
|
|
|
612
|
-
Dir.chdir(temp_dir)
|
|
613
|
-
|
|
614
604
|
if prev_tag
|
|
615
605
|
"#{prev_tag}..#{matching_tag}"
|
|
616
606
|
else
|
|
@@ -635,10 +625,7 @@ module Pindo
|
|
|
635
625
|
end
|
|
636
626
|
end
|
|
637
627
|
|
|
638
|
-
# 获取 commit messages(包含完整的 body)
|
|
639
|
-
temp_dir = Dir.pwd
|
|
640
|
-
Dir.chdir(git_root)
|
|
641
|
-
|
|
628
|
+
# 获取 commit messages(包含完整的 body;命令已带 -C,无需 chdir)
|
|
642
629
|
# 使用特殊分隔符来区分不同的 commits
|
|
643
630
|
separator = "---COMMIT-SEPARATOR---"
|
|
644
631
|
commits_raw = Pindo::GitHandler.git!(%W(-C #{git_root} log #{git_range} --pretty=format:%B#{separator}))
|
|
@@ -646,8 +633,6 @@ module Pindo
|
|
|
646
633
|
# 按分隔符拆分成单个 commits
|
|
647
634
|
commits = commits_raw.split(separator).map(&:strip).reject(&:empty?)
|
|
648
635
|
|
|
649
|
-
Dir.chdir(temp_dir)
|
|
650
|
-
|
|
651
636
|
# 过滤只保留符合规范的 commits(支持带 scope 和不带 scope 格式)
|
|
652
637
|
# 匹配格式: feat:, feat(scope):, fix:, fix(api): 等
|
|
653
638
|
conventional_commit_regex = /^(feat|fix|docs|doc|perf|refactor|style|test|chore|revert)(\([^)]+\))?:/i
|
|
@@ -330,53 +330,34 @@ module Pindo
|
|
|
330
330
|
raise ArgumentError, "项目目录不能为空" if project_dir.nil?
|
|
331
331
|
raise ArgumentError, "tag名称不能为空" if tag_name.nil? || tag_name.empty?
|
|
332
332
|
|
|
333
|
+
local_exists = Pindo::GitHandler.local_tag_exists?(local_repo_dir: project_dir, tag_name: tag_name)
|
|
334
|
+
|
|
335
|
+
# 建本地 tag 是纯本地操作,不依赖远程可达,网络故障也能留下本地 tag;
|
|
336
|
+
# 删远程残留与推送一律交给 push_tag_with_verify,推送失败时本地 tag 仍保留,可事后手动补推。
|
|
333
337
|
case tag_type
|
|
334
338
|
when Pindo::CreateTagType::NONE
|
|
339
|
+
# 不创建 tag
|
|
335
340
|
Funlog.instance.fancyinfo_success("跳过创建 Tag")
|
|
336
|
-
|
|
337
|
-
when Pindo::CreateTagType::RECREATE
|
|
338
|
-
#
|
|
339
|
-
if Pindo::GitHandler.remote_tag_exists?(local_repo_dir: project_dir, tag_name: tag_name)
|
|
340
|
-
Funlog.instance.fancyinfo_update("删除远程 Tag: #{tag_name}")
|
|
341
|
-
Pindo::GitHandler.git_remote!(project_dir, %W(-C #{project_dir} push origin :refs/tags/#{tag_name}))
|
|
342
|
-
end
|
|
343
|
-
if Pindo::GitHandler.local_tag_exists?(local_repo_dir: project_dir, tag_name: tag_name)
|
|
344
|
-
Funlog.instance.fancyinfo_update("删除本地 Tag: #{tag_name}")
|
|
345
|
-
Pindo::GitHandler.git!(%W(-C #{project_dir} tag -d #{tag_name}))
|
|
346
|
-
end
|
|
347
|
-
when Pindo::CreateTagType::NEW
|
|
348
|
-
local_exists = Pindo::GitHandler.local_tag_exists?(local_repo_dir: project_dir, tag_name: tag_name)
|
|
349
|
-
remote_exists = Pindo::GitHandler.remote_tag_exists?(local_repo_dir: project_dir, tag_name: tag_name)
|
|
350
|
-
|
|
351
|
-
# 如果 tag 在 HEAD 上,不需要重新创建
|
|
341
|
+
nil
|
|
342
|
+
when Pindo::CreateTagType::NEW, Pindo::CreateTagType::RECREATE
|
|
343
|
+
# 本地 tag 已在 HEAD:无需重建,仅确保推送到远程(RECREATE 在 HEAD 原地重建也是空转,同样适用)
|
|
352
344
|
if local_exists && Pindo::GitHandler.is_tag_at_head?(git_root_dir: project_dir, tag_name: tag_name)
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
Funlog.instance.
|
|
345
|
+
push_tag_with_verify(project_dir: project_dir, tag_name: tag_name)
|
|
346
|
+
tag_name
|
|
347
|
+
else
|
|
348
|
+
# 本地 tag 不存在或不在 HEAD:删旧重建后推送
|
|
349
|
+
if local_exists
|
|
350
|
+
Funlog.instance.fancyinfo_update("删除本地旧 Tag: #{tag_name}")
|
|
351
|
+
Pindo::GitHandler.git!(%W(-C #{project_dir} tag -d #{tag_name}))
|
|
359
352
|
end
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
if remote_exists
|
|
365
|
-
Funlog.instance.fancyinfo_update("删除远程 Tag: #{tag_name}")
|
|
366
|
-
Pindo::GitHandler.git_remote!(project_dir, %W(-C #{project_dir} push origin :refs/tags/#{tag_name}))
|
|
367
|
-
end
|
|
368
|
-
if local_exists
|
|
369
|
-
Funlog.instance.fancyinfo_update("删除本地 Tag: #{tag_name}")
|
|
370
|
-
Pindo::GitHandler.git!(%W(-C #{project_dir} tag -d #{tag_name}))
|
|
353
|
+
Pindo::GitHandler.git!(%W(-C #{project_dir} tag #{tag_name}))
|
|
354
|
+
Funlog.instance.fancyinfo_success("已创建本地 Tag: #{tag_name}")
|
|
355
|
+
push_tag_with_verify(project_dir: project_dir, tag_name: tag_name)
|
|
356
|
+
tag_name
|
|
371
357
|
end
|
|
372
|
-
|
|
358
|
+
else
|
|
359
|
+
raise ArgumentError, "未知的 tag_type: #{tag_type}"
|
|
373
360
|
end
|
|
374
|
-
|
|
375
|
-
# 创建并推送 tag
|
|
376
|
-
Pindo::GitHandler.git!(%W(-C #{project_dir} tag #{tag_name}))
|
|
377
|
-
push_tag_with_verify(project_dir: project_dir, tag_name: tag_name)
|
|
378
|
-
Funlog.instance.fancyinfo_success("创建并推送 Tag: #{tag_name}")
|
|
379
|
-
tag_name
|
|
380
361
|
end
|
|
381
362
|
|
|
382
363
|
# 推送 tag 到远程并校验是否真正推送成功,失败则重试
|
|
@@ -393,23 +374,37 @@ module Pindo
|
|
|
393
374
|
loop do
|
|
394
375
|
attempt += 1
|
|
395
376
|
begin
|
|
377
|
+
# 远程若残留指向不同 commit 的同名 tag,先删除再推送,避免 non-fast-forward 被拒
|
|
378
|
+
remote_commit = Pindo::GitHandler.remote_tag_commit(local_repo_dir: project_dir, tag_name: tag_name)
|
|
379
|
+
if remote_commit == local_commit
|
|
380
|
+
Funlog.instance.fancyinfo_success("已确认 Tag 推送到远程: #{tag_name}")
|
|
381
|
+
return true
|
|
382
|
+
elsif remote_commit
|
|
383
|
+
Funlog.instance.fancyinfo_update("删除远程残留 Tag(远程 #{remote_commit[0..7]} ≠ 本地 #{local_commit[0..7]}): #{tag_name}")
|
|
384
|
+
Pindo::GitHandler.git_remote!(project_dir, %W(-C #{project_dir} push origin :refs/tags/#{tag_name}))
|
|
385
|
+
end
|
|
386
|
+
|
|
396
387
|
Pindo::GitHandler.git_remote!(project_dir, %W(-C #{project_dir} push origin #{tag_name}))
|
|
397
388
|
rescue => e
|
|
398
389
|
first_line = e.message.lines.first&.strip
|
|
399
390
|
Funlog.instance.fancyinfo_warning("推送 Tag 失败(第 #{attempt}/#{max_retries} 次): #{first_line}")
|
|
400
391
|
end
|
|
401
392
|
|
|
402
|
-
# 校验远程 tag 指向的 commit
|
|
403
|
-
remote_commit =
|
|
393
|
+
# 校验远程 tag 指向的 commit 是否与本地一致,确认推送成功(探测失败按未确认处理,不中断重试)
|
|
394
|
+
remote_commit = begin
|
|
395
|
+
Pindo::GitHandler.remote_tag_commit(local_repo_dir: project_dir, tag_name: tag_name)
|
|
396
|
+
rescue
|
|
397
|
+
nil
|
|
398
|
+
end
|
|
404
399
|
if remote_commit && remote_commit == local_commit
|
|
405
400
|
Funlog.instance.fancyinfo_success("已确认 Tag 推送到远程: #{tag_name}")
|
|
406
401
|
return true
|
|
407
|
-
elsif remote_commit
|
|
408
|
-
Funlog.instance.fancyinfo_warning("远程 Tag 指向不同 commit(远程 #{remote_commit[0..7]} ≠ 本地 #{local_commit[0..7]}),疑似残留旧 Tag: #{tag_name}")
|
|
409
402
|
end
|
|
410
403
|
|
|
411
404
|
if attempt >= max_retries
|
|
412
|
-
|
|
405
|
+
# 本地 tag 已创建,仅远程未确认 —— 保留本地 tag,提示可手动补推
|
|
406
|
+
Funlog.instance.fancyinfo_warning("本地 Tag 已创建并保留,可稍后手动执行: git push origin #{tag_name}")
|
|
407
|
+
raise Informative, "Tag 推送失败,已重试 #{max_retries} 次仍未在远程确认: #{tag_name}(本地 Tag 已保留,可手动补推)"
|
|
413
408
|
end
|
|
414
409
|
|
|
415
410
|
Funlog.instance.fancyinfo_update("远程未确认 Tag,#{attempt}s 后重试推送: #{tag_name}")
|