parent
deebca6a05
commit
21b53b137c
2
build.ln
2
build.ln
|
@ -39,7 +39,7 @@
|
|||
{
|
||||
"name": "push",
|
||||
"commands": [
|
||||
"SH dotnet nuget push .build/ln.build.*.nupkg -s $NUGET_SOURCE -k $NUGET_APIKEY",
|
||||
"SH for NUPKG in .build/ln.build.*.nupkg; do dotnet nuget push $NUPKG -s $NUGET_SOURCE -k $NUGET_APIKEY; done",
|
||||
"RELEASE .build/linux-x64/ln.build.server=ln.build.server-linux-amd64"
|
||||
],
|
||||
"secrets": {
|
||||
|
|
|
@ -22,6 +22,9 @@ namespace ln.build.server
|
|||
[StaticArgument( LongOption = "build")]
|
||||
public static string BuildPath { get; set; }
|
||||
|
||||
[StaticArgument( LongOption = "build-secret")]
|
||||
public static string BuildSecret { get; set; }
|
||||
|
||||
static void Main(string[] args)
|
||||
{
|
||||
ArgumentContainer ac = new ArgumentContainer(typeof(Program));
|
||||
|
@ -34,7 +37,7 @@ namespace ln.build.server
|
|||
|
||||
if (BuildPath != null)
|
||||
{
|
||||
CIJob job = new CIJob(CIService,null);
|
||||
CIJob job = new CIJob(CIService,null, (BuildSecret != null) ? CIService.GetSecretStorage(BuildSecret) : null);
|
||||
job.WorkingDirectory = BuildPath;
|
||||
job.RunJob();
|
||||
} else {
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<Version>0.3.0</Version>
|
||||
<Version>0.4.0</Version>
|
||||
<Authors>Harald Wolff-Thobaben</Authors>
|
||||
<Company>l--n.de</Company>
|
||||
<Description>A simple build server scheduling builds triggered via web-hooks</Description>
|
||||
|
|
|
@ -40,6 +40,7 @@ namespace ln.build
|
|||
:this(ciService, repository)
|
||||
{
|
||||
SecretStorage = secretStorage;
|
||||
Environment.SecretStorage = secretStorage;
|
||||
}
|
||||
public CIJob(CIService ciService, Repository repository)
|
||||
{
|
||||
|
@ -52,7 +53,7 @@ namespace ln.build
|
|||
Logger = new Logger(new FileLogger(Path.Combine(ciService.ReportsDirectory, JobID, "build.log")));
|
||||
Logger.Backends.Add(Logger.ConsoleLogger);
|
||||
|
||||
Environment = new CommandEnvironment();
|
||||
Environment = new CommandEnvironment(){ CIJob = this };
|
||||
}
|
||||
|
||||
public Stream GetLogStream(string name)
|
||||
|
|
|
@ -6,6 +6,7 @@ using System.Net;
|
|||
using System.Net.NetworkInformation;
|
||||
using ln.application;
|
||||
using ln.build.repositories;
|
||||
using ln.build.secrets;
|
||||
using ln.http;
|
||||
using ln.http.router;
|
||||
using ln.json;
|
||||
|
@ -131,6 +132,8 @@ namespace ln.build
|
|||
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)
|
||||
{
|
||||
|
|
|
@ -47,12 +47,30 @@ namespace ln.build
|
|||
}
|
||||
return httpResponse.StatusCode;
|
||||
}
|
||||
|
||||
|
||||
public HttpStatusCode PostContent(HttpContent content, out JSONValue response,params string[] path)
|
||||
{
|
||||
HttpResponseMessage httpResponse = HttpClient.PostAsync(string.Format("{0}/{1}",BaseURL, string.Join('/', path)),content).Result;
|
||||
if (httpResponse.StatusCode == System.Net.HttpStatusCode.Created)
|
||||
{
|
||||
response = JSONParser.Parse(httpResponse.Content.ReadAsStringAsync().Result);
|
||||
} else {
|
||||
response = null;
|
||||
Logging.Log(LogLevel.DEBUG, "failed URL: {0}", httpResponse.RequestMessage.RequestUri);
|
||||
Logging.Log(LogLevel.DEBUG, "httpResponse; {0}", httpResponse);
|
||||
}
|
||||
return httpResponse.StatusCode;
|
||||
}
|
||||
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
HttpClient?.Dispose();
|
||||
}
|
||||
|
||||
static JsonApiClient(){
|
||||
System.Net.ServicePointManager.Expect100Continue = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -29,6 +29,9 @@ namespace ln.build.commands
|
|||
|
||||
SecretStorage secretStorage;
|
||||
public SecretStorage SecretStorage { get => secretStorage ?? Parent?.SecretStorage; set => secretStorage = value; }
|
||||
|
||||
CIJob cIJob;
|
||||
public CIJob CIJob { get => cIJob ?? Parent?.CIJob ; set => cIJob = value; }
|
||||
|
||||
public CommandEnvironment(CommandEnvironment parent) : this(parent.Logger) { Parent = parent; WorkingDirectory = parent.WorkingDirectory; }
|
||||
public CommandEnvironment(CommandEnvironment parent, Logger logger) : this(logger) { Parent = parent; WorkingDirectory = parent.WorkingDirectory; }
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<Version>0.3.0</Version>
|
||||
<Version>0.4.0</Version>
|
||||
<Authors>Harald Wolff-Thobaben</Authors>
|
||||
<Company>l--n.de</Company>
|
||||
<Description>A simple build server scheduling builds triggered via web-hooks</Description>
|
||||
|
|
|
@ -1,9 +1,4 @@
|
|||
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using ln.build.commands;
|
||||
using ln.json;
|
||||
using ln.logging;
|
||||
|
@ -113,71 +108,4 @@ namespace ln.build.pipeline
|
|||
}
|
||||
}
|
||||
|
||||
public abstract class StageCommand
|
||||
{
|
||||
public string Name { get; }
|
||||
|
||||
public StageCommand(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
|
||||
public abstract void Run(Stage stage);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static Dictionary<string,Func<string,StageCommand>> commandFactories = new Dictionary<string, Func<string, StageCommand>>();
|
||||
|
||||
public static StageCommand Create(string cmdline)
|
||||
{
|
||||
string[] tokens = cmdline.Split(new char[]{' ','\t'}, 2);
|
||||
|
||||
if (commandFactories.TryGetValue(tokens[0],out Func<string,StageCommand> factory))
|
||||
{
|
||||
return factory(tokens[1]);
|
||||
}
|
||||
throw new Exception(string.Format("can't find factory for command keyword '{0}'", tokens[0]));
|
||||
}
|
||||
|
||||
static StageCommand()
|
||||
{
|
||||
commandFactories.Add("SH", (args) => new ShellCommand(args));
|
||||
commandFactories.Add("DOTNET", (args) => new DotNetCommand(args));
|
||||
commandFactories.Add("GITEA", (args) => new DotNetCommand(args));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class ShellCommand : StageCommand
|
||||
{
|
||||
public event CommandExitedDelegate OnCommandExited;
|
||||
|
||||
public CommandRunner CommandRunner { get; }
|
||||
|
||||
public ShellCommand(string filename,params CommandRunner.Argument[] arguments)
|
||||
:base("SH")
|
||||
{
|
||||
CommandRunner = new CommandRunner("/bin/bash", "-c"){ Throw = CRThrow.NEVER, };
|
||||
CommandRunner.AddArguments(arguments);
|
||||
}
|
||||
public ShellCommand(string cmdline)
|
||||
:base("SH")
|
||||
{
|
||||
CommandRunner = new CommandRunner("/bin/bash", "-c", string.Format("\"{0}\"",cmdline)){ Throw = CRThrow.NEVER, };
|
||||
}
|
||||
|
||||
public override void Run(Stage stage)
|
||||
{
|
||||
MemoryStream logStream = new MemoryStream();
|
||||
int result = CommandRunner.Run(stage.CommandEnvironment, logStream);
|
||||
stage.CommandEnvironment.Logger.Log(LogLevel.INFO, "command output:\n{0}", Encoding.UTF8.GetString(logStream.ToArray()));
|
||||
stage.CommandEnvironment.Logger.Log(LogLevel.INFO, "command exit code: {0}", result);
|
||||
if (result != 0)
|
||||
throw new Exception(String.Format("command exited with code {0} [0x{0:x}]", result));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
|
||||
using System;
|
||||
using System.IO;
|
||||
using Jint.Native.Function;
|
||||
using ln.build.repositories;
|
||||
using ln.logging;
|
||||
|
||||
namespace ln.build.pipeline
|
||||
{
|
||||
public class ReleaseCommand : StageCommand
|
||||
{
|
||||
public string[] Arguments { get; }
|
||||
public ReleaseCommand(string args) :base("RELEASE")
|
||||
{
|
||||
Arguments = args.Split();
|
||||
}
|
||||
|
||||
public override void Run(Stage stage)
|
||||
{
|
||||
if (stage.CommandEnvironment.Get("REPO_EVENT","").Equals("release"))
|
||||
{
|
||||
Repository repository = stage.CommandEnvironment.CIJob?.Repository;
|
||||
if (repository != null)
|
||||
{
|
||||
Release release = repository.GetRelease(int.Parse(stage.CommandEnvironment.Get("RELEASE_ID")));
|
||||
|
||||
foreach (string arg in Arguments)
|
||||
{
|
||||
string localPath, remoteFileName;
|
||||
int ie = arg.IndexOf('=');
|
||||
if (ie == -1)
|
||||
{
|
||||
localPath = Path.Combine(stage.CommandEnvironment.WorkingDirectory, arg);
|
||||
remoteFileName = Path.GetFileName(arg);
|
||||
} else {
|
||||
localPath = Path.Combine(stage.CommandEnvironment.WorkingDirectory, arg.Substring(0, ie));
|
||||
remoteFileName = arg.Substring(ie + 1);
|
||||
}
|
||||
|
||||
stage.CommandEnvironment.Logger.Log(LogLevel.INFO, "Adding {0} to release as {1}", localPath, remoteFileName);
|
||||
release.AddAttachement(localPath, remoteFileName);
|
||||
}
|
||||
|
||||
} else {
|
||||
stage.CommandEnvironment.Logger.Log(LogLevel.ERROR, "RELEASE: no repository interface found!");
|
||||
throw new Exception("Respository needed!");
|
||||
}
|
||||
} else {
|
||||
stage.CommandEnvironment.Logger.Log(LogLevel.INFO, "RELEASE: build is no release build. Ignoring.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using ln.build.commands;
|
||||
using ln.logging;
|
||||
|
||||
namespace ln.build.pipeline
|
||||
{
|
||||
public class ShellCommand : StageCommand
|
||||
{
|
||||
public event CommandExitedDelegate OnCommandExited;
|
||||
|
||||
public CommandRunner CommandRunner { get; }
|
||||
|
||||
public ShellCommand(string filename,params CommandRunner.Argument[] arguments)
|
||||
:base("SH")
|
||||
{
|
||||
CommandRunner = new CommandRunner("/bin/bash", "-c"){ Throw = CRThrow.NEVER, };
|
||||
CommandRunner.AddArguments(arguments);
|
||||
}
|
||||
public ShellCommand(string cmdline)
|
||||
:base("SH")
|
||||
{
|
||||
CommandRunner = new CommandRunner("/bin/bash", "-c", string.Format("\"{0}\"",cmdline)){ Throw = CRThrow.NEVER, };
|
||||
}
|
||||
|
||||
public override void Run(Stage stage)
|
||||
{
|
||||
MemoryStream logStream = new MemoryStream();
|
||||
int result = CommandRunner.Run(stage.CommandEnvironment, logStream);
|
||||
stage.CommandEnvironment.Logger.Log(LogLevel.INFO, "command output:\n{0}", Encoding.UTF8.GetString(logStream.ToArray()));
|
||||
stage.CommandEnvironment.Logger.Log(LogLevel.INFO, "command exit code: {0}", result);
|
||||
if (result != 0)
|
||||
throw new Exception(String.Format("command exited with code {0} [0x{0:x}]", result));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ln.build.commands;
|
||||
|
||||
namespace ln.build.pipeline
|
||||
{
|
||||
public abstract class StageCommand
|
||||
{
|
||||
public string Name { get; }
|
||||
|
||||
public StageCommand(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
|
||||
public abstract void Run(Stage stage);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static Dictionary<string,Func<string,StageCommand>> commandFactories = new Dictionary<string, Func<string, StageCommand>>();
|
||||
|
||||
public static StageCommand Create(string cmdline)
|
||||
{
|
||||
string[] tokens = cmdline.Split(new char[]{' ','\t'}, 2);
|
||||
|
||||
if (commandFactories.TryGetValue(tokens[0],out Func<string,StageCommand> factory))
|
||||
{
|
||||
return factory(tokens[1]);
|
||||
}
|
||||
throw new Exception(string.Format("can't find factory for command keyword '{0}'", tokens[0]));
|
||||
}
|
||||
|
||||
static StageCommand()
|
||||
{
|
||||
commandFactories.Add("SH", (args) => new ShellCommand(args));
|
||||
commandFactories.Add("DOTNET", (args) => new DotNetCommand(args));
|
||||
commandFactories.Add("GITEA", (args) => new DotNetCommand(args));
|
||||
commandFactories.Add("RELEASE", (args) => new ReleaseCommand(args));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
|
||||
using System;
|
||||
using System.Net.Http.Headers;
|
||||
|
||||
namespace ln.build.repositories
|
||||
{
|
||||
|
@ -16,7 +17,8 @@ namespace ln.build.repositories
|
|||
public abstract Repository Repository { get; }
|
||||
|
||||
public abstract Attachement[] GetAttachements();
|
||||
public abstract void AddAttachement(Attachement attachement,string localPath);
|
||||
|
||||
public abstract void AddAttachement(string localPath,string remoteFilename);
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ namespace ln.build.repositories
|
|||
|
||||
public abstract Release[] GetReleases();
|
||||
public abstract Release GetRelease(string tagName);
|
||||
public abstract Release GetRelease(int id);
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using ln.json;
|
||||
using ln.type;
|
||||
|
||||
namespace ln.build.repositories.gitea
|
||||
{
|
||||
|
@ -13,7 +18,7 @@ namespace ln.build.repositories.gitea
|
|||
}
|
||||
public GiteaRelease(GiteaRepository repository,JSONObject jsonRelease) : this(repository)
|
||||
{
|
||||
Id = (int)jsonRelease["id"].ToNative();
|
||||
Id = (int)(long)jsonRelease["id"].ToNative();
|
||||
Name = jsonRelease["name"].ToNative().ToString();
|
||||
TagName = jsonRelease["tag_name"].ToNative().ToString();
|
||||
Body = jsonRelease["body"].ToNative().ToString();
|
||||
|
@ -21,9 +26,18 @@ namespace ln.build.repositories.gitea
|
|||
IsPreRelease = (bool)jsonRelease["prerelease"].ToNative();
|
||||
}
|
||||
|
||||
public override void AddAttachement(Attachement attachement, string localPath)
|
||||
public override void AddAttachement(string localPath, string remoteFileName)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
using (FileStream fs = new FileStream(localPath,FileMode.Open))
|
||||
{
|
||||
StreamContent attachmentBytes = new StreamContent(fs);
|
||||
MultipartFormDataContent formDataContent = new MultipartFormDataContent();
|
||||
formDataContent.Add(attachmentBytes, "attachment", remoteFileName);
|
||||
if (HttpStatusCode.Created != GiteaRepository.Client.PostContent(formDataContent,out JSONValue response, "repos", GiteaRepository.Owner, GiteaRepository.Name, "releases", Id.ToString(), String.Format("assets?name={0}", remoteFileName)))
|
||||
{
|
||||
throw new Exception(String.Format("could not create attachment to release: {0}", localPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override Attachement[] GetAttachements()
|
||||
|
|
|
@ -29,7 +29,7 @@ namespace ln.build.repositories.gitea
|
|||
Owner = owner;
|
||||
Name = name;
|
||||
|
||||
Client = new JsonApiClient(baseURL);
|
||||
Client = new JsonApiClient(string.Format("{0}/api/v1",baseURL));
|
||||
}
|
||||
public GiteaRepository(string baseURL, string owner, string name, string accessToken)
|
||||
:this(baseURL, owner, name)
|
||||
|
@ -49,7 +49,7 @@ namespace ln.build.repositories.gitea
|
|||
if (HttpStatusCode.Created != Client.PostJson(
|
||||
stateObject,
|
||||
out JSONValue response,
|
||||
"api/v1/repos", job.GetVariable("REPO_OWNER"), job.GetVariable("REPO_NAME"), "statuses", job.GetVariable("REPO_REF")
|
||||
"repos", job.GetVariable("REPO_OWNER"), job.GetVariable("REPO_NAME"), "statuses", job.GetVariable("REPO_REF")
|
||||
))
|
||||
{
|
||||
job.Logger.Log(LogLevel.ERROR, "{0}: UpdateBuildState(): could not post state", GetType().Name);
|
||||
|
@ -58,14 +58,9 @@ namespace ln.build.repositories.gitea
|
|||
|
||||
public override Release[] GetReleases()
|
||||
{
|
||||
string releasesUrl = string.Format("{0}/api/v1/repos/{1}/{2}/releases",
|
||||
BaseURL,
|
||||
Owner,
|
||||
Name
|
||||
);
|
||||
if (HttpStatusCode.OK == Client.GetJson(
|
||||
out JSONValue jsonReleases,
|
||||
"api/v1/repos", Owner, "releases"
|
||||
"repos", Owner, Name, "releases"
|
||||
))
|
||||
{
|
||||
List<Release> releases = new List<Release>();
|
||||
|
@ -86,9 +81,17 @@ namespace ln.build.repositories.gitea
|
|||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public override Release GetRelease(int releaseId)
|
||||
{
|
||||
if (HttpStatusCode.OK == Client.GetJson(out JSONValue jsonRelease, "repos", Owner, Name, "releases", releaseId.ToString()))
|
||||
{
|
||||
return new GiteaRelease(this, jsonRelease as JSONObject);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public bool ContainsFile(string filename,string _ref) => (Client.GetJson(out JSONValue response, "/api/v1/repos", Owner, Name, "contents", string.Format("{0}?ref={1}",filename, _ref)) == HttpStatusCode.OK);
|
||||
public bool ContainsFile(string filename,string _ref) => (Client.GetJson(out JSONValue response, "repos", Owner, Name, "contents", string.Format("{0}?ref={1}",filename, _ref)) == HttpStatusCode.OK);
|
||||
|
||||
|
||||
|
||||
|
@ -125,7 +128,7 @@ namespace ln.build.repositories.gitea
|
|||
string giteaBaseUrl = htmlUrl.Remove(htmlUrl.Length - repoPath.Length);
|
||||
|
||||
|
||||
SecretStorage secretStorage = new SecretStorage(Path.Combine(ciService.BaseDirectory,"secrets", String.Format("{0}.json", hookSecret)));
|
||||
SecretStorage secretStorage = ciService.GetSecretStorage(hookSecret);
|
||||
GiteaRepository giteaRepository = new GiteaRepository(giteaBaseUrl, owner, repoName, secretStorage.GetSecret(giteaBaseUrl)){
|
||||
SecretStorage = secretStorage,
|
||||
};
|
||||
|
@ -156,10 +159,21 @@ namespace ln.build.repositories.gitea
|
|||
.SetVariable("REPO_OWNER", owner)
|
||||
.SetVariable("REPO_NAME", repoName)
|
||||
.SetVariable("REPO_REF", _ref)
|
||||
.SetVariable("NOTIFY", message["pusher"]["email"].ToNative().ToString())
|
||||
// .SetVariable("NOTIFY", message["pusher"]["email"].ToNative().ToString())
|
||||
;
|
||||
ciJob.Environment.SecretStorage = secretStorage;
|
||||
|
||||
switch (eventType)
|
||||
{
|
||||
case "release":
|
||||
ciJob.SetVariable("RELEASE_TAGNAME", message["release"]["tag_name"].ToNative().ToString());
|
||||
ciJob.SetVariable("RELEASE_ID", message["release"]["id"].ToNative().ToString());
|
||||
break;
|
||||
case "push":
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
ciJob.UpdateBuildState(BuildState.PENDING);
|
||||
ciService.Enqueue(ciJob);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue