using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Net; using System.Net.NetworkInformation; using ln.application; using ln.build.commands; using ln.build.pipeline; using ln.build.repositories; using ln.build.secrets; using ln.build.semver; using ln.http; using ln.http.router; using ln.json; using ln.logging; using ln.templates.html; using ln.templates.http; using ln.threading; using ln.type; using Microsoft.VisualBasic; namespace ln.build { public class CIService { [StaticArgument( LongOption = "context-directory")] public string ContextDirectory { get; set; } = AppContext.BaseDirectory; [StaticArgument( Option = 'b', LongOption = "base-directory")] public string BaseDirectory { get; set; } [StaticArgument( Option = 'u', LongOption = "base-url")] public string BaseURL { get; set; } [StaticArgument( Option = 'h', LongOption = "hostname")] public string HostName { get; set; } public Pool buildPool; public string ReportsDirectory { get; set; } Logger webLogger; SimpleRouter httpRouter; SimpleRouter hookRouter; StaticRouter staticsRouter; TemplateRouter templateRouter; HTTPServer httpServer; public StageCommandContainer StageCommands { get; } = new StageCommandContainer(null,null); public CIService() { buildPool = new Pool(2); } public CIService(string baseDirectory) : this() { BaseDirectory = baseDirectory; ReportsDirectory = Path.Combine(baseDirectory, "builds" ); } public void Initialize() { BaseDirectory ??= ContextDirectory; ReportsDirectory ??= Path.Combine(BaseDirectory, "builds" ); ReportsDirectory = Path.GetFullPath(ReportsDirectory); Logging.Log(LogLevel.INFO, "Commandline: {0}", Environment.CommandLine); Logging.Log(LogLevel.INFO, "Working Directory: {0}", Environment.CurrentDirectory); Logging.Log(LogLevel.INFO, "Base Directory: {0}", BaseDirectory); Logging.Log(LogLevel.INFO, "Reports Directory: {0}", ReportsDirectory); Directory.CreateDirectory(ReportsDirectory); Directory.CreateDirectory(Path.Combine(BaseDirectory, "secrets")); InitializeStageCommands(); InitializeHttpServer(); } private void InitializeHttpServer() { if (HostName == null) HostName = FindHostName(); if (BaseURL == null) BaseURL = string.Format("http://{0}:{1}", HostName, 1888); Logging.Log(LogLevel.DEBUG, "Hostname: {0}", HostName); Logging.Log(LogLevel.DEBUG, "BaseURL: {0}", BaseURL); httpRouter = new SimpleRouter(); httpRouter.AddSimpleRoute("/builds/*", new StaticRouter(ReportsDirectory)); hookRouter = new SimpleRouter(); staticsRouter = new StaticRouter(Path.Combine(ContextDirectory, "html", "static")); templateRouter = new TemplateRouter(new FileSystemTemplateSource(Path.Combine(ContextDirectory, "html", "documents"), false)); templateRouter.DefaultTemplatePath = "index.html"; templateRouter.OnPrepareRenderContext += (templateRouter, templateDocument, renderContext) => { renderContext.SetScriptObject("CIService", this); }; httpRouter.AddSimpleRoute("/hooks/*", hookRouter); httpRouter.AddSimpleRoute("/builds/:buildid/*", templateRouter); httpRouter.AddSimpleRoute("/builds/:buildid", templateRouter); httpRouter.AddSimpleRoute("/*", staticsRouter); webLogger = new Logger(Logger.ConsoleLogger); webLogger.Backends.Add(new FileLogger("ln.build.http.log")); httpServer = new HTTPServer(new LoggingRouter(httpRouter, webLogger)); foreach (IPAddress _ip in Dns.GetHostAddresses(HostName)) { IPv6 ip6 = _ip; httpServer.AddEndpoint(new Endpoint(ip6, 1888)); } } public void InitializeStageCommands() { StageCommands.AddCommand(CoreCommands.ShellCommand, "sh"); StageCommands.AddCommand(DotNetCommand.Prepare, "dotnet", "prepare" ); StageCommands.AddCommand(DotNetCommand.Build, "dotnet", "build" ); StageCommands.AddCommand(DotNetCommand.Test, "dotnet", "test" ); StageCommands.AddCommand(DotNetCommand.Pack, "dotnet", "pack" ); StageCommands.AddCommand(DotNetCommand.Push, "dotnet", "push" ); StageCommands.AddCommand(DotNetCommand.Publish, "dotnet", "publish" ); StageCommands.AddCommand(DeployCommand.Deploy, "deploy" ); StageCommands.AddCommand(DeployCommand.Release, "release" ); } public string GetJobURL(CIJob job) => string.Format("{0}/builds/{1}", BaseURL, job.JobID); public void Start() { buildPool.Start(); httpServer.Start(); } public void AddWebHookHandler(string name, Func webHookHandler) { hookRouter.AddSimpleRoute(String.Format("/{0}", name), (HttpRoutingContext context,HttpRequest request) => webHookHandler(this, request)); } public SecretStorage GetSecretStorage(string secretName) => new SecretStorage(Path.Combine(BaseDirectory,"secrets", String.Format("{0}.json", secretName))); public void Enqueue(CIJob job) { buildPool.Enqueue(job); } public void CreateReport(CIJob job) { JSONObject report = new JSONObject(); JSONArray steps = new JSONArray(); report["steps"] = steps; report["state"] = new JSONString(job.BuildState.ToString()); string reportDirectory = Path.Combine(ReportsDirectory, job.JobID); Directory.CreateDirectory(reportDirectory); foreach (string logName in job.GetLogStreamNames()) { string logFileName = Path.Combine(reportDirectory, logName); using (FileStream fileStream = new FileStream(logFileName, FileMode.CreateNew)) { job.GetLogStream(logName).Position = 0; job.GetLogStream(logName).CopyTo(fileStream); fileStream.Flush(); fileStream.Close(); } steps.Add(new JSONString(logName)); } using (StreamWriter writer = new StreamWriter(Path.Combine(reportDirectory, "build.json"))) writer.Write(report.ToString()); } public string FindHostName() { return Dns.GetHostEntry("LocalHost").HostName; /* string hostName = IPGlobalProperties.GetIPGlobalProperties().HostName; string domainName = IPGlobalProperties.GetIPGlobalProperties().DomainName; if (!hostName.EndsWith(domainName)) { hostName = string.Format("{0}.{1}", hostName, domainName); } return hostName; */ } } }