// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 // // 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 // // 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! using System.IO.Compression; namespace Admin.NET.Core.Service; /// /// 系统更新管理服务 🧩 /// [ApiDescriptionSettings(Order = 390)] public class SysUpdateService : IDynamicApiController, ITransient { private readonly SqlSugarRepository _sysUserRep; private readonly SysOnlineUserService _onlineUserService; private readonly SysCacheService _sysCacheService; private readonly CDConfigOptions _cdConfigOptions; private readonly UserManager _userManager; public SysUpdateService( SqlSugarRepository sysUserRep, SysOnlineUserService onlineUserService, IOptions giteeOptions, SysCacheService sysCacheService, UserManager userManager) { _sysUserRep = sysUserRep; _userManager = userManager; _cdConfigOptions = giteeOptions.Value; _sysCacheService = sysCacheService; _onlineUserService = onlineUserService; } /// /// 从远端更新项目 /// /// public async Task Update() { var originColor = Console.ForegroundColor; Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine($"【{DateTime.Now}】从远端仓库部署项目"); try { await SendMessage("----------------------------从远端仓库部署项目-开始----------------------------"); await SendMessage($"客户端host:{App.HttpContext.Request.Host}"); await SendMessage($"客户端IP:{App.HttpContext.GetRemoteIpAddressToIPv4(true)}"); await SendMessage($"仓库地址:https://gitee.com/{_cdConfigOptions.Owner}/{_cdConfigOptions.Repo}.git"); await SendMessage($"仓库分支:{_cdConfigOptions.Branch}"); await SendMessage("项目备份..."); // TODO 备份项目 // 获取解压后的根目录 var rootPath = Path.GetFullPath(Path.Combine(_cdConfigOptions.BackendOutput, "..")); var tempDir = Path.Combine(rootPath, $"{_cdConfigOptions.Repo}-{_cdConfigOptions.Branch}"); await SendMessage("清理旧文件..."); FileHelper.TryDelete(tempDir); await SendMessage("拉取远端代码..."); var stream = await GiteeHelper.DownloadRepoZip(_cdConfigOptions.Owner, _cdConfigOptions.Repo, _cdConfigOptions.AccessToken, _cdConfigOptions.Branch); await SendMessage("文件包解压..."); using ZipArchive archive = new(stream, ZipArchiveMode.Read, leaveOpen: false); archive.ExtractToDirectory(rootPath); // 项目目录 var backendDir = "Admin.NET"; var entryProjectName = "Admin.NET.Web.Entry"; var tempOutput = Path.Combine(_cdConfigOptions.BackendOutput, "temp"); await SendMessage("编译项目..."); await SendMessage($"发布版本:{_cdConfigOptions.Publish.Configuration}"); await SendMessage($"目标框架:{_cdConfigOptions.Publish.TargetFramework}"); await SendMessage($"运行环境:{_cdConfigOptions.Publish.RuntimeIdentifier}"); var option = _cdConfigOptions.Publish; var adminNetDir = Path.Combine(tempDir, backendDir); var args = $"publish \"{entryProjectName}\" -c {option.Configuration} -f {option.TargetFramework} -r {option.RuntimeIdentifier} --output \"{tempOutput}\""; await RunCommandAsync("dotnet", args, adminNetDir); await SendMessage("移动wwwroot目录..."); var wwwrootDir = Path.Combine(adminNetDir, entryProjectName, "wwwroot"); FileHelper.CopyDirectory(wwwrootDir, Path.Combine(tempOutput, "wwwroot"), true); // 删除排除文件 await SendMessage("删除排除文件..."); foreach (var file in _cdConfigOptions.ExcludeFiles ?? new()) FileHelper.TryDelete(Path.Combine(tempOutput, file)); // 将临时文件移动到正式目录 FileHelper.CopyDirectory(tempOutput, _cdConfigOptions.BackendOutput, true); await SendMessage("清理文件..."); FileHelper.TryDelete(tempOutput); FileHelper.TryDelete(tempDir); await SendMessage("----------------------------从远端仓库部署项目-结束----------------------------"); } catch (Exception ex) { await SendMessage("发生异常:" + ex.Message); throw; } finally { Console.ForegroundColor = originColor; } } /// /// 推送消息 /// /// public Task SendMessage(string message) { var logList = _sysCacheService.Get>(CacheConst.KeySysUpdateLog) ?? new(); var content = $"【{DateTime.Now}】 {message}"; Console.WriteLine(content); logList.Add(content); _sysCacheService.Set(CacheConst.KeySysUpdateLog, logList); return Task.CompletedTask; } /// /// 执行命令 /// /// 命令 /// 参数 /// 工作目录 private async Task RunCommandAsync(string command, string arguments, string workingDirectory) { var processStartInfo = new ProcessStartInfo { FileName = command, Arguments = arguments, WorkingDirectory = workingDirectory, RedirectStandardOutput = true, RedirectStandardError = true, StandardOutputEncoding = Encoding.UTF8, StandardErrorEncoding = Encoding.UTF8, UseShellExecute = false, CreateNoWindow = true }; using var process = new Process(); process.StartInfo = processStartInfo; process.Start(); while (!process.StandardOutput.EndOfStream) { string line = await process.StandardOutput.ReadLineAsync(); if (string.IsNullOrEmpty(line)) continue; await SendMessage(line.Trim()); } await process.WaitForExitAsync(); } }