前言
上一篇,我只實現(xiàn)了一鍵檢測代碼變化,本篇才是真正的實現(xiàn)了一鍵打包發(fā)布
效果圖
客戶端打包待發(fā)布文件
/// <summary>
/// 把多個文件添加到壓縮包 (保留文件夾層級關系)
/// </summary>
public static async Task<ZipFileResult> CreateZipAsync(IEnumerable<ZipFileInfo> zipFileInfo)
{
return await Task.Run(() =>
{
var zipDir = EnsureZipDirCreated();
var zipFileName = $"{DateTime.Now:yyyyMMdd_HHmmss_}{Guid.NewGuid()}.zip";
var zipPath = Path.Combine(zipDir, zipFileName);
using var archive = ZipFile.Open(zipPath, ZipArchiveMode.Update);
foreach (var item in zipFileInfo)
{
archive.CreateEntryFromFile(item.FileAbsolutePath, item.FileRelativePath, CompressionLevel.SmallestSize);
}
return new ZipFileResult() { FullFileName = zipPath, FileName = zipFileName };
});
}
客戶端封裝 NettyMessage
//讀取zip字節(jié)數(shù)組,填充到 NettyMessage 的 Body
var body = await File.ReadAllBytesAsync(zipResult.FullFileName);
//NettyHeader
var header = new DeployRequestHeader()
{
Files = PublishFiles,
SolutionName = SolutionName,
ProjectName = webProject!.ProjectName,
ZipFileName = zipResult.FileName,
};
var nettyMessage = new NettyMessage { Header = header, Body = body };
//創(chuàng)建 NettyClient
Logger.Info("開始發(fā)送");
using var nettyClient = new NettyClient(webProject.ServerIp, webProject.ServerPort);
await nettyClient.SendAsync(nettyMessage);
Logger.Info("完成發(fā)送");
Growl.SuccessGlobal($"發(fā)布成功");
//保存發(fā)布記錄
await solutionRepo.SaveFirstPublishAsync(SolutionId, SolutionName, lastGitCommit!.Sha);
Growl.SuccessGlobal($"操作成功");
quickDeployDialog?.Close();
NettyHeader 設計
具體實現(xiàn)是 DeployRequestHeader
, 繼承自 NettyHeader
, 保存待發(fā)布文件集合,項目名稱,解決方案名稱, zip 文件名稱等
/// <summary>
/// 發(fā)布請求頭部
/// </summary>
public class DeployRequestHeader : NettyHeader
{
public DeployRequestHeader() : base("Deploy/Run") { }
public List<DeployFileInfo> Files { get; set; } = [];
public string ProjectName { get; set; } = string.Empty;
public string SolutionName { get; set; } = string.Empty;
public string ZipFileName { get; set; } = string.Empty;
}
服務端處理
- 解壓 zip
- 備份目標文件(存在才備份)
- 替換目標文件(不存在則新建)
/// <summary>
/// 執(zhí)行服務端發(fā)布
/// </summary>
/// <param name="model"></param>
public void Run(DeployRequestHeader model)
{
Logger.Warn($"收到客戶端的消息: {model.ToJsonString(true)}");
var configs = NettyServer.AppHost.Services.GetRequiredService<IOptions<List<ProjectConfig>>>();
var projectConfig = configs.Value.FirstOrDefault(a => a.ProjectName == model.ProjectName);
if (projectConfig == null)
{
Logger.Error("請現(xiàn)在服務器項目的appsettings.json中配置項目信息");
return;
}
var zipBytes = Request.Body;
if (zipBytes == null || zipBytes.Length == 0)
{
Logger.Error("ZipBytes為空");
return;
}
var zipFileName = model.ZipFileName;
if (string.IsNullOrEmpty(zipFileName))
{
Logger.Error("ZipFileName為空");
return;
}
//解壓
var zipDir = ZipHelper.UnZip(zipBytes, zipFileName);
Logger.Info($"解壓成功: {zipDir}");
//備份并覆蓋舊文件
DoPublish(model.Files, zipDir, zipFileName, projectConfig);
Logger.Info($"發(fā)布成功: {zipDir}");
}
/// <summary>
/// 備份并覆蓋舊文件
/// </summary>
private static void DoPublish(List<DeployFileInfo> files, string zipDir, string zipFileName, ProjectConfig projectConfig)
{
try
{
//先創(chuàng)建備份文件夾
var backupDir = EnsureBackupDirCreated(zipFileName);
//遍歷每個待發(fā)布的文件,依次先備份再替換
foreach (DeployFileInfo file in files)
{
//文件相對路徑(相對于待發(fā)布的項目根目錄,也是相對于解壓后的根目錄)
var relativeFilePath = file.PublishFileRelativePath;
//源文件路徑(解壓后的文件路徑)
var sourceFileName = Path.Combine(zipDir, relativeFilePath);
//待發(fā)布的文件路徑 (服務器真實文件路徑)
var destFileName = Path.Combine(projectConfig.ProjectDir, relativeFilePath);
//服務器已存在此文件,先執(zhí)行備份
if (File.Exists(destFileName))
{
//備份文件路徑
var backupFileName = Path.Combine(backupDir, relativeFilePath);
//確保創(chuàng)建備份文件夾
var backupFileDir = Path.GetDirectoryName(backupFileName);
if (!Directory.Exists(backupFileDir))
{
Directory.CreateDirectory(backupFileDir!);
}
File.Copy(destFileName, backupFileName);
}
else
{
//服務器不存在此文件,先創(chuàng)建文件夾層級(比如你新加了一個頁面demo.aspx,需要發(fā)布到服務器對應的位置)
var destFileDir = Path.GetDirectoryName(destFileName);
if (!Directory.Exists(destFileDir))
{
Directory.CreateDirectory(destFileDir!);
}
}
//替換服務器文件
File.Copy(sourceFileName, destFileName, true);
}
}
catch (Exception ex)
{
Logger.Error(ex.ToString());
}
}
總結
至此,我已經(jīng)完成了自動發(fā)布項目的主體功能,實現(xiàn)自動檢測代碼變化,自動一鍵打包發(fā)布, 不足的地方有: 第一次發(fā)布需要手動處理, 項目也需要手動編譯,并配置輸出目錄,但是我相信,這些都不是問題,只要有思路,都是可以解決的,我主要分享下我的實現(xiàn)步驟
注意
1. 本項目目前只支持 .net framework 的單體 Web 項目
2. 客戶端和服務端均是 Windows 服務器
3. 線上項目是 IIS 部署的
4. 代碼可能存在 BUG,大家發(fā)現(xiàn)可以自行解決,或者聯(lián)系我,我后面不準備繼續(xù)維護這個項目,畢竟主要是學習分享用的~~~
代碼倉庫
項目暫且就叫
OpenDeploy
吧
- OpenDeploy: https://gitee.com/broadm-dotnet/OpenDeploy
歡迎大家拍磚,Star文章來源:http://www.zghlxwxcb.cn/news/detail-750464.html
下一步
服務端目前是控制臺實現(xiàn), 可以部署為 Windows 服務, 這個也很簡單, 我就不發(fā)了, 大家自行實現(xiàn)吧, 完結~文章來源地址http://www.zghlxwxcb.cn/news/detail-750464.html
到了這里,關于基于DotNetty實現(xiàn)自動發(fā)布 - 實現(xiàn)一鍵打包發(fā)布的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!