快速打造一个MINI自动发布系统

dotNET跨平台 今天

以下文章来源于桂迹 ,作者桂素伟

前情提要:因为项目特点,需要在自己的服务器上集成测试,而不是用github的DevOpt体系;再有就是服务器是windows的;项目仓库在github上;并且项目是asp.net core的项目;开发人员一枚。以前的做法就是发布后,把执行码复制在服务器上启动;后来就是在服务器写了个bat,运行bat,完成clone项目,发布项目,运行项项目;再后台就是写了个web服务,让github项目感知到推送后,通知web服务,web服务执行这个bat。

在这里面,有几个要素:

  • bat:完成代码clone,发布,运行(其实真实中还要完成对运行的项目进行关闭,清理等工作,以方便展开最新一次的clone,发布,运行)
  • web服务
  • github通知配置

先看bat,命令如下,就是关闭运行中的项目,清理目录,clone项目,进入项目目录,发布项目,然后运行项目

    taskkill /f /im 项目名称.exetimeout 1 >NUL@RD /S /Q "C:\项目名称\项目目录"cd  C:\项目名称git clone https://用户名:密码@github.com/用户名/项目名称.gitcd  C:\项目名称\项目目录dotnet publish -o C:\项目名称\pubtimeout 1>NULcd C:\项目名称\pub项目名称.exe

    再看一下web服务,就是等待github通知,收到通知后,获取key参数,然后验证这个调用通知是否有效(最好和https),再调用bat

    appsettings.json

    projects的配置目的是实现一个web服务,可以接收多个项目的通知,然后针对不对的项目进行发布和运行,当github通知的时间,会在参数key中告诉web服务要执行那个project,这里与web服务的key参数结合使用。
      {  "urls": "http://*:6789",  "Logging": {    "LogLevel": {      "Default": "Information",      "Microsoft": "Warning",      "Microsoft.Hosting.Lifetime": "Information"    }  },  "AllowedHosts": "*",  "Projects": [    {      "key": "memall",      "BATPath": "C://MeMall/MeMall_Start.bat",      "Secret": "e0f0d18348fbcb090fa17f9fbc638a8be56be3ab"    }  ]}

      homecontrolloer.cs文件

      其中IsGitHubSignatureSHA1和 IsGitHubSignatureSHA256是两种验证方式,可以选其中的一种进行验证就可以,验证成功才能执行bat,这里同时用两种验证方式,只是为了演示而以。

        using Microsoft.AspNetCore.Mvc;using Microsoft.Extensions.Configuration;using Microsoft.Extensions.Logging;using Microsoft.Extensions.Primitives;using System;using System.Collections.Generic;using System.Diagnostics;using System.IO;using System.Linq;using System.Security.Cryptography;using System.Text;using System.Text.RegularExpressions;using System.Threading.Tasks;namespace GitHubNotice.Controllers{    [ApiController]    [Route("[controller]")]    public class HomeController : ControllerBase    {        private readonly IEnumerable<Project> _projects;        private readonly ILogger<HomeController> _logger;        public HomeController(ILogger<HomeController> logger, IConfiguration configuration)        {            _projects = configuration.GetSection("Projects").Get<List<Project>>();            _logger = logger;        }        [HttpPost("/")]        public async Task<IActionResult> Notice()        {            _logger.LogInformation($"应用收到github Notice");                 Request.Headers.TryGetValue("X-GitHub-Delivery", out StringValues gitHubDeliveryId);            Request.Headers.TryGetValue("X-GitHub-Event", out StringValues gitHubEvent);            _logger.LogInformation($"收到github通知事务:X-GitHub-Delivery:{gitHubDeliveryId},X-GitHub-Event:{gitHubEvent}");            Request.Headers.TryGetValue("X-Hub-Signature", out StringValues gitHubSignature);            Request.Headers.TryGetValue("X-Hub-Signature-256", out StringValues gitHubSignature256);            var reader = new StreamReader(Request.Body, Encoding.UTF8);            var bodyContent = await reader.ReadToEndAsync();            var key = GetMark(bodyContent);            var rgx = new Regex(@"^[a-zA-Z]+$");            if (!string.IsNullOrWhiteSpace(key) && !rgx.IsMatch(key))            {                _logger.LogError($"key={key} 错误");                return BadRequest();            }            else            {                var project = _projects.SingleOrDefault(s => s.Key == key);                if (project != null)                {                    var resultSHA256 = IsGitHubSignatureSHA256(project.Secret, bodyContent, gitHubSignature256);                    var resultSHA1 = IsGitHubSignatureSHA1(project.Secret, bodyContent, gitHubSignature);                    _logger.LogInformation($"SHA1={resultSHA1},SHA256={resultSHA256}");                    if (resultSHA1 && resultSHA256)                    {                        var p = new Process();                        p.StartInfo.CreateNoWindow = true;                        p.StartInfo.UseShellExecute = true;                        p.StartInfo.FileName = project.BATPath;                        p.Start();                        p.Close();                        _logger.LogInformation($"github通知成功");                        return Ok();                    }                    else                    {                        _logger.LogError("认证错误");                        return Unauthorized();                    }                }                else                {                    _logger.LogError($"检查配置文件Projects是否与github中的Payload URL相匹配");                    return BadRequest();                }            }        }        /// <summary>        /// 获取匹配项目的key        /// </summary>        /// <param name="text"></param>        /// <returns></returns>        string GetMark(string text)        {            var arry = text.Split('&');            foreach (var item in arry)            {                if (item.Contains("key="))                {                    return item.Split('=')[1];                }            }            return "";        }        /// <summary>        /// sha1        /// </summary>        /// <param name="seckey"></param>        /// <param name="bodyContent"></param>        /// <param name="signatureSHA1"></param>        /// <returns></returns>        static bool IsGitHubSignatureSHA1(string seckey, string bodyContent, string signatureSHA1)        {            if (string.IsNullOrWhiteSpace(bodyContent))                throw new ArgumentNullException(nameof(bodyContent));            if (string.IsNullOrWhiteSpace(signatureSHA1))                throw new ArgumentNullException(nameof(signatureSHA1));            var signature = signatureSHA1.Replace("sha1=", "");            var secret = Encoding.ASCII.GetBytes(seckey);            var payloadBytes = Encoding.ASCII.GetBytes(bodyContent);            using (var hmacsha1 = new HMACSHA1(secret))            {                var hash = hmacsha1.ComputeHash(payloadBytes);                var hashString = ToHexString(hash);                if (hashString.Equals(signature))                    return true;            }            return false;            static string ToHexString(byte[] bytes)            {                var builder = new StringBuilder(bytes.Length * 2);                foreach (byte b in bytes)                {                    builder.AppendFormat("{0:x2}", b);                }                return builder.ToString();            }        }        /// <summary>        /// X-Hub-Signature-256        /// </summary>        /// <param name="secret"></param>        /// <param name="bodyContent"></param>        /// <param name="signatureSHA256"></param>        /// <returns></returns>        static bool IsGitHubSignatureSHA256(string secret, string bodyContent, string signatureSHA256)        {            if (string.IsNullOrWhiteSpace(bodyContent))                throw new ArgumentNullException(nameof(bodyContent));            if (string.IsNullOrWhiteSpace(signatureSHA256))                throw new ArgumentNullException(nameof(signatureSHA256));            var secretBytes = Encoding.UTF8.GetBytes(secret);            var hasher = new HMACSHA256(secretBytes);            var data = Encoding.UTF8.GetBytes(bodyContent);            var computedSignature = BitConverter.ToString(hasher.ComputeHash(data)).Replace("-", "").ToLower();            return computedSignature == signatureSHA256.Replace("sha256=", "");        }    }    /// <summary>    /// 项目实体    /// </summary>    public class Project    {        public string Key { get; set; }        public string BATPath { get; set; }        public string Secret { get; set; }    }}

        最后就是github的Webhooks,通过配置,当有人推送代码时,会通知web服务,方便进行拉取构建。

        其实这就是一个极其简单,代替手工发布的脚本级别工具,特点就是小巧,灵活,简单,可以随心所欲的改;如果是大团队,或更多的要求,比如集成测试,打包镜像等,可以选用比较成熟的产品(可以搜索CICD工具,这里不作广告了)来使用,相对来说也需要学习成本。

        (0)

        相关推荐