SysUpdateService.cs 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. // Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
  2. //
  3. // 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
  4. //
  5. // 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
  6. using System.IO.Compression;
  7. namespace Admin.NET.Core.Service;
  8. /// <summary>
  9. /// 系统更新管理服务 🧩
  10. /// </summary>
  11. [ApiDescriptionSettings(Order = 390)]
  12. public class SysUpdateService : IDynamicApiController, ITransient
  13. {
  14. private readonly SqlSugarRepository<SysUser> _sysUserRep;
  15. private readonly SysOnlineUserService _onlineUserService;
  16. private readonly SysCacheService _sysCacheService;
  17. private readonly CDConfigOptions _cdConfigOptions;
  18. private readonly UserManager _userManager;
  19. public SysUpdateService(
  20. SqlSugarRepository<SysUser> sysUserRep,
  21. SysOnlineUserService onlineUserService,
  22. IOptions<CDConfigOptions> giteeOptions,
  23. SysCacheService sysCacheService,
  24. UserManager userManager)
  25. {
  26. _sysUserRep = sysUserRep;
  27. _userManager = userManager;
  28. _cdConfigOptions = giteeOptions.Value;
  29. _sysCacheService = sysCacheService;
  30. _onlineUserService = onlineUserService;
  31. }
  32. /// <summary>
  33. /// 从远端更新项目
  34. /// </summary>
  35. /// <returns></returns>
  36. public async Task Update()
  37. {
  38. var originColor = Console.ForegroundColor;
  39. Console.ForegroundColor = ConsoleColor.Yellow;
  40. Console.WriteLine($"【{DateTime.Now}】从远端仓库部署项目");
  41. try
  42. {
  43. await SendMessage("----------------------------从远端仓库部署项目-开始----------------------------");
  44. await SendMessage($"客户端host:{App.HttpContext.Request.Host}");
  45. await SendMessage($"客户端IP:{App.HttpContext.GetRemoteIpAddressToIPv4(true)}");
  46. await SendMessage($"仓库地址:https://gitee.com/{_cdConfigOptions.Owner}/{_cdConfigOptions.Repo}.git");
  47. await SendMessage($"仓库分支:{_cdConfigOptions.Branch}");
  48. await SendMessage("项目备份...");
  49. // TODO 备份项目
  50. // 获取解压后的根目录
  51. var rootPath = Path.GetFullPath(Path.Combine(_cdConfigOptions.BackendOutput, ".."));
  52. var tempDir = Path.Combine(rootPath, $"{_cdConfigOptions.Repo}-{_cdConfigOptions.Branch}");
  53. await SendMessage("清理旧文件...");
  54. FileHelper.TryDelete(tempDir);
  55. await SendMessage("拉取远端代码...");
  56. var stream = await GiteeHelper.DownloadRepoZip(_cdConfigOptions.Owner, _cdConfigOptions.Repo,
  57. _cdConfigOptions.AccessToken, _cdConfigOptions.Branch);
  58. await SendMessage("文件包解压...");
  59. using ZipArchive archive = new(stream, ZipArchiveMode.Read, leaveOpen: false);
  60. archive.ExtractToDirectory(rootPath);
  61. // 项目目录
  62. var backendDir = "Admin.NET";
  63. var entryProjectName = "Admin.NET.Web.Entry";
  64. var tempOutput = Path.Combine(_cdConfigOptions.BackendOutput, "temp");
  65. await SendMessage("编译项目...");
  66. await SendMessage($"发布版本:{_cdConfigOptions.Publish.Configuration}");
  67. await SendMessage($"目标框架:{_cdConfigOptions.Publish.TargetFramework}");
  68. await SendMessage($"运行环境:{_cdConfigOptions.Publish.RuntimeIdentifier}");
  69. var option = _cdConfigOptions.Publish;
  70. var adminNetDir = Path.Combine(tempDir, backendDir);
  71. var args = $"publish \"{entryProjectName}\" -c {option.Configuration} -f {option.TargetFramework} -r {option.RuntimeIdentifier} --output \"{tempOutput}\"";
  72. await RunCommandAsync("dotnet", args, adminNetDir);
  73. await SendMessage("移动wwwroot目录...");
  74. var wwwrootDir = Path.Combine(adminNetDir, entryProjectName, "wwwroot");
  75. FileHelper.CopyDirectory(wwwrootDir, Path.Combine(tempOutput, "wwwroot"), true);
  76. // 删除排除文件
  77. await SendMessage("删除排除文件...");
  78. foreach (var file in _cdConfigOptions.ExcludeFiles ?? new()) FileHelper.TryDelete(Path.Combine(tempOutput, file));
  79. // 将临时文件移动到正式目录
  80. FileHelper.CopyDirectory(tempOutput, _cdConfigOptions.BackendOutput, true);
  81. await SendMessage("清理文件...");
  82. FileHelper.TryDelete(tempOutput);
  83. FileHelper.TryDelete(tempDir);
  84. await SendMessage("----------------------------从远端仓库部署项目-结束----------------------------");
  85. }
  86. catch (Exception ex)
  87. {
  88. await SendMessage("发生异常:" + ex.Message);
  89. throw;
  90. }
  91. finally
  92. {
  93. Console.ForegroundColor = originColor;
  94. }
  95. }
  96. /// <summary>
  97. /// 推送消息
  98. /// </summary>
  99. /// <param name="message"></param>
  100. public Task SendMessage(string message)
  101. {
  102. var logList = _sysCacheService.Get<List<string>>(CacheConst.KeySysUpdateLog) ?? new();
  103. var content = $"【{DateTime.Now}】 {message}";
  104. Console.WriteLine(content);
  105. logList.Add(content);
  106. _sysCacheService.Set(CacheConst.KeySysUpdateLog, logList);
  107. return Task.CompletedTask;
  108. }
  109. /// <summary>
  110. /// 执行命令
  111. /// </summary>
  112. /// <param name="command">命令</param>
  113. /// <param name="arguments">参数</param>
  114. /// <param name="workingDirectory">工作目录</param>
  115. private async Task RunCommandAsync(string command, string arguments, string workingDirectory)
  116. {
  117. var processStartInfo = new ProcessStartInfo
  118. {
  119. FileName = command,
  120. Arguments = arguments,
  121. WorkingDirectory = workingDirectory,
  122. RedirectStandardOutput = true,
  123. RedirectStandardError = true,
  124. StandardOutputEncoding = Encoding.UTF8,
  125. StandardErrorEncoding = Encoding.UTF8,
  126. UseShellExecute = false,
  127. CreateNoWindow = true
  128. };
  129. using var process = new Process();
  130. process.StartInfo = processStartInfo;
  131. process.Start();
  132. while (!process.StandardOutput.EndOfStream)
  133. {
  134. string line = await process.StandardOutput.ReadLineAsync();
  135. if (string.IsNullOrEmpty(line)) continue;
  136. await SendMessage(line.Trim());
  137. }
  138. await process.WaitForExitAsync();
  139. }
  140. }