本文共 7791 字,大约阅读时间需要 25 分钟。
前两篇博文中提到Development,QA,Staging,Production四个环境,也说明了源代码的分支和四个环境的对应关系,本篇博文聊一下,怎么把源码自动化发布到对应的环境中。
市面上主流的DevOpt工具都支持这些功能,github,gitlab,都有CICD功能,当然,如果源码服务器是自己搭建的,也可以利用像Jenkins这类软件来实现CICD,关于这些大众工具,网上有很多教程序,这里就不主要来分享了,本例是用.net core实现一个极简的自动发布工具——《MyCICD》。
说一下实现思路吧!
clone 或 pull分支代码
publish
run
是不是很简单,上代码吧
using System;using System.Collections.Generic;using System.Configuration;using System.Diagnostics;using System.IO;using System.Linq;using System.Text;using System.Threading;namespace MyCICD{ class Program { static void Main(string[] args) { var processIDs = new int[0]; while (true) { if (Clone()) { if (Publish(processIDs)) { processIDs = Run(); } } Thread.Sleep(30000); } } ////// git 克隆 /// ///static bool Clone() { var gitLib = ConfigurationManager.AppSettings["GitLib"]; var sourcePath = ConfigurationManager.AppSettings["SourcePath"]; var sourceDir = $"{sourcePath.TrimEnd('/', '\\') }/{ Path.GetFileNameWithoutExtension(gitLib)} "; //存在就拉取代码,不存在就克隆 if (Directory.Exists(sourceDir)) { return Pull(sourceDir); } else { return Clone(gitLib, sourceDir); } } /// /// 克隆项目代码 /// /// git库 /// 本地保存路径 ///static bool Clone(string gitLib, string sourceDir) { Console.WriteLine("开始Clone"); var processStartInfo = new ProcessStartInfo("git", $"clone {gitLib} {sourceDir}") { RedirectStandardOutput = true }; var process = Process.Start(processStartInfo); if (process == null) { Console.WriteLine("请确认是否安装git"); return false; } else { using (var output = process.StandardOutput) { while (!output.EndOfStream) { Console.WriteLine(output.ReadLine()); } if (!process.HasExited) { process.Kill(); } } Console.WriteLine($"执行时间 :{(process.ExitTime - process.StartTime).TotalMilliseconds} ms"); Console.WriteLine("结束Clone"); return process.ExitCode == 0; } } /// /// 拉取项目代码 /// /// 源码路径 ///static bool Pull(string sourceDir) { Console.WriteLine("开始Fetch"); var processStartInfo = new ProcessStartInfo("git", $"pull origin") { RedirectStandardOutput = true, WorkingDirectory = sourceDir, }; var process = Process.Start(processStartInfo); using (var output = process.StandardOutput) { var resultBuilder = new StringBuilder(); while (!output.EndOfStream) { resultBuilder.AppendLine(output.ReadLine()); } Console.WriteLine(resultBuilder); if (!process.HasExited) { process.Kill(); } if (resultBuilder.ToString() != "Already up to date.\r\n") { Console.WriteLine("结束Fetch"); return true; } else { Console.WriteLine("结束Fetch,远程仓库没有更新"); return false; } } } #region 发布项目 /// /// 发布项目 /// ///static bool Publish(int[] processIDs) { Console.WriteLine("开始Publish"); var sourcePath = ConfigurationManager.AppSettings["SourcePath"]; var publishProject = ConfigurationManager.AppSettings["PublishProject"]; //找出要发布的项目 var projectPathLists = publishProject.Split(","); var projects = GetProjectsPath(sourcePath, projectPathLists); var publishDir = $"{sourcePath}/publish"; var result = true;//如果有一个项目失败,发布就会失败 //为了发布,关闭之前运行中的进程 foreach (var processid in processIDs) { Process.GetProcessById(processid).Kill(); } //发布项目 foreach (var project in projects) { var processStartInfo = new ProcessStartInfo("dotnet", $"publish {project} -o {publishDir}/{Path.GetFileNameWithoutExtension(project)}") { RedirectStandardOutput = true }; var process = Process.Start(processStartInfo); if (process == null) { Console.WriteLine("请确认是否安装dotnet sdk"); return false; } else { using (var output = process.StandardOutput) { while (!output.EndOfStream) { Console.WriteLine(output.ReadLine()); } if (!process.HasExited) { process.Kill(); } } Console.WriteLine($"执行时间 :{(process.ExitTime - process.StartTime).TotalMilliseconds} ms"); if (process.ExitCode != 0) { Console.WriteLine($"{Path.GetFileNameWithoutExtension(project)}发布失败"); } result = result || process.ExitCode == 0; } } Console.WriteLine("结束Publish"); return result; } /// /// 查找项目 /// /// 源码路径 /// 项目集 ///static string[] GetProjectsPath(string sourcePath, string[] projects) { var paths = new List (); foreach (var file in Directory.GetFiles(sourcePath)) { if (projects.Contains(Path.GetFileName(file))) { paths.Add(file); } } foreach (var dir in Directory.GetDirectories(sourcePath)) { paths.AddRange(GetProjectsPath(dir, projects)); } return paths.ToArray(); } #endregion #region 运行项目 /// /// 运行项目 /// ///static int[] Run() { Console.WriteLine("开始运行"); var sourcePath = ConfigurationManager.AppSettings["SourcePath"]; var publishDir = $"{sourcePath}/publish"; var proceddIDs = new List (); foreach (var projectPath in Directory.GetDirectories(publishDir)) { var processStartInfo = new ProcessStartInfo("dotnet", $"{Path.GetFileNameWithoutExtension(projectPath)}.dll") { RedirectStandardOutput = true, WorkingDirectory = projectPath, }; var process = Process.Start(processStartInfo); proceddIDs.Add(process.Id); } Console.WriteLine("结束运行"); return proceddIDs.ToArray(); } #endregion }}
App.config配置文件
这个例子很简单,只支持在windows下运行,同时run起来的应用和MyCICD是在一个进程中,一但进程关闭,run的服务也就掉了,还有很多需要改进,如果有兴趣,可以完善,比如可以跑大多个平台上(linux,docker,mac)下,也可以把MyCICD和运行的服务分离,进程间互不影响。
转载地址:http://xakdi.baihongyu.com/