diff --git a/src/admin/Bootstrap.Admin/wwwroot/css/lte.css b/src/admin/Bootstrap.Admin/wwwroot/css/lte.css index a9cc8dfc..cdb9da1f 100644 --- a/src/admin/Bootstrap.Admin/wwwroot/css/lte.css +++ b/src/admin/Bootstrap.Admin/wwwroot/css/lte.css @@ -33,14 +33,6 @@ transform: none; } -.header .badge { - top: 4px; -} - -.userinfo .badge { - top: 64px; -} - .username { font-size: 1rem; vertical-align: middle; @@ -109,6 +101,12 @@ margin-right: 10px; } + .userinfo .dropdown-item a .badge { + top: 74px; + left: auto; + right: 42px; + } + .userinfo .dropdown-item:last-child { border-bottom: none; } diff --git a/src/admin/Bootstrap.Admin/wwwroot/css/theme.css b/src/admin/Bootstrap.Admin/wwwroot/css/theme.css index e6960edd..5c88beec 100644 --- a/src/admin/Bootstrap.Admin/wwwroot/css/theme.css +++ b/src/admin/Bootstrap.Admin/wwwroot/css/theme.css @@ -6,7 +6,6 @@ body { color: #797979; background: #f1f2f7; - text-rendering: optimizeLegibility; -webkit-font-smoothing: antialiased; -webkit-overflow-scrolling: touch; } diff --git a/src/admin/Bootstrap.DataAccess/Bootstrap.DataAccess.csproj b/src/admin/Bootstrap.DataAccess/Bootstrap.DataAccess.csproj index c89d9988..5f417dee 100644 --- a/src/admin/Bootstrap.DataAccess/Bootstrap.DataAccess.csproj +++ b/src/admin/Bootstrap.DataAccess/Bootstrap.DataAccess.csproj @@ -6,7 +6,8 @@ - + + diff --git a/src/client/Bootstrap.Client.DataAccess/Bootstrap.Client.DataAccess.csproj b/src/client/Bootstrap.Client.DataAccess/Bootstrap.Client.DataAccess.csproj index 345399e4..0948ceb5 100644 --- a/src/client/Bootstrap.Client.DataAccess/Bootstrap.Client.DataAccess.csproj +++ b/src/client/Bootstrap.Client.DataAccess/Bootstrap.Client.DataAccess.csproj @@ -6,8 +6,8 @@ - - + + diff --git a/src/client/Bootstrap.Client/Controllers/Api/CaptchaController.cs b/src/client/Bootstrap.Client/Controllers/Api/CaptchaController.cs index 8695b0aa..fc58d363 100644 --- a/src/client/Bootstrap.Client/Controllers/Api/CaptchaController.cs +++ b/src/client/Bootstrap.Client/Controllers/Api/CaptchaController.cs @@ -24,7 +24,7 @@ namespace Bootstrap.Client.Controllers.Api var sum = datas.Sum(); var avg = sum * 1.0 / datas.Count; var stddev = datas.Select(v => Math.Pow(v - avg, 2)).Sum() / datas.Count; - return stddev != 0; + return stddev > 0; } } } diff --git a/src/client/Bootstrap.Client/Controllers/Api/DeployController.cs b/src/client/Bootstrap.Client/Controllers/Api/DeployController.cs new file mode 100644 index 00000000..d12f9ab2 --- /dev/null +++ b/src/client/Bootstrap.Client/Controllers/Api/DeployController.cs @@ -0,0 +1,25 @@ +using Bootstrap.Client.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace Bootstrap.Client.Controllers.Api +{ + /// + /// 自动发布 WebApi 接口 + /// + [Route("api/[controller]")] + [ApiController] + [AllowAnonymous] + public class DeployController : ControllerBase + { + /// + /// 自动发布 webhook 接口 + /// + /// + [HttpPost] + public void Post([FromBody]GiteePushEventArgs args) + { + DeployTaskManager.Add(args); + } + } +} diff --git a/src/client/Bootstrap.Client/Controllers/HomeController.cs b/src/client/Bootstrap.Client/Controllers/HomeController.cs index 645df249..c837e0b8 100644 --- a/src/client/Bootstrap.Client/Controllers/HomeController.cs +++ b/src/client/Bootstrap.Client/Controllers/HomeController.cs @@ -1,4 +1,5 @@ using Bootstrap.Client.Models; +using Bootstrap.Client.Tasks; using Longbow.Configuration; using Microsoft.AspNetCore; using Microsoft.AspNetCore.Authentication.Cookies; @@ -10,28 +11,30 @@ using System; namespace Bootstrap.Client.Controllers { /// - /// + /// 前台主页控制器 /// public class HomeController : Controller { /// - /// + /// 默认视图 /// /// public IActionResult Index() { return View(new NavigatorBarModel(this)); } + /// - /// + /// About 视图 /// /// public IActionResult About() { return View(new NavigatorBarModel(this)); } + /// - /// + /// 错误视图 /// /// /// diff --git a/src/client/Bootstrap.Client/Startup.cs b/src/client/Bootstrap.Client/Startup.cs index 60cf7bdc..39e34eec 100644 --- a/src/client/Bootstrap.Client/Startup.cs +++ b/src/client/Bootstrap.Client/Startup.cs @@ -56,11 +56,8 @@ namespace Bootstrap.Client services.AddResponseCompression(); services.AddBootstrapAdminAuthentication(); services.AddAuthorization(options => options.DefaultPolicy = new AuthorizationPolicyBuilder().RequireBootstrapAdminAuthorizate().Build()); - services.AddControllersWithViews(options => - { - options.Filters.Add(); - options.Filters.Add>(); - }).AddJsonOptions(op => op.JsonSerializerOptions.AddDefaultConverters()); + services.AddControllersWithViews(options => options.Filters.Add()).AddJsonOptions(op => op.JsonSerializerOptions.AddDefaultConverters()); + services.AddAutoPublish(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. diff --git a/src/client/Bootstrap.Client/Tasks/DeployExtensions.cs b/src/client/Bootstrap.Client/Tasks/DeployExtensions.cs new file mode 100644 index 00000000..553286c6 --- /dev/null +++ b/src/client/Bootstrap.Client/Tasks/DeployExtensions.cs @@ -0,0 +1,21 @@ +using Bootstrap.Client.Tasks; + +namespace Microsoft.Extensions.DependencyInjection +{ + /// + /// 自动发布服务扩展操作类 + /// + public static class DeployExtensions + { + /// + /// 注入自动发布到容器内 + /// + /// + /// + public static IServiceCollection AddAutoPublish(this IServiceCollection services) + { + DeployTaskManager.RegisterServices(services); + return services; + } + } +} diff --git a/src/client/Bootstrap.Client/Tasks/DeployOptions.cs b/src/client/Bootstrap.Client/Tasks/DeployOptions.cs new file mode 100644 index 00000000..9723d398 --- /dev/null +++ b/src/client/Bootstrap.Client/Tasks/DeployOptions.cs @@ -0,0 +1,28 @@ +namespace Bootstrap.Client.Tasks +{ + /// + /// 自动发布配置类 + /// + public class DeployOptions + { + /// + /// 获得/设置 是否启用自动部署功能 + /// + public bool Enabled { get; set; } + + /// + /// 获得/设置 自动部署脚本文件 + /// + public string DeployFile { get; set; } + + /// + /// 获得/设置 自动部署分支 + /// + public string Branch { get; set; } = "release"; + + /// + /// 获得/设置 自动部署平台 + /// + public string OSPlatform { get; set; } = "Windows"; + } +} diff --git a/src/client/Bootstrap.Client/Tasks/DeployTaskManager.cs b/src/client/Bootstrap.Client/Tasks/DeployTaskManager.cs new file mode 100644 index 00000000..ce492cd6 --- /dev/null +++ b/src/client/Bootstrap.Client/Tasks/DeployTaskManager.cs @@ -0,0 +1,112 @@ +using Bootstrap.Client.Controllers.Api; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Concurrent; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Threading.Tasks; + +namespace Bootstrap.Client.Tasks +{ + /// + /// 发布任务管理操作类 + /// + public static class DeployTaskManager + { + private static BlockingCollection _pool = new BlockingCollection(new ConcurrentQueue()); + private static IServiceCollection _services; + + /// + /// IServiceCollection 实例 + /// + /// + internal static void RegisterServices(IServiceCollection services) => _services = services; + + /// + /// 添加自动发布任务到队列中 + /// + /// + public static void Add(GiteePushEventArgs args) + { + // 判断是否需要自动发布 + var sp = _services.BuildServiceProvider(); + var config = sp.GetRequiredService(); + var logger = sp.GetRequiredService>(); + var option = config.GetSection().Get(); + if (option.Enabled && !string.IsNullOrEmpty(option.DeployFile)) + { + if (!_pool.IsAddingCompleted) + { + _pool.Add(args); + } + + RunAsync(logger, option).ConfigureAwait(false); + } + } + + private static bool _running; + private static object _locker = new object(); + + private static async Task RunAsync(ILogger logger, DeployOptions options) + { + if (!_running) + { + await Task.Run(() => + { + // 线程等待防止多个部署任务同时执行 + lock (_locker) + { + if (!_running) + { + _running = true; + while (_pool.TryTake(out var args)) + { + // 分析提交分支 + if (args.Ref.SpanSplit("/").LastOrDefault().Equals(options.Branch, StringComparison.OrdinalIgnoreCase) && CanDeploy(options.OSPlatform)) + { + // 仅部署配置分支代码 + Deploy(logger, options.DeployFile); + } + } + } + } + _running = false; + }); + } + } + + private static bool CanDeploy(string osPlatform) + { + var os = OSPlatform.Create(osPlatform); + return !RuntimeInformation.IsOSPlatform(os); + } + + private static void Deploy(ILogger logger, string deployFile) + { + // 调用部署脚本 + try + { + //var psi = new ProcessStartInfo("sh", "~/BootstrapAdmin/deploy-admin.sh"); + var cmd = deployFile; + if (File.Exists(cmd)) + { + var psi = new ProcessStartInfo("sh", $"\"{cmd}\""); + psi.RedirectStandardOutput = true; + psi.UseShellExecute = false; + var p = Process.Start(psi); + p.WaitForExit(); + var result = p.StandardOutput.ReadToEnd(); + logger.LogError("deploy success: {0}", result); + } + } + catch (Exception ex) + { + logger.LogError(ex, ""); + } + } + } +} diff --git a/src/client/Bootstrap.Client/Tasks/GiteePushEventArgs.cs b/src/client/Bootstrap.Client/Tasks/GiteePushEventArgs.cs new file mode 100644 index 00000000..9bd32785 --- /dev/null +++ b/src/client/Bootstrap.Client/Tasks/GiteePushEventArgs.cs @@ -0,0 +1,88 @@ +using System; +using System.Collections.Generic; + +namespace Bootstrap.Client.Tasks +{ + /// + /// Gitee 提交事件参数实体类 + /// + public class GiteePushEventArgs + { + /// + /// 获得/设置 提交分支信息 + /// + public string Ref { get; set; } + + /// + /// 获得/设置 提交信息集合 + /// + public ICollection Commits { get; set; } = new HashSet(); + + /// + /// 获得/设置 提交信息数量 + /// + public int Total_Commits_Count { get; set; } + } + + /// + /// 获得/设置 提交信息实体类 + /// + public class GiteeCommit + { + /// + /// 获得/设置 提交消息 + /// + public string Message { get; set; } + + /// + /// 获得/设置 提交时间戳 + /// + public DateTimeOffset Timestamp { get; set; } + + /// + /// 获得/设置 提交地址 + /// + public string Url { get; set; } + + /// + /// 获得/设置 提交作者 + /// + public GiteeAuthor Author { get; set; } + } + + /// + /// 获得/设置 提交作者信息 + /// + public class GiteeAuthor + { + /// + /// 获得/设置 提交时间 + /// + public DateTimeOffset Time { get; set; } + + /// + /// 获得/设置 提交人 ID + /// + public int Id { get; set; } + + /// + /// 获得/设置 提交人名称 + /// + public string Name { get; set; } + + /// + /// 获得/设置 提交人邮件地址 + /// + public string Email { get; set; } + + /// + /// 获得/设置 提交人名称 + /// + public string UserName { get; set; } + + /// + /// 获得/设置 提交人 Gitee 地址 + /// + public string Url { get; set; } + } +} diff --git a/src/client/Bootstrap.Client/appsettings.Development.json b/src/client/Bootstrap.Client/appsettings.Development.json index 6bc5d4c7..75fdb315 100644 --- a/src/client/Bootstrap.Client/appsettings.Development.json +++ b/src/client/Bootstrap.Client/appsettings.Development.json @@ -46,6 +46,12 @@ "AuthHost": "http://localhost:50852", "KeyPath": "..\\..\\admin\\keys" }, + "DeployOptions": { + "Enabled": false, + "DeployFile": "..\\..\\publish-admin.sh", + "OSPlatform": "Windows", + "Branch": "release" + }, "AllowOrigins": "http://localhost:50852", "LongbowCache": { "Enabled": true, diff --git a/src/client/Bootstrap.Client/appsettings.json b/src/client/Bootstrap.Client/appsettings.json index 9597da70..25edaef5 100644 --- a/src/client/Bootstrap.Client/appsettings.json +++ b/src/client/Bootstrap.Client/appsettings.json @@ -41,6 +41,12 @@ "BootstrapAdminAuthenticationOptions": { "AuthHost": "http://argo.zylweb.cn/BA" }, + "DeployOptions": { + "Enabled": false, + "DeployFile": "..\\..\\..\\..\\publish.ps1", + "OSPlatform": "Windows", + "Branch": "release" + }, "AllowOrigins": "http://localhost,http://argo.zylweb.cn", "LongbowCache": { "Enabled": true, diff --git a/src/client/Bootstrap.Client/wwwroot/css/lte.css b/src/client/Bootstrap.Client/wwwroot/css/lte.css index b1bc3668..680f910e 100644 --- a/src/client/Bootstrap.Client/wwwroot/css/lte.css +++ b/src/client/Bootstrap.Client/wwwroot/css/lte.css @@ -35,14 +35,6 @@ transform: none; } -.header .badge { - top: 4px; -} - -.userinfo .badge { - top: 64px; -} - .username { font-size: 1rem; vertical-align: middle; @@ -111,6 +103,12 @@ margin-right: 10px; } + .userinfo .dropdown-item a .badge { + top: 74px; + left: auto; + right: 42px; + } + .userinfo .dropdown-item:last-child { border-bottom: none; } diff --git a/src/client/Bootstrap.Client/wwwroot/css/theme.css b/src/client/Bootstrap.Client/wwwroot/css/theme.css index 2254c606..10aac221 100644 --- a/src/client/Bootstrap.Client/wwwroot/css/theme.css +++ b/src/client/Bootstrap.Client/wwwroot/css/theme.css @@ -6,7 +6,6 @@ body { color: #797979; background: #f1f2f7; - text-rendering: optimizeLegibility; -webkit-font-smoothing: antialiased; -webkit-overflow-scrolling: touch; }