天天看點

持續傳遞三:動手自動化“開發”—>“測試”

持續傳遞三:動手自動化“開發”—>“測試”

  前兩篇博文中提到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和運作的服務分離,程序間互不影響。

  想要更快更友善的了解相關知識,可以關注微信公衆号