<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      基于DotNetty實現自動發布 - 實現一鍵打包發布

      前言

      上一篇,我只實現了一鍵檢測代碼變化,本篇才是真正的實現了一鍵打包發布

      效果圖

      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 服務, 這個也很簡單, 我就不發了, 大家自行實現吧, 完結~

      posted @ 2023-12-11 11:50  Broadm  閱讀(1063)  評論(6)    收藏  舉報
      主站蜘蛛池模板: 国产熟女激情一区二区三区| 亚洲乱码精品久久久久..| 视频一区视频二区视频三| 免费观看的AV毛片的网站不卡| 鲁山县| 26uuu另类亚洲欧美日本| 亚亚洲视频一区二区三区| 四虎成人在线观看免费| 无码人妻一区二区三区AV| 中文国产成人精品久久一| 无码人妻一区二区三区免费N鬼沢 亚洲国产精品自产在线播放 | 人妻激情另类乱人伦人妻| 亚洲av永久无码天堂影院| 偷拍一区二区三区在线视频 | 美女无遮挡免费视频网站| 日本强好片久久久久久aaa| av无码一区二区大桥久未| 日韩福利片午夜免费观着| 手机看片AV永久免费| 色偷偷亚洲男人的天堂| 人妻无码久久久久久久久久久| 熟妇人妻久久春色视频网| 国产精品白嫩初高生免费视频| 在线精品国产中文字幕| 99中文字幕国产精品| 亚洲精品日韩中文字幕| 亚洲av成人无码天堂| 亚洲gv天堂无码男同在线观看| 国产精品露脸视频观看| 国产精品无码无片在线观看3d| 久久精品免视看国产成人| 欧美牲交a欧美牲交aⅴ图片| 精品久久久bbbb人妻| 日韩乱码人妻无码中文字幕视频| 一区二区三区人妻无码| 年日韩激情国产自偷亚洲| 成人网站网址导航| 性按摩玩人妻hd中文字幕| 久久亚洲国产五月综合网| 午夜av高清在线观看| 国产一区二区亚洲精品|