Fork me on GitHub

持续交付三:动手自动化“开发”—>“测试”

 

 

  前两篇博文中提到Development,QA,Staging,Production四个环境,也说明了源代码的分支和四个环境的对应关系,本篇博文聊一下,怎么把源码自动化发布到对应的环境中。

  市面上主流的DevOpt工具都支持这些功能,github,gitlab,都有CICD功能,当然,如果源码服务器是自己搭建的,也可以利用像Jenkins这类软件来实现CICD,关于这些大众工具,网上有很多教程序,这里就不主要来分享了,本例是用.net core实现一个极简的自动发布工具——《MyCICD》。

  说一下实现思路吧!

  1. clone 或 pull分支代码
  2. publish
  3. 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);
            }
        }

        /// <summary>
        /// git 克隆
        /// </summary>
        /// <returns></returns>
        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);
            }
        }
        /// <summary>
        /// 克隆项目代码
        /// </summary>
        /// <param name="gitLib">git库</param>
        /// <param name="sourceDir">本地保存路径</param>
        /// <returns></returns>
        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;
            }
        }
        /// <summary>
        /// 拉取项目代码
        /// </summary>
        /// <param name="sourceDir">源码路径</param>
        /// <returns></returns>
        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 发布项目
        /// <summary>
        /// 发布项目
        /// </summary>
        /// <returns></returns>
        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;
        }
        /// <summary>
        /// 查找项目
        /// </summary>
        /// <param name="sourcePath">源码路径</param>
        /// <param name="projects">项目集</param>
        /// <returns></returns>
        static string[] GetProjectsPath(string sourcePath, string[] projects)
        {
            var paths = new List<string>();
            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 运行项目
        /// <summary>
        /// 运行项目
        /// </summary>
        /// <returns></returns>
        static int[] Run()
        {
            Console.WriteLine("开始运行");
            var sourcePath = ConfigurationManager.AppSettings["SourcePath"];
            var publishDir = $"{sourcePath}/publish";
            var proceddIDs = new List<int>();
            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配置文件

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>    
    <!--git库相关:git库路径,clone后本地何存路径-->
    <add key="GitLib" value="https://github.com/axzxs2001/Asp.NetCoreExperiment.git"/>
    <add key="SourcePath" value="e:/test/"/>
    <!--dotnet发布相关:要发布的项目-->
    <add key="PublishProject" value="AspNetCoreEnvironment.csproj,WebError.csproj"/>    
  </appSettings>
</configuration>

  这个例子很简单,只支持在windows下运行,同时run起来的应用和MyCICD是在一个进程中,一但进程关闭,run的服务也就掉了,还有很多需要改进,如果有兴趣,可以完善,比如可以跑大多个平台上(linux,docker,mac)下,也可以把MyCICD和运行的服务分离,进程间互不影响。

 

  想要更快更方便的了解相关知识,可以关注微信公众号 

 

 

 

posted @ 2022-01-31 15:47  桂素伟  阅读(46)  评论(0编辑  收藏  举报