前言

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

效果图





客户端打包待发布文件

  1. /// <summary>
  2. /// 把多个文件添加到压缩包 (保留文件夹层级关系)
  3. /// </summary>
  4. public static async Task<ZipFileResult> CreateZipAsync(IEnumerable<ZipFileInfo> zipFileInfo)
  5. {
  6. return await Task.Run(() =>
  7. {
  8. var zipDir = EnsureZipDirCreated();
  9. var zipFileName = $"{DateTime.Now:yyyyMMdd_HHmmss_}{Guid.NewGuid()}.zip";
  10. var zipPath = Path.Combine(zipDir, zipFileName);
  11. using var archive = ZipFile.Open(zipPath, ZipArchiveMode.Update);
  12. foreach (var item in zipFileInfo)
  13. {
  14. archive.CreateEntryFromFile(item.FileAbsolutePath, item.FileRelativePath, CompressionLevel.SmallestSize);
  15. }
  16. return new ZipFileResult() { FullFileName = zipPath, FileName = zipFileName };
  17. });
  18. }

客户端封装 NettyMessage

  1. //读取zip字节数组,填充到 NettyMessage 的 Body
  2. var body = await File.ReadAllBytesAsync(zipResult.FullFileName);
  3. //NettyHeader
  4. var header = new DeployRequestHeader()
  5. {
  6. Files = PublishFiles,
  7. SolutionName = SolutionName,
  8. ProjectName = webProject!.ProjectName,
  9. ZipFileName = zipResult.FileName,
  10. };
  11. var nettyMessage = new NettyMessage { Header = header, Body = body };
  12. //创建 NettyClient
  13. Logger.Info("开始发送");
  14. using var nettyClient = new NettyClient(webProject.ServerIp, webProject.ServerPort);
  15. await nettyClient.SendAsync(nettyMessage);
  16. Logger.Info("完成发送");
  17. Growl.SuccessGlobal($"发布成功");
  18. //保存发布记录
  19. await solutionRepo.SaveFirstPublishAsync(SolutionId, SolutionName, lastGitCommit!.Sha);
  20. Growl.SuccessGlobal($"操作成功");
  21. quickDeployDialog?.Close();

NettyHeader 设计

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

  1. /// <summary>
  2. /// 发布请求头部
  3. /// </summary>
  4. public class DeployRequestHeader : NettyHeader
  5. {
  6. public DeployRequestHeader() : base("Deploy/Run") { }
  7. public List<DeployFileInfo> Files { get; set; } = [];
  8. public string ProjectName { get; set; } = string.Empty;
  9. public string SolutionName { get; set; } = string.Empty;
  10. public string ZipFileName { get; set; } = string.Empty;
  11. }

服务端处理

  • 解压 zip
  • 备份目标文件(存在才备份)
  • 替换目标文件(不存在则新建)
  1. /// <summary>
  2. /// 执行服务端发布
  3. /// </summary>
  4. /// <param name="model"></param>
  5. public void Run(DeployRequestHeader model)
  6. {
  7. Logger.Warn($"收到客户端的消息: {model.ToJsonString(true)}");
  8. var configs = NettyServer.AppHost.Services.GetRequiredService<IOptions<List<ProjectConfig>>>();
  9. var projectConfig = configs.Value.FirstOrDefault(a => a.ProjectName == model.ProjectName);
  10. if (projectConfig == null)
  11. {
  12. Logger.Error("请现在服务器项目的appsettings.json中配置项目信息");
  13. return;
  14. }
  15. var zipBytes = Request.Body;
  16. if (zipBytes == null || zipBytes.Length == 0)
  17. {
  18. Logger.Error("ZipBytes为空");
  19. return;
  20. }
  21. var zipFileName = model.ZipFileName;
  22. if (string.IsNullOrEmpty(zipFileName))
  23. {
  24. Logger.Error("ZipFileName为空");
  25. return;
  26. }
  27. //解压
  28. var zipDir = ZipHelper.UnZip(zipBytes, zipFileName);
  29. Logger.Info($"解压成功: {zipDir}");
  30. //备份并覆盖旧文件
  31. DoPublish(model.Files, zipDir, zipFileName, projectConfig);
  32. Logger.Info($"发布成功: {zipDir}");
  33. }
  1. /// <summary>
  2. /// 备份并覆盖旧文件
  3. /// </summary>
  4. private static void DoPublish(List<DeployFileInfo> files, string zipDir, string zipFileName, ProjectConfig projectConfig)
  5. {
  6. try
  7. {
  8. //先创建备份文件夹
  9. var backupDir = EnsureBackupDirCreated(zipFileName);
  10. //遍历每个待发布的文件,依次先备份再替换
  11. foreach (DeployFileInfo file in files)
  12. {
  13. //文件相对路径(相对于待发布的项目根目录,也是相对于解压后的根目录)
  14. var relativeFilePath = file.PublishFileRelativePath;
  15. //源文件路径(解压后的文件路径)
  16. var sourceFileName = Path.Combine(zipDir, relativeFilePath);
  17. //待发布的文件路径 (服务器真实文件路径)
  18. var destFileName = Path.Combine(projectConfig.ProjectDir, relativeFilePath);
  19. //服务器已存在此文件,先执行备份
  20. if (File.Exists(destFileName))
  21. {
  22. //备份文件路径
  23. var backupFileName = Path.Combine(backupDir, relativeFilePath);
  24. //确保创建备份文件夹
  25. var backupFileDir = Path.GetDirectoryName(backupFileName);
  26. if (!Directory.Exists(backupFileDir))
  27. {
  28. Directory.CreateDirectory(backupFileDir!);
  29. }
  30. File.Copy(destFileName, backupFileName);
  31. }
  32. else
  33. {
  34. //服务器不存在此文件,先创建文件夹层级(比如你新加了一个页面demo.aspx,需要发布到服务器对应的位置)
  35. var destFileDir = Path.GetDirectoryName(destFileName);
  36. if (!Directory.Exists(destFileDir))
  37. {
  38. Directory.CreateDirectory(destFileDir!);
  39. }
  40. }
  41. //替换服务器文件
  42. File.Copy(sourceFileName, destFileName, true);
  43. }
  44. }
  45. catch (Exception ex)
  46. {
  47. Logger.Error(ex.ToString());
  48. }
  49. }

总结

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

注意

1. 本项目目前只支持 .net framework 的单体 Web 项目

2. 客户端和服务端均是 Windows 服务器

3. 线上项目是 IIS 部署的

4. 代码可能存在 BUG,大家发现可以自行解决,或者联系我,我后面不准备继续维护这个项目,毕竟主要是学习分享用的~~~

代码仓库

项目暂且就叫 OpenDeploy

欢迎大家拍砖,Star

下一步

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

基于DotNetty实现自动发布 - 实现一键打包发布的更多相关文章

  1. 使用jenkins一键打包发布vue项目

    jenkins的安装 Jenkins是一款开源 CI&CD 软件,用于自动化各种任务,包括构建.测试和部署软件. Jenkins 支持各种运行方式,可通过系统包.Docker 或者通过一个独立 ...

  2. linux自动更新代码,打包发布

    1.安装svn yum install subversion 2.安装 maven 下载:百度云盘地址为 http://pan.baidu.com/s/1nuKQGjv 解压 tar -zxvf ap ...

  3. 带你了解基于Ploto构建自动驾驶平台

    摘要:华为云Solution as Code推出基于Ploto构建自动驾驶平台解决方案. 本文分享自华为云社区<基于Ploto构建自动驾驶平台>,作者:阿米托福 . 2022年6月15日, ...

  4. Ant自动编译打包&发布 android项目

    Eclipse用起来虽然方便,但是编译打包android项目还是比较慢,尤其将应用打包发布到各个渠道时,用Eclipse手动打包各种渠道包就有点不切实际了,这时候我们用到Ant帮我们自动编译打包了. ...

  5. Andorid进阶7—— Ant自动编译打包&发布 android项目

    http://www.cnblogs.com/tt_mc/p/3891546.html Eclipse用起来虽然方便,但是编译打包android项目还是比较慢,尤其将应用打包发布到各个渠道时,用Ecl ...

  6. 使用 maven 自动将源码打包并发布

    1.maven-source-plugin 访问地址 在 pom.xml 中添加 下面的 内容,可以 使用 maven 生成 jar 的同时 生成 sources 包 <plugin> & ...

  7. maven 聚合的含义是父类打包 ,清理等 则子类自动打包;也就是一键打包 方便服务

    maven 聚合的含义是父类打包 ,清理等 则子类自动打包:也就是一键打包 方便服务

  8. 使用release自动打包发布正式版详细教程

    昨天写了个release插件的版本管理,今天就在自动发布过程中遇到了许多坑,只能再写一篇自动发布详细教程,纪念我那昨日逝去的青春 (╥ _ ╥`) release正常打包发布流程按照如下几个阶段: C ...

  9. 使用VS中自带的一键打包功能将我们的ASP.NET Core类库打包并将程序包(类库)发布到NuGet平台上进行管理

    本章将和大家简单分享下如何使用VS中自带的一键打包功能将我们的ASP.NET Core类库打包并将程序包(类库)发布到NuGet平台上进行管理. 一.注册并登录NuGet平台 NuGet官网:http ...

  10. 发布nuget包的正确姿势---cicd自动打包发布nuget包

    最轻便的发布nuget包方式,方便cicd自动打包发布nuget包 首先新建项目 项目名随便取,这里就叫它GuiH.ClassLibrary 默认即可,需要改目标版本时,等创建好再改 项目创建好了 随 ...

随机推荐

  1. 7.1 C++ STL 非变易查找算法

    C++ STL 中的非变易算法(Non-modifying Algorithms)是指那些不会修改容器内容的算法,是C++提供的一组模板函数,该系列函数不会修改原序列中的数据,而是对数据进行处理.查找 ...

  2. java与es8实战之一:以builder pattern开篇

    欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 关于<java与es8实战>系列 < ...

  3. Jquery ajax方式提交表单

    Jquery ajax方式提交表单,目前用过的有两种方式: 1.引人jquery.js和jquery.easyui.min.js后,用下面的方法: $('#checkForm1').form('sub ...

  4. 《Hadoop大数据技术开发实战》新书上线

    当今互联网已进入大数据时代,大数据技术已广泛应用于金融.医疗.教育.电信.政府等领域.各行各业每天都在产生大量的数据,数据计量单位已从B.KB.MB.GB.TB发展到PB.EB.ZB.YB甚至BB.N ...

  5. PowerShell收集信息及绕过PowerShell权限

    PowerShell脚本的4种执行权限: Restricted:默认设置,不允许任何script运行 AllSigned:只能运行经过数字证书签名的script RemoteSigned:本地脚本不做 ...

  6. 如何解决IOS 15提示“此App的开发者需要更新APP以在此IOS版本上正常工作”, 无法打开安装的APP的问题

    在苹果手机最新的IOS 15 beta的系统上安装自签名或者企业签名的APP时,可能会遇到如下的错误提示: 此App的开发者需要更新APP以在此IOS版本上正常工作 The developer of ...

  7. 解密Prompt系列15. LLM Agent之数据库应用设计:DIN & C3 & SQL-Palm & BIRD

    上一章我们主要讲搜索引擎和LLM的应用设计,这一章我们来唠唠大模型和DB数据库之间的交互方案.有很多数据平台已经接入,可以先去玩玩再来看下面的实现方案,推荐 sql translate:简单,文本到S ...

  8. Spring Boot虚拟线程与Webflux在JWT验证和MySQL查询上的性能比较

    早上看到一篇关于Spring Boot虚拟线程和Webflux性能对比的文章,觉得还不错.内容较长,我就不翻译了,抓重点给大家介绍一下这篇文章的核心内容,方便大家快速阅读. 测试场景 作者采用了一个尽 ...

  9. 【好玩】如何在github主页放一条贪吃蛇

    前言 缘由 github放小蛇,就问你烧不烧 起因看到大佬github上有一条贪吃蛇扭来扭去,觉得好玩,遂给大家分享一下本狗的玩蛇历程 成果初展 贪吃蛇 访问地址 https://github.com ...

  10. ElasticSearch系列——倒排索引、删除映射类型、打分机制、配置文件、常见错误

    文章目录 1 倒排索引 2 删除映射类型 一 前言 二 什么是映射类型? 三 为什么要删除映射类型? 四 映射类型的替代方法 4.1 将映射类型分开存储在索引中 4.2 自定义类型字段回到顶部 五 没 ...