diff --git a/build.ln b/build.ln
index db60fbc..3364810 100644
--- a/build.ln
+++ b/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": {
diff --git a/ln.build.server/Program.cs b/ln.build.server/Program.cs
index 7074d43..03678f5 100644
--- a/ln.build.server/Program.cs
+++ b/ln.build.server/Program.cs
@@ -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 {
diff --git a/ln.build.server/ln.build.server.csproj b/ln.build.server/ln.build.server.csproj
index 5302868..186c26b 100644
--- a/ln.build.server/ln.build.server.csproj
+++ b/ln.build.server/ln.build.server.csproj
@@ -6,7 +6,7 @@
- 0.3.0
+ 0.4.0
Harald Wolff-Thobaben
l--n.de
A simple build server scheduling builds triggered via web-hooks
diff --git a/ln.build/CIJob.cs b/ln.build/CIJob.cs
index b6d6deb..b7f4d92 100644
--- a/ln.build/CIJob.cs
+++ b/ln.build/CIJob.cs
@@ -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)
diff --git a/ln.build/CIService.cs b/ln.build/CIService.cs
index 1593a7c..c4e6aa3 100644
--- a/ln.build/CIService.cs
+++ b/ln.build/CIService.cs
@@ -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)
{
diff --git a/ln.build/JsonApiClient.cs b/ln.build/JsonApiClient.cs
index c2f589b..c8caecc 100644
--- a/ln.build/JsonApiClient.cs
+++ b/ln.build/JsonApiClient.cs
@@ -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;
+ }
}
}
\ No newline at end of file
diff --git a/ln.build/commands/CommandEnvironment.cs b/ln.build/commands/CommandEnvironment.cs
index b1aac79..405712c 100644
--- a/ln.build/commands/CommandEnvironment.cs
+++ b/ln.build/commands/CommandEnvironment.cs
@@ -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; }
diff --git a/ln.build/ln.build.csproj b/ln.build/ln.build.csproj
index 1352265..a0305de 100644
--- a/ln.build/ln.build.csproj
+++ b/ln.build/ln.build.csproj
@@ -5,7 +5,7 @@
- 0.3.0
+ 0.4.0
Harald Wolff-Thobaben
l--n.de
A simple build server scheduling builds triggered via web-hooks
diff --git a/ln.build/pipeline/DefaultPipeLine.cs b/ln.build/pipeline/DefaultPipeLine.cs
index 885e6c3..dadcfa5 100644
--- a/ln.build/pipeline/DefaultPipeLine.cs
+++ b/ln.build/pipeline/DefaultPipeLine.cs
@@ -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> commandFactories = new Dictionary>();
-
- public static StageCommand Create(string cmdline)
- {
- string[] tokens = cmdline.Split(new char[]{' ','\t'}, 2);
-
- if (commandFactories.TryGetValue(tokens[0],out Func 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));
- }
- }
-
}
\ No newline at end of file
diff --git a/ln.build/pipeline/ReleaseCommand.cs b/ln.build/pipeline/ReleaseCommand.cs
new file mode 100644
index 0000000..5711171
--- /dev/null
+++ b/ln.build/pipeline/ReleaseCommand.cs
@@ -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.");
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/ln.build/pipeline/ShellCommand.cs b/ln.build/pipeline/ShellCommand.cs
new file mode 100644
index 0000000..895c320
--- /dev/null
+++ b/ln.build/pipeline/ShellCommand.cs
@@ -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));
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/ln.build/pipeline/StageCommand.cs b/ln.build/pipeline/StageCommand.cs
new file mode 100644
index 0000000..d0462d7
--- /dev/null
+++ b/ln.build/pipeline/StageCommand.cs
@@ -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> commandFactories = new Dictionary>();
+
+ public static StageCommand Create(string cmdline)
+ {
+ string[] tokens = cmdline.Split(new char[]{' ','\t'}, 2);
+
+ if (commandFactories.TryGetValue(tokens[0],out Func 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));
+
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/ln.build/repositories/Release.cs b/ln.build/repositories/Release.cs
index 6df1e85..4372f1d 100644
--- a/ln.build/repositories/Release.cs
+++ b/ln.build/repositories/Release.cs
@@ -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);
diff --git a/ln.build/repositories/Repository.cs b/ln.build/repositories/Repository.cs
index 0622ea9..91c5259 100644
--- a/ln.build/repositories/Repository.cs
+++ b/ln.build/repositories/Repository.cs
@@ -12,6 +12,7 @@ namespace ln.build.repositories
public abstract Release[] GetReleases();
public abstract Release GetRelease(string tagName);
+ public abstract Release GetRelease(int id);
}
diff --git a/ln.build/repositories/gitea/GiteaRelease.cs b/ln.build/repositories/gitea/GiteaRelease.cs
index 776d52e..b086a7f 100644
--- a/ln.build/repositories/gitea/GiteaRelease.cs
+++ b/ln.build/repositories/gitea/GiteaRelease.cs
@@ -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()
diff --git a/ln.build/repositories/gitea/GiteaRepository.cs b/ln.build/repositories/gitea/GiteaRepository.cs
index 130dccf..408ba1d 100644
--- a/ln.build/repositories/gitea/GiteaRepository.cs
+++ b/ln.build/repositories/gitea/GiteaRepository.cs
@@ -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 releases = new List();
@@ -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);
}