Several Fixes and improvements.
ln.build build job pending

master
Harald Wolff 2020-11-28 01:08:49 +01:00
parent 9885e21d60
commit 8455a2dad4
8 changed files with 131 additions and 57 deletions

View File

@ -13,8 +13,7 @@ namespace ln.build.server
class Program class Program
{ {
static Pool pool = new Pool(2); static CIService CIService;
static HttpResponse WebHookRequest(HttpRoutingContext context, HttpRequest request) static HttpResponse WebHookRequest(HttpRoutingContext context, HttpRequest request)
{ {
HttpResponse response = new HttpResponse(request); HttpResponse response = new HttpResponse(request);
@ -32,28 +31,24 @@ namespace ln.build.server
{ {
try try
{ {
string repoName = message["repository"]["name"].ToNative().ToString();
string cloneUrl = message["repository"]["clone_url"].ToNative().ToString(); string cloneUrl = message["repository"]["clone_url"].ToNative().ToString();
string notifyEmail = message["pusher"]["email"].ToNative().ToString();
foreach (JSONValue commit in (message["commits"] as JSONArray).Children) foreach (JSONValue commit in (message["commits"] as JSONArray).Children)
{ {
string commitID = commit["id"].ToNative().ToString(); string commitID = commit["id"].ToNative().ToString();
Logging.Log("Received CI request of repository {0} for commit {1}", cloneUrl, commitID);
Logging.Log("Received CI request for repository {2} [{0}] for the commit {1}", cloneUrl, commitID, repoName); CIJob job = new CIJob(cloneUrl)
.SetVariable("REPO_OWNER", message["repository"]["owner"]["username"].ToNative().ToString())
CIJob job = new CIJob(repoName, cloneUrl, commitID, notifyEmail); .SetVariable("REPO_NAME", message["repository"]["name"].ToNative().ToString())
.SetVariable("COMMIT_ID", commitID)
job.SetVariable("REPO_OWNER", message["repository"]["owner"]["username"].ToNative().ToString()); .SetVariable("NOTIFY", message["pusher"]["email"].ToNative().ToString());
job.SetVariable("REPO_NAME", message["repository"]["name"].ToNative().ToString());
job.SetVariable("COMMIT_ID", commitID);
job.UpdateBuildState(BuildState.PENDING); job.UpdateBuildState(BuildState.PENDING);
pool.Enqueue(job); CIService.Enqueue(job);
} }
} catch (Exception e) } catch (Exception e)
{ {
response.StatusCode = 500; response.StatusCode = 500;
@ -74,15 +69,17 @@ namespace ln.build.server
static void Main(string[] args) static void Main(string[] args)
{ {
CommandRunner.DefaultThrow = CRThrow.NEGATIVE; CIService = new CIService();
CIService.Start();
CIService.AddPipeline(new DotNetPipeLine());
SimpleRouter genericRouter = new SimpleRouter(); SimpleRouter genericRouter = new SimpleRouter();
genericRouter.AddSimpleRoute("/", WebHookRequest); genericRouter.AddSimpleRoute("/", WebHookRequest);
HTTPServer httpServer = new HTTPServer(new Endpoint(IPv6.ANY, 1888), new LoggingRouter(genericRouter)); HTTPServer httpServer = new HTTPServer(new Endpoint(IPv6.ANY, 1888), new LoggingRouter(genericRouter));
pool.Start();
Logging.Log("Starting http listener..."); Logging.Log("Starting http listener...");
httpServer.Start(); httpServer.Start();
} }

View File

@ -6,7 +6,7 @@
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<Version>0.1.0-test</Version> <Version>0.1.0-test1</Version>
<Authors>Harald Wolff-Thobaben</Authors> <Authors>Harald Wolff-Thobaben</Authors>
<Company>l--n.de</Company> <Company>l--n.de</Company>
<Description>A simple build server scheduling builds triggered via web-hooks</Description> <Description>A simple build server scheduling builds triggered via web-hooks</Description>

View File

@ -4,6 +4,7 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Reflection; using System.Reflection;
using System.Reflection.Metadata.Ecma335;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using System.Text; using System.Text;
using ln.json; using ln.json;
@ -16,15 +17,12 @@ namespace ln.build
{ {
static HttpClient httpClient = new HttpClient(); static HttpClient httpClient = new HttpClient();
public string JobID { get; } = Guid.NewGuid().ToString("N"); public string JobID { get; } = Guid.NewGuid().ToString("N");
public string RepositoryURL { get; } public string RepositoryURL { get; }
public string RepositoryName { get; }
public string Commit { get; }
public string NotifyEMail { get; set; }
public string WorkingDirectory { get; set; } public string WorkingDirectory { get; set; }
public CIService CurrentCIService { get; set; }
public Logger Logger { get; private set; } public Logger Logger { get; private set; }
@ -34,16 +32,13 @@ namespace ln.build
Dictionary<string,MemoryStream> logStreams = new Dictionary<string, MemoryStream>(); Dictionary<string,MemoryStream> logStreams = new Dictionary<string, MemoryStream>();
Dictionary<string,Logger> logStreamLoggers = new Dictionary<string, Logger>(); Dictionary<string,Logger> logStreamLoggers = new Dictionary<string, Logger>();
public CIJob(string repositoryName, string repositoryURL, string commit, string notifyEMail) public CIJob(string repositoryURL)
{ {
WorkingDirectory = Path.Combine(Path.GetTempPath(), JobID); WorkingDirectory = Path.Combine(Path.GetTempPath(), JobID);
Logger = new Logger(new FileLogger(Path.Combine(Path.GetTempPath(), String.Format("{0}.log", JobID)))); Logger = new Logger(new FileLogger(Path.Combine(Path.GetTempPath(), String.Format("{0}.log", JobID))));
Logger.Backends.Add(Logger.ConsoleLogger); Logger.Backends.Add(Logger.ConsoleLogger);
RepositoryName = repositoryName;
RepositoryURL = repositoryURL; RepositoryURL = repositoryURL;
Commit = commit;
NotifyEMail = notifyEMail;
} }
public Stream GetLogStream(string name) public Stream GetLogStream(string name)
@ -73,12 +68,14 @@ namespace ln.build
value = defValue; value = defValue;
return value; return value;
} }
public void SetVariable(string varName,string value) public CIJob SetVariable(string varName,string value)
{ {
if (value != null) if (value != null)
variables[varName] = value; variables[varName] = value;
else else
variables.Remove(varName); variables.Remove(varName);
return this;
} }
public void ExtendVariable(string varName,string value) => ExtendVariable(varName, value, ':'); public void ExtendVariable(string varName,string value) => ExtendVariable(varName, value, ':');
public void ExtendVariable(string varName, string value, char seperator) public void ExtendVariable(string varName, string value, char seperator)
@ -93,6 +90,16 @@ namespace ln.build
SetVariable(varName, currentValue); SetVariable(varName, currentValue);
} }
public bool ContainsVariable(string varName) => variables.ContainsKey(varName);
public bool ContainsVariable(params string[] varNames)
{
foreach (string varName in varNames)
{
if (!variables.ContainsKey(varName))
return false;
}
return true;
}
public async void UpdateBuildState(BuildState buildState) public async void UpdateBuildState(BuildState buildState)
{ {
@ -110,7 +117,7 @@ namespace ln.build
stateObject.Add("state", buildState.ToString().ToLower()); stateObject.Add("state", buildState.ToString().ToLower());
stateObject.Add("target_url", JSONNull.Instance); stateObject.Add("target_url", JSONNull.Instance);
HttpResponseMessage response = await httpClient.PostAsync(buildStateURL, new StringContent(stateObject.ToString(),Encoding.UTF8,"application/json")); HttpResponseMessage response = httpClient.PostAsync(buildStateURL, new StringContent(stateObject.ToString(),Encoding.UTF8,"application/json")).Result;
Logger.Log(LogLevel.DEBUG, "UpdateBuildState({0}): {1}", buildState, buildStateURL); Logger.Log(LogLevel.DEBUG, "UpdateBuildState({0}): {1}", buildState, buildStateURL);
Logger.Log(LogLevel.DEBUG, "Response: {0}", response ); Logger.Log(LogLevel.DEBUG, "Response: {0}", response );
@ -122,39 +129,50 @@ namespace ln.build
public override void RunJob() public override void RunJob()
{ {
CloneRepository(); if (CloneRepository())
DetectPipelines();
try{
foreach (PipeLine pipeLine in pipeLines)
{
pipeLine.Run(this);
}
UpdateBuildState(BuildState.SUCCESS);
} catch (Exception e)
{ {
UpdateBuildState(BuildState.FAILURE); if (DetectPipelines())
{
UpdateBuildState(BuildState.PENDING);
try{
foreach (PipeLine pipeLine in pipeLines)
{
pipeLine.Run(this);
}
UpdateBuildState(BuildState.SUCCESS);
} catch (Exception e)
{
UpdateBuildState(BuildState.FAILURE);
}
}
} else {
Logger.Log(LogLevel.ERROR, "CIJob failed at CloneRepository()");
} }
Notify(); Notify();
Cleanup(); Cleanup();
} }
void DetectPipelines() public bool CloneRepository()
{
string[] sln = Directory.GetFileSystemEntries(WorkingDirectory,"*.sln");
if (sln.Length > 0)
{
pipeLines.Add(new DotNetPipeLine());
}
}
public void CloneRepository()
{ {
Logging.Log("cloning repository to {0}", WorkingDirectory); Logging.Log("cloning repository to {0}", WorkingDirectory);
new CommandRunner(Logger, "git", "clone",RepositoryURL,WorkingDirectory).Run(); return
new CommandRunner(Logger, "git", "checkout", Commit).Run(); (new CommandRunner(Logger, "git", "clone", RepositoryURL, WorkingDirectory).Run() == 0) &&
(!ContainsVariable("COMMIT_ID") || new CommandRunner(Logger, "git", "checkout", GetVariable("COMMIT_ID")){ WorkingDirectory = this.WorkingDirectory, }.Run() == 0);
}
public bool DetectPipelines()
{
if (CurrentCIService != null)
{
foreach (PipeLine pipeLine in CurrentCIService.PipeLines)
{
if (pipeLine.DetectValidity(this))
pipeLines.Add(pipeLine);
}
}
return pipeLines.Count > 0;
} }
public void Notify() public void Notify()

View File

@ -0,0 +1,38 @@
using System.Collections.Generic;
using ln.threading;
namespace ln.build
{
public class CIService
{
public Pool buildPool;
HashSet<PipeLine> pipelines = new HashSet<PipeLine>();
public IEnumerable<PipeLine> PipeLines => pipelines;
public CIService()
{
buildPool = new Pool(2);
}
public void Start()
{
buildPool.Start();
}
public void AddPipeline(PipeLine pipeLine)
{
pipelines.Add(pipeLine);
}
public void Enqueue(CIJob job)
{
job.CurrentCIService = this;
buildPool.Enqueue(job);
}
}
}

View File

@ -6,7 +6,6 @@ using ln.logging;
namespace ln.build namespace ln.build
{ {
public enum CRThrow { NEVER, NEGATIVE, NONZERO } public enum CRThrow { NEVER, NEGATIVE, NONZERO }
public class CommandRunner public class CommandRunner
{ {
public static CRThrow DefaultThrow { get; set; } = CRThrow.NONZERO; public static CRThrow DefaultThrow { get; set; } = CRThrow.NONZERO;
@ -18,12 +17,17 @@ namespace ln.build
public Logger Logger { get; } public Logger Logger { get; }
Func<int,bool> TestExitCode = null;
public CommandRunner(string executable,params string[] arguments) : this(Logger.Default, executable, arguments){} public CommandRunner(string executable,params string[] arguments) : this(Logger.Default, executable, arguments){}
public CommandRunner(Logger logger, string executable,params string[] arguments) public CommandRunner(Logger logger, string executable,params string[] arguments) : this(logger, null, executable, arguments){}
public CommandRunner(Logger logger, Func<int,bool> testExitCode, string executable,params string[] arguments)
{ {
Logger = logger; Logger = logger;
Executable = executable; Executable = executable;
Arguments = arguments; Arguments = arguments;
TestExitCode = testExitCode;
} }
public string FindFileInPath(string filename) public string FindFileInPath(string filename)
@ -68,6 +72,7 @@ namespace ln.build
logger.Log(LogLevel.INFO, "Result: {0}", process.ExitCode); logger.Log(LogLevel.INFO, "Result: {0}", process.ExitCode);
if ( if (
((TestExitCode != null) && !TestExitCode(process.ExitCode)) ||
((Throw == CRThrow.NEGATIVE) && (process.ExitCode < 0)) || ((Throw == CRThrow.NEGATIVE) && (process.ExitCode < 0)) ||
((Throw == CRThrow.NONZERO) && (process.ExitCode != 0)) ((Throw == CRThrow.NONZERO) && (process.ExitCode != 0))
) )

View File

@ -2,6 +2,7 @@
using System; using System;
using System.IO; using System.IO;
using System.Reflection.Metadata.Ecma335; using System.Reflection.Metadata.Ecma335;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Runtime.Serialization.Formatters; using System.Runtime.Serialization.Formatters;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
@ -33,7 +34,19 @@ namespace ln.build
AddStep(new PublishStep()); AddStep(new PublishStep());
} }
public override bool DetectValidity(CIJob job)
{
string[] sln = Directory.GetFileSystemEntries(job.WorkingDirectory,"*.sln");
if (sln.Length > 0)
return true;
sln = Directory.GetFileSystemEntries(job.WorkingDirectory,"*.csproj");
if (sln.Length > 0)
return true;
return false;
}
void FilterPackageFilenames(CommandStep commandStep, CIJob job, int exitCode) void FilterPackageFilenames(CommandStep commandStep, CIJob job, int exitCode)
{ {
Stream outStream = job.GetLogStream(commandStep.DefaultLogFileName); Stream outStream = job.GetLogStream(commandStep.DefaultLogFileName);

View File

@ -8,7 +8,7 @@ using ln.logging;
namespace ln.build namespace ln.build
{ {
public class PipeLine public abstract class PipeLine
{ {
List<Step> steps = new List<Step>(); List<Step> steps = new List<Step>();
@ -17,6 +17,9 @@ namespace ln.build
{ {
} }
public abstract bool DetectValidity(CIJob job);
public void AddStep(Step step) => steps.Add(step); public void AddStep(Step step) => steps.Add(step);
public virtual void Run(CIJob job) public virtual void Run(CIJob job)

View File

@ -5,7 +5,7 @@
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<Version>0.1.0</Version> <Version>0.1.0-test1</Version>
<Authors>Harald Wolff-Thobaben</Authors> <Authors>Harald Wolff-Thobaben</Authors>
<Company>l--n.de</Company> <Company>l--n.de</Company>
<Description>A simple build server scheduling builds triggered via web-hooks</Description> <Description>A simple build server scheduling builds triggered via web-hooks</Description>