首页 > 其他分享 >基于DotNetty实现自动发布 - 实现一键打包发布

基于DotNetty实现自动发布 - 实现一键打包发布

时间:2023-12-11 11:55:54浏览次数:32  
标签:string zipDir 一键 发布 zipFileName var Logger DotNetty public

前言

上一篇,我只实现了一键检测代码变化,本篇才是真正的实现了一键打包发布

效果图

image
image
image

客户端打包待发布文件

    /// <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字节数组,填充到 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 };

            //创建 NettyClient
            Logger.Info("开始发送");
            using var nettyClient = new NettyClient(webProject.ServerIp, webProject.ServerPort);
            await nettyClient.SendAsync(nettyMessage);
            Logger.Info("完成发送");

            Growl.SuccessGlobal($"发布成功");

            //保存发布记录
            await solutionRepo.SaveFirstPublishAsync(SolutionId, SolutionName, lastGitCommit!.Sha);
            Growl.SuccessGlobal($"操作成功");

            quickDeployDialog?.Close();

NettyHeader 设计

具体实现是 DeployRequestHeader, 继承自 NettyHeader, 保存待发布文件集合,项目名称,解决方案名称, zip 文件名称等

/// <summary>
/// 发布请求头部
/// </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>
/// 执行服务端发布
/// </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("请现在服务器项目的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($"发布成功: {zipDir}");
}
/// <summary>
/// 备份并覆盖旧文件
/// </summary>
private static void DoPublish(List<DeployFileInfo> files, string zipDir, string zipFileName, ProjectConfig projectConfig)
{
    try
    {
        //先创建备份文件夹
        var backupDir = EnsureBackupDirCreated(zipFileName);

        //遍历每个待发布的文件,依次先备份再替换
        foreach (DeployFileInfo file in files)
        {
            //文件相对路径(相对于待发布的项目根目录,也是相对于解压后的根目录)
            var relativeFilePath = file.PublishFileRelativePath;

            //源文件路径(解压后的文件路径)
            var sourceFileName = Path.Combine(zipDir, relativeFilePath);

            //待发布的文件路径 (服务器真实文件路径)
            var destFileName = Path.Combine(projectConfig.ProjectDir, relativeFilePath);

            //服务器已存在此文件,先执行备份
            if (File.Exists(destFileName))
            {
                //备份文件路径
                var backupFileName = Path.Combine(backupDir, relativeFilePath);
                //确保创建备份文件夹
                var backupFileDir = Path.GetDirectoryName(backupFileName);
                if (!Directory.Exists(backupFileDir))
                {
                    Directory.CreateDirectory(backupFileDir!);
                }
                File.Copy(destFileName, backupFileName);
            }
            else
            {
                //服务器不存在此文件,先创建文件夹层级(比如你新加了一个页面demo.aspx,需要发布到服务器对应的位置)
                var destFileDir = Path.GetDirectoryName(destFileName);
                if (!Directory.Exists(destFileDir))
                {
                    Directory.CreateDirectory(destFileDir!);
                }
            }

            //替换服务器文件
            File.Copy(sourceFileName, destFileName, true);
        }
    }
    catch (Exception ex)
    {
        Logger.Error(ex.ToString());
    }
}

总结

至此,我已经完成了自动发布项目的主体功能,实现自动检测代码变化,自动一键打包发布, 不足的地方有: 第一次发布需要手动处理, 项目也需要手动编译,并配置输出目录,但是我相信,这些都不是问题,只要有思路,都是可以解决的,我主要分享下我的实现步骤

注意

1. 本项目目前只支持 .net framework 的单体 Web 项目
2. 客户端和服务端均是 Windows 服务器
3. 线上项目是 IIS 部署的
4. 代码可能存在 BUG,大家发现可以自行解决,或者联系我,我后面不准备继续维护这个项目,毕竟主要是学习分享用的~~~

代码仓库

项目暂且就叫 OpenDeploy

欢迎大家拍砖,Star

下一步

服务端目前是控制台实现, 可以部署为 Windows 服务, 这个也很简单, 我就不发了, 大家自行实现吧, 完结~

标签:string,zipDir,一键,发布,zipFileName,var,Logger,DotNetty,public
From: https://www.cnblogs.com/broadm/p/17894063.html

相关文章

  • GeminiDB Cassandra接口新特性PITR发布:支持任意时间点恢复
    本文分享自华为云社区《GeminiDBCassandra接口新特性PITR发布:支持任意时间点恢复》,作者:GaussDB数据库。技术背景当业务发生数据损毁、数据丢失、数据误删除等一系列故障场景时,往往需要数据库恢复到故障发生前的某一个时刻,且恢复的颗粒度越小越好。而传统数据库采取周期性备份的......
  • 我用 AI 写的《JavaScript 工程师的 Python 指南》电子书发布啦!
    关于本书你好,我是luckrnx09,一名靠React恰饭的前端工程师,很高兴向你介绍我的第一本开源电子书《JavaScript工程师的Python指南》。本书的内容完全免费,开源地址:https://github.com/luckrnx09/python-guide-for-javascript-engineers为什么会有这本书2022年,ChatGPT引起了......
  • 【靶场部署】一键搭建靶场OWASP Mutillidae II
    一、linux提前安装好docker二、安装过程一键安装dockerpullcitizenstig/nowasp 端口映射dockerrun-d-p9009:80citizenstig/nowasp最后浏览器访问即可(你的IP)http://IP地址:9009/index.php 确认即可 开始搞事情! ......
  • Windows 12发布时间曝光!系统需求大幅提高 老电脑恐难更新
    多方消息显示,微软正在准备发布“突破性”的以人工智能为中心的新一代Windows版本,内部代号“HudsonValley”(哈德逊河谷)。WC最新报道称,“HudsonValley”将于2024年下半年推出。微软已经在WindowsInsiderCanary频道中测试下一版本Windows的早期代码和平台工作。据悉,新版Window......
  • Spring的事件发布机制
    观察者模式的事件思想观察者模式作为对象间一对多依赖关系的实现。在观察者模式中,被观察者相当于事件中的时间发布者,而观察者相当于事件中的监听者。因此可以说:观察者模式就是事件驱动机制的一种体现。事件驱动一个常见的形式就是发布-订阅模式,在跨进程的通信间,我们常常使......
  • Redis7 发布订阅
    1、是什么是一种通信模式:发送者(PUBLISH)发送消息,订阅者(SUBSCRIBE)接收消息,可以实现进程间的消息传递Redis可以实现消息中间件MQ的功能,通过发布订阅实现消息的引导和分流2、能干啥Redis客户端可以订阅任意数量的频道,类似于微信关注多个公众号当有新消息通过PUBLISH命令发送给频......
  • K3588芯片助力,全新单板计算机ArmSoM-Sige7震撼发布!
    RK3588芯片助力,全新单板计算机ArmSoM-Sige7震撼发布!近日,我们欣喜地宣布推出一款全新的单板计算机,搭载着强大的RK3588芯片,为用户提供更卓越的计算性能和多样化的应用场景。这一新产品的发布标志着我们在技术创新和产品研发方面取得了重要突破,为用户提供了更为出色的计算体验,Sige7......
  • RK3588芯片助力,全新单板计算机ArmSoM-Sige7震撼发布!
     近日,我们欣喜地宣布推出一款全新的单板计算机,搭载着强大的RK3588芯片,为用户提供更卓越的计算性能和多样化的应用场景。这一新产品的发布标志着我们在技术创新和产品研发方面取得了重要突破,为用户提供了更为出色的计算体验,Sige7-连接创新,无限可能  1.RK3588芯片的强大......
  • 《安富莱嵌入式周报》第328期:自主微型机器人,火星探测器发射前失误故障分析,微软推出12
    周报汇总地址:http://www.armbbs.cn/forum.php?mod=forumdisplay&fid=12&filter=typeid&typeid=104 更新一期视频教程:【实战技能】单步运行源码分析,一期视频整明白FreeRTOS内核源码框架和运行机制,RTOSTrace链表功能展示https://www.armbbs.cn/forum.php?mod=viewthread&tid......
  • 被动副业机赚钱项目教程,Docker一键运行
    被动副业机赚钱项目教程,Docker一键运行软件下载视频教程/opt/wxedge_storage路径换成你设备里面的路径即可,其余参数不用变,镜像名为onething1/wxedge更多安装说明,可参考官方文档:容器魔方产品介绍dockerrun-d--name=wxedge\--restart=always--privileged--net=host\-......