NewLife/Stardust

fix(DeployWorker): 增强Git克隆和拉取的错误处理机制
何炳宏 authored at 2026-04-17 16:19:44 何炳宏 committed at 2026-04-17 16:19:50
e914e19
Tree
1 Parent(s) a9e3f24
Summary: 1 changed files with 140 additions and 36 deletions.
Modified +140 -36
Modified +140 -36
diff --git a/DeployAgent/DeployWorker.cs b/DeployAgent/DeployWorker.cs
index 398657f..f6d45d4 100644
--- a/DeployAgent/DeployWorker.cs
+++ b/DeployAgent/DeployWorker.cs
@@ -1,5 +1,6 @@
 using System.Diagnostics;
 using System.IO.Compression;
+using System.Text;
 using NewLife;
 using NewLife.Log;
 using NewLife.Model;
@@ -88,14 +89,32 @@ public class DeployWorker(StarFactory factory) : IHostedService
                 if (!Directory.Exists(cmd.SourcePath))
                 {
                     Directory.CreateDirectory(cmd.SourcePath);
-                    GitClone(cmd.Repository, cmd.Branch ?? "main", repoDir);
-                    XTrace.WriteLine("代码拉取完成:{0}", repoDir);
+                    try
+                    {
+                        GitClone(cmd.Repository, cmd.Branch ?? "main", repoDir);
+                        XTrace.WriteLine("代码拉取完成:{0}", repoDir);
+                    }
+                    catch (Exception ex)
+                    {
+                        XTrace.WriteLine("Git 克隆失败:{0}", ex.Message);
+                        XTrace.WriteException(ex);
+                        TryRecoverAndReclone(cmd.Repository, cmd.Branch ?? "main", repoDir);
+                    }
                 }
                 // 拉取最新代码
                 else if (cmd.PullCode)
                 {
-                    GitPull(repoDir, cmd.Branch);
-                    XTrace.WriteLine("代码拉取完成");
+                    try
+                    {
+                        GitPull(repoDir, cmd.Branch);
+                        XTrace.WriteLine("代码拉取完成");
+                    }
+                    catch (Exception ex)
+                    {
+                        XTrace.WriteLine("Git 拉取失败:{0}", ex.Message);
+                        XTrace.WriteException(ex);
+                        TryRecoverAndReclone(cmd.Repository, cmd.Branch ?? "main", repoDir);
+                    }
                 }
             }
             else if (cmd.PullCode && !cmd.Repository.IsNullOrEmpty())
@@ -106,8 +125,17 @@ public class DeployWorker(StarFactory factory) : IHostedService
                 XTrace.WriteLine("工作目录:{0}", workDir);
 
                 repoDir = Path.Combine(workDir, "repo");
-                GitClone(cmd.Repository, cmd.Branch ?? "main", repoDir);
-                XTrace.WriteLine("代码拉取完成:{0}", repoDir);
+                try
+                {
+                    GitClone(cmd.Repository, cmd.Branch ?? "main", repoDir);
+                    XTrace.WriteLine("代码拉取完成:{0}", repoDir);
+                }
+                catch (Exception ex)
+                {
+                    XTrace.WriteLine("Git 克隆失败:{0}", ex.Message);
+                    XTrace.WriteException(ex);
+                    TryRecoverAndReclone(cmd.Repository, cmd.Branch ?? "main", repoDir);
+                }
             }
             else
             {
@@ -257,18 +285,44 @@ public class DeployWorker(StarFactory factory) : IHostedService
     {
         var psi = CreateProcessStartInfo(fileName, arguments, workingDirectory);
 
-        using var p = System.Diagnostics.Process.Start(psi);
-        if (p == null) throw new Exception($"无法启动进程:{fileName}");
+        using var p = new Process { StartInfo = psi, EnableRaisingEvents = true };
+
+        var outputSb = new StringBuilder();
+        var errorSb = new StringBuilder();
 
-        var output = p.StandardOutput.ReadToEnd();
-        var error = p.StandardError.ReadToEnd();
-        p.WaitForExit(600_000);
+        p.OutputDataReceived += (s, e) =>
+        {
+            if (e.Data != null)
+            {
+                XTrace.WriteLine(e.Data);
+                outputSb.AppendLine(e.Data);
+            }
+        };
+        p.ErrorDataReceived += (s, e) =>
+        {
+            if (e.Data != null)
+            {
+                XTrace.WriteLine(e.Data);
+                errorSb.AppendLine(e.Data);
+            }
+        };
 
-        if (!output.IsNullOrEmpty()) XTrace.WriteLine(output);
-        if (!error.IsNullOrEmpty()) XTrace.WriteLine(error);
+        if (!p.Start()) throw new Exception($"无法启动进程:{fileName}");
+
+        p.BeginOutputReadLine();
+        p.BeginErrorReadLine();
+
+        var timeout = 600_000;
+        if (!p.WaitForExit(timeout))
+        {
+            try { p.Kill(); } catch { }
+            throw new Exception($"{fileName} 执行超时,已终止({timeout} ms)");
+        }
+
+        p.WaitForExit();
 
         if (p.ExitCode != 0)
-            throw new Exception($"{fileName} 执行失败,退出码:{p.ExitCode}\n{error}");
+            throw new Exception($"{fileName} 执行失败,退出码:{p.ExitCode}\n{errorSb}");
     }
 
     /// <summary>Git 克隆仓库</summary>
@@ -276,16 +330,8 @@ public class DeployWorker(StarFactory factory) : IHostedService
     {
         XTrace.WriteLine("开始克隆仓库:{0} 分支:{1}", repoUrl, branch);
 
-        var psi = CreateProcessStartInfo("git", $"clone -b {branch} --depth 1 {repoUrl} \"{targetPath}\"");
-
-        using var p = System.Diagnostics.Process.Start(psi);
-        if (p == null) throw new Exception("无法启动 git 进程");
-
-        var error = p.StandardError.ReadToEnd();
-        p.WaitForExit(300_000);
-
-        if (p.ExitCode != 0)
-            throw new Exception($"Git 克隆失败:{error}");
+        var args = $"clone -b {branch} --depth 1 {repoUrl} \"{targetPath}\"";
+        ExecuteProcess("git", args, null);
 
         XTrace.WriteLine("Git 克隆成功");
     }
@@ -300,20 +346,10 @@ public class DeployWorker(StarFactory factory) : IHostedService
         // 如果指定了分支则先切换
         if (!branch.IsNullOrEmpty())
         {
-            var psiCheckout = CreateProcessStartInfo("git", $"checkout {branch}", repoDir);
-            using var pCheckout = System.Diagnostics.Process.Start(psiCheckout);
-            pCheckout?.WaitForExit(60_000);
+            ExecuteProcess("git", $"checkout {branch}", repoDir);
         }
 
-        var psi = CreateProcessStartInfo("git", "pull", repoDir);
-        using var p = System.Diagnostics.Process.Start(psi);
-        if (p == null) throw new Exception("无法启动 git 进程");
-
-        var error = p.StandardError.ReadToEnd();
-        p.WaitForExit(300_000);
-
-        if (p.ExitCode != 0)
-            throw new Exception($"Git 拉取失败:{error}");
+        ExecuteProcess("git", "pull", repoDir);
 
         XTrace.WriteLine("Git 拉取成功");
     }
@@ -442,6 +478,74 @@ public class DeployWorker(StarFactory factory) : IHostedService
         }
     }
 
+    /// <summary>尝试备份现有仓库并重新克隆(失败则尝试还原备份)</summary>
+    private void TryRecoverAndReclone(String repoUrl, String branch, String repoDir)
+    {
+        XTrace.WriteLine("尝试备份并重新拉取仓库:{0}", repoDir);
+
+        var backupDir = "";
+        try
+        {
+            if (Directory.Exists(repoDir))
+            {
+                backupDir = repoDir + ".backup." + DateTime.Now.ToString("yyyyMMddHHmmss");
+                Directory.Move(repoDir, backupDir);
+                XTrace.WriteLine("已备份原仓库到:{0}", backupDir);
+            }
+        }
+        catch (Exception ex)
+        {
+            XTrace.WriteLine("备份原仓库失败:{0}", ex.Message);
+            XTrace.WriteException(ex);
+        }
+
+        try
+        {
+            GitClone(repoUrl, branch ?? "main", repoDir);
+            XTrace.WriteLine("重新克隆成功:{0}", repoDir);
+
+            if (!string.IsNullOrEmpty(backupDir) && Directory.Exists(backupDir))
+            {
+                try
+                {
+                    Directory.Delete(backupDir, true);
+                    XTrace.WriteLine("已删除备份:{0}", backupDir);
+                }
+                catch (Exception ex)
+                {
+                    XTrace.WriteLine("删除备份失败:{0}", ex.Message);
+                    XTrace.WriteException(ex);
+                }
+            }
+        }
+        catch (Exception ex)
+        {
+            XTrace.WriteLine("重新克隆失败:{0}", ex.Message);
+            XTrace.WriteException(ex);
+
+            // 尝试还原备份
+            try
+            {
+                if (!string.IsNullOrEmpty(backupDir) && Directory.Exists(backupDir))
+                {
+                    if (Directory.Exists(repoDir))
+                    {
+                        try { Directory.Delete(repoDir, true); } catch { }
+                    }
+                    Directory.Move(backupDir, repoDir);
+                    XTrace.WriteLine("已还原备份到:{0}", repoDir);
+                }
+            }
+            catch (Exception ex2)
+            {
+                XTrace.WriteLine("还原备份失败:{0}", ex2.Message);
+                XTrace.WriteException(ex2);
+            }
+
+            throw;
+        }
+    }
+
     /// <summary>上传包文件到星尘平台。调用Deploy/UploadBuildFile接口创建应用版本</summary>
     /// <param name="server">服务器地址</param>
     /// <param name="deployName">应用部署集名称</param>