Added SecretStorage as mentioned in #3, reworked on Hook->Job workflow to prepare for "Release" support (like #1)
ln.build - build0.waldrennach.l--n.de build job pending Details

master
Harald Wolff 2020-12-03 14:13:42 +01:00
parent a8feb55dab
commit 715dc5d3d9
16 changed files with 422 additions and 200 deletions

View File

@ -11,6 +11,7 @@ using ln.build.repositories;
using Microsoft.VisualBasic;
using ln.application;
using System.IO;
using ln.build.repositories.gitea;
namespace ln.build.server
{
@ -38,7 +39,9 @@ namespace ln.build.server
job.RunJob();
} else {
CIService.Initialize();
CIService.AddRepositoryInterface(new GiteaRepositoryInterface("https://git.l--n.de"){ AuthorizationToken = "1d03e9577c404b5b4f46b340147b1d500ff95b2e", });
CIService.AddWebHookHandler("gitea", GiteaRepository.WebHookHandler);
// (new GiteaRepository("https://git.l--n.de"){ AuthorizationToken = "1d03e9577c404b5b4f46b340147b1d500ff95b2e", });
CIService.Start();
}

View File

@ -6,6 +6,7 @@ using System.Text;
using ln.build.commands;
using ln.build.pipeline;
using ln.build.repositories;
using ln.build.secrets;
using ln.json;
using ln.logging;
using ln.threading;
@ -15,15 +16,18 @@ namespace ln.build
public class CIJob : PoolJob
{
public string JobID { get; } = Guid.NewGuid().ToString("N");
public string RepositoryURL { get; }
public string WorkingDirectory { get; set; }
public CIService CIService { get; }
public Repository Repository { get; set; }
public SecretStorage SecretStorage { get; set; }
public string WorkingDirectory { get; set; }
public Logger Logger { get; private set; }
public CommandEnvironment Environment { get; }
public RepositoryInterface RepositoryInterface { get; set; }
public DefaultPipeLine PipeLine { get; set; }
@ -32,12 +36,15 @@ namespace ln.build
Dictionary<string,MemoryStream> logStreams = new Dictionary<string, MemoryStream>();
Dictionary<string,Logger> logStreamLoggers = new Dictionary<string, Logger>();
public CIJob(CIService ciService, string repositoryURL)
:this(ciService, null, repositoryURL){ }
public CIJob(CIService ciService, RepositoryInterface repositoryInterface ,string repositoryURL)
public CIJob(CIService ciService, Repository repository, SecretStorage secretStorage)
:this(ciService, repository)
{
SecretStorage = secretStorage;
}
public CIJob(CIService ciService, Repository repository)
{
CIService = ciService;
RepositoryInterface = repositoryInterface;
Repository = repository;
WorkingDirectory = Path.Combine(Path.GetTempPath(), JobID);
Directory.CreateDirectory(Path.Combine(ciService.ReportsDirectory, JobID));
@ -45,8 +52,6 @@ namespace ln.build
Logger = new Logger(new FileLogger(Path.Combine(ciService.ReportsDirectory, JobID, "build.log")));
Logger.Backends.Add(Logger.ConsoleLogger);
RepositoryURL = repositoryURL;
Environment = new CommandEnvironment();
}
@ -86,7 +91,7 @@ namespace ln.build
public void UpdateBuildState(BuildState buildState)
{
BuildState = buildState;
RepositoryInterface?.UpdateBuildState(this);
Repository?.UpdateBuildState(this);
}
public override void RunJob()
@ -115,7 +120,7 @@ namespace ln.build
public bool CloneRepository()
{
RepositoryInterface?.CloneSources(this);
Repository?.CloneSources(this);
return true;
}
@ -148,7 +153,7 @@ namespace ln.build
{
CIService.CreateReport(this);
if (RepositoryInterface != null)
if (Repository != null)
Directory.Delete(WorkingDirectory, true);
}

View File

@ -20,6 +20,9 @@ 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; }
@ -38,32 +41,14 @@ namespace ln.build
StaticRouter staticsRouter;
TemplateRouter templateRouter;
HTTPServer httpServer;
List<RepositoryInterface> repositoryInterfaces = new List<RepositoryInterface>();
HashSet<PipeLine> pipelines = new HashSet<PipeLine>();
public IEnumerable<PipeLine> PipeLines => pipelines;
Dictionary<string,string> secrets = new Dictionary<string, string>();
public CIService()
{
buildPool = new Pool(2);
String secretsPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),".ln.build.secrets.json");
Logging.Log(LogLevel.INFO, "Trying to load secrets from {0}", secretsPath);
if (File.Exists(secretsPath))
{
JSONObject secretsObject = JSONParser.ParseFile(secretsPath) as JSONObject;
foreach (string key in secretsObject.Keys)
{
secrets.Add(key, secretsObject[key].ToNative().ToString());
}
Logging.Log(LogLevel.INFO, "Secrets loaded from {0}", secretsPath);
}
}
public CIService(string baseDirectory) : this()
{
@ -73,10 +58,9 @@ namespace ln.build
public void Initialize()
{
BaseDirectory ??= AppContext.BaseDirectory;
BaseDirectory ??= ContextDirectory;
ReportsDirectory ??= Path.Combine(BaseDirectory, "builds" );
ReportsDirectory = Path.GetFullPath(ReportsDirectory);
Logging.Log(LogLevel.INFO, "Commandline: {0}", Environment.CommandLine);
@ -85,6 +69,7 @@ namespace ln.build
Logging.Log(LogLevel.INFO, "Reports Directory: {0}", ReportsDirectory);
Directory.CreateDirectory(ReportsDirectory);
Directory.CreateDirectory(Path.Combine(BaseDirectory, "secrets"));
InitializeHttpServer();
}
@ -104,8 +89,8 @@ namespace ln.build
httpRouter.AddSimpleRoute("/builds/*", new StaticRouter(ReportsDirectory));
hookRouter = new SimpleRouter();
staticsRouter = new StaticRouter(Path.Combine(BaseDirectory, "html", "static"));
templateRouter = new TemplateRouter(new FileSystemTemplateSource(Path.Combine(BaseDirectory, "html", "documents"), false));
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);
@ -130,8 +115,6 @@ namespace ln.build
public string GetJobURL(CIJob job) => string.Format("{0}/builds/{1}", BaseURL, job.JobID);
public string GetSecret(string key) => secrets[key];
public void Start()
{
buildPool.Start();
@ -143,10 +126,9 @@ namespace ln.build
pipelines.Add(pipeLine);
}
public void AddRepositoryInterface(RepositoryInterface repositoryInterface)
public void AddWebHookHandler(string name, Func<CIService,HttpRequest,HttpResponse> webHookHandler)
{
hookRouter.AddSimpleRoute(String.Format("/{0}", repositoryInterface.WebHookName), (HttpRoutingContext context,HttpRequest request) => repositoryInterface.WebHookHandler(this, request));
repositoryInterfaces.Add(repositoryInterface);
hookRouter.AddSimpleRoute(String.Format("/{0}", name), (HttpRoutingContext context,HttpRequest request) => webHookHandler(this, request));
}

View File

@ -0,0 +1,58 @@
using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using ln.json;
using ln.logging;
namespace ln.build
{
public class JsonApiClient : IDisposable
{
public string BaseURL { get; }
public HttpClient HttpClient { get; }
public JsonApiClient(string baseUrl)
{
HttpClient = new HttpClient();
BaseURL = baseUrl;
}
public HttpStatusCode GetJson(out JSONValue response,params string[] path)
{
HttpResponseMessage httpResponse = HttpClient.GetAsync(string.Format("{0}/{1}",BaseURL, string.Join('/', path))).Result;
if (httpResponse.StatusCode == System.Net.HttpStatusCode.OK)
{
response = JSONParser.Parse(httpResponse.Content.ReadAsStringAsync().Result);
} else {
response = null;
}
return httpResponse.StatusCode;
}
public HttpStatusCode PostJson(JSONValue content, out JSONValue response,params string[] path)
{
StringContent jsonContent = new StringContent(content.ToString(),Encoding.UTF8, "application/json");
HttpResponseMessage httpResponse = HttpClient.PostAsync(string.Format("{0}/{1}",BaseURL, string.Join('/', path)),jsonContent).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();
}
}
}

View File

@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using ln.build.secrets;
using ln.json;
using ln.logging;
@ -26,6 +27,9 @@ namespace ln.build.commands
public bool IsReadOnly => ((ICollection<KeyValuePair<string, string>>)variables).IsReadOnly;
public string this[string key] { get => Get(key); set => Set(key,value); }
SecretStorage secretStorage;
public SecretStorage SecretStorage { get => secretStorage ?? Parent?.SecretStorage; set => secretStorage = 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; }
public CommandEnvironment() : this(Logger.Default)

View File

@ -19,7 +19,6 @@
<ItemGroup>
<PackageReference Include="LibGit2Sharp" Version="0.26.2" />
<PackageReference Include="ln.logging" Version="1.0.1" />
<PackageReference Include="ln.threading" Version="0.1.0" />
<PackageReference Include="ln.json" Version="1.0.0" />

View File

@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using ln.build.commands;
using ln.json;
@ -100,7 +99,7 @@ namespace ln.build.pipeline
JSONObject jsonSecrets = jsonStage["secrets"] as JSONObject;
foreach (string key in jsonSecrets.Keys)
{
CommandEnvironment.Set(key, PipeLine.CIService.GetSecret(jsonSecrets[key].ToNative().ToString()));
CommandEnvironment.Set(key, CommandEnvironment.SecretStorage.GetSecret(jsonSecrets[key].ToNative().ToString()));
}
}

View File

@ -1,13 +1,8 @@
using System;
using System.IO;
using System.Reflection.Metadata.Ecma335;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Runtime.Serialization.Formatters;
using System.Text;
using System.Text.RegularExpressions;
using LibGit2Sharp.Handlers;
using ln.build.commands;
using ln.logging;
using ln.type;

View File

@ -6,14 +6,12 @@ using ln.http;
namespace ln.build.repositories
{
public abstract class GitRepositoryInterface : RepositoryInterface
public abstract class GitRepository : Repository
{
string webHookName;
public override string WebHookName => webHookName;
public GitRepositoryInterface(string webHookName)
public string CloneURL { get; }
public GitRepository(string cloneUrl)
{
this.webHookName = webHookName;
CloneURL = cloneUrl;
}
public override void CloneSources(CIJob job)
@ -21,7 +19,7 @@ namespace ln.build.repositories
job.Logger.Log("{0}: cloning repository to {1}", GetType().Name, job.WorkingDirectory);
job.Environment.WorkingDirectory = Path.GetTempPath();
bool success = new CommandRunner("git", "clone", job.RepositoryURL, job.WorkingDirectory).Run(job.Environment) == 0;
bool success = new CommandRunner("git", "clone", CloneURL, job.WorkingDirectory).Run(job.Environment) == 0;
job.Environment.WorkingDirectory = job.WorkingDirectory;
if (success && job.ContainsVariable("COMMIT_ID"))

View File

@ -1,127 +0,0 @@
using System;
using System.Net.Http;
using System.Text;
using ln.http;
using ln.json;
using ln.logging;
namespace ln.build.repositories
{
public class GiteaRepositoryInterface : GitRepositoryInterface
{
public string BaseURL { get; }
public string AuthorizationToken { get; set; }
public GiteaRepositoryInterface(string baseURL) :base("gitea")
{
BaseURL = baseURL;
}
HttpClient CreateHttpClient()
{
HttpClient httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Add("Authorization",String.Format("token {0}", AuthorizationToken));
return httpClient;
}
public override bool DetectValidity(string cloneUrl) => cloneUrl.StartsWith(BaseURL);
public override void UpdateBuildState(CIJob job)
{
string buildStateURL = string.Format("{3}/api/v1/repos/{0}/{1}/statuses/{2}",
job.GetVariable("REPO_OWNER"),
job.GetVariable("REPO_NAME"),
job.GetVariable("COMMIT_ID"),
BaseURL
);
if (buildStateURL != null)
{
JSONObject stateObject = new JSONObject();
stateObject.Add("context", string.Format("ln.build - {0}", job.CIService.HostName));
stateObject.Add("description", "build job pending");
stateObject.Add("state", job.BuildState.ToString().ToLower());
stateObject.Add("target_url", job.CIService.GetJobURL(job));
using (HttpClient httpClient = CreateHttpClient())
{
HttpResponseMessage response = httpClient.PostAsync(buildStateURL, new StringContent(stateObject.ToString(),Encoding.UTF8,"application/json")).Result;
job.Logger.Log(LogLevel.DEBUG, "UpdateBuildState({0}): {1}", job.BuildState, buildStateURL);
job.Logger.Log(LogLevel.DEBUG, "Request: {0}", stateObject.ToString());
job.Logger.Log(LogLevel.DEBUG, "Response: {0}", response );
}
}
}
public override HttpResponse WebHookHandler(CIService ciService, HttpRequest request)
{
HttpResponse response = new HttpResponse(request);
if (!request.Method.Equals("POST"))
{
response.StatusCode = 405;
} else if (!request.GetRequestHeader("content-type").Equals("application/json"))
{
response.StatusCode = 415;
response.ContentWriter.WriteLine("Unsupported Media Type, should be application/json");
} else {
JSONValue jsonRequest = JSONParser.Parse(request.ContentReader.ReadToEnd());
if (jsonRequest is JSONObject message)
{
try
{
string cloneUrl = message["repository"]["clone_url"].ToNative().ToString();
foreach (JSONValue commit in (message["commits"] as JSONArray).Children)
{
string commitID = commit["id"].ToNative().ToString();
Logging.Log("Received CI request of repository {0} for commit {1}", cloneUrl, commitID);
CIJob job = new CIJob(ciService, this, cloneUrl)
.SetVariable("REPO_OWNER", message["repository"]["owner"]["username"].ToNative().ToString())
.SetVariable("REPO_NAME", message["repository"]["name"].ToNative().ToString())
.SetVariable("COMMIT_ID", commitID)
.SetVariable("NOTIFY", message["pusher"]["email"].ToNative().ToString())
.SetVariable("NUGET_APIKEY", "3yAJPMxcaEhb_HP62dxK")
.SetVariable("NUGET_SOURCE", "http://nuget.l--n.de/nuget/l--n/v3/index.json")
;
using (HttpClient httpClient = CreateHttpClient())
{
string triggerFile = string.Format("{3}/api/v1/repos/{0}/{1}/contents/build.ln?ref={2}",
job.GetVariable("REPO_OWNER"),
job.GetVariable("REPO_NAME"),
job.GetVariable("COMMIT_ID"),
BaseURL
);
HttpResponseMessage triggerResponse = httpClient.GetAsync(triggerFile).Result;
if (triggerResponse.StatusCode == System.Net.HttpStatusCode.OK)
{
job.UpdateBuildState(BuildState.PENDING);
ciService.Enqueue(job);
}
}
}
} catch (Exception e)
{
response.StatusCode = 500;
response.StatusMessage = "An exception occured";
response.ContentWriter.WriteLine("{0}", e.ToString());
Logging.Log(e);
}
} else {
response.StatusCode = 400;
}
}
return response;
}
}
}

View File

@ -0,0 +1,40 @@
using System;
namespace ln.build.repositories
{
public abstract class Release
{
public string Name { get; set; }
public int Id { get; set; } = -1;
public string TagName { get; set; }
public Boolean IsDraft {get; set; }
public Boolean IsPreRelease {get; set; }
public string Body { get; set; }
public abstract Repository Repository { get; }
public abstract Attachement[] GetAttachements();
public abstract void AddAttachement(Attachement attachement,string localPath);
public class Attachement
{
public Release Release { get; set; }
public int Id { get; set; } = -1;
public string Name { get; set; }
public Guid UUID { get; set; }
public string DownloadURL { get; set; }
public Attachement(Release release)
{
Release = release;
}
}
}
}

View File

@ -0,0 +1,18 @@
using System;
using ln.http;
namespace ln.build.repositories
{
public abstract class Repository
{
public abstract void CloneSources(CIJob job);
public abstract void UpdateBuildState(CIJob job);
public abstract Release[] GetReleases();
public abstract Release GetRelease(string tagName);
}
}

View File

@ -1,18 +0,0 @@
using System;
using ln.http;
namespace ln.build.repositories
{
public abstract class RepositoryInterface
{
public abstract String WebHookName { get; }
public abstract HttpResponse WebHookHandler(CIService service, HttpRequest request);
public abstract bool DetectValidity(string cloneUrl);
public abstract void CloneSources(CIJob job);
public abstract void UpdateBuildState(CIJob job);
}
}

View File

@ -0,0 +1,34 @@
using ln.json;
namespace ln.build.repositories.gitea
{
public class GiteaRelease : Release
{
public override Repository Repository => GiteaRepository;
public GiteaRepository GiteaRepository { get; }
public GiteaRelease(GiteaRepository repository)
{
GiteaRepository = repository;
}
public GiteaRelease(GiteaRepository repository,JSONObject jsonRelease) : this(repository)
{
Id = (int)jsonRelease["id"].ToNative();
Name = jsonRelease["name"].ToNative().ToString();
TagName = jsonRelease["tag_name"].ToNative().ToString();
Body = jsonRelease["body"].ToNative().ToString();
IsDraft = (bool)jsonRelease["draft"].ToNative();
IsPreRelease = (bool)jsonRelease["prerelease"].ToNative();
}
public override void AddAttachement(Attachement attachement, string localPath)
{
throw new System.NotImplementedException();
}
public override Attachement[] GetAttachements()
{
throw new System.NotImplementedException();
}
}
}

View File

@ -0,0 +1,188 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using ln.build.secrets;
using ln.http;
using ln.json;
using ln.logging;
namespace ln.build.repositories.gitea
{
public class GiteaRepository : GitRepository
{
public string BaseURL { get; }
public string Owner { get; }
public string Name { get; }
public SecretStorage SecretStorage { get; set; }
public JsonApiClient Client { get; }
public GiteaRepository(string baseURL, string owner, string name)
:base(string.Format("{0}/{1}/{2}.git",baseURL, owner, name))
{
Logging.Log(LogLevel.DEBUG, "new GiteaRepository({0},{1},{2})", baseURL, owner, name);
BaseURL = baseURL;
Owner = owner;
Name = name;
Client = new JsonApiClient(baseURL);
}
public GiteaRepository(string baseURL, string owner, string name, string accessToken)
:this(baseURL, owner, name)
{
if (accessToken != null)
Client.HttpClient.DefaultRequestHeaders.Add("Authorization",String.Format("token {0}", accessToken));
}
public override void UpdateBuildState(CIJob job)
{
JSONObject stateObject = new JSONObject();
stateObject.Add("context", string.Format("ln.build - {0}", job.CIService.HostName));
stateObject.Add("description", "build job pending");
stateObject.Add("state", job.BuildState.ToString().ToLower());
stateObject.Add("target_url", job.CIService.GetJobURL(job));
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")
))
{
job.Logger.Log(LogLevel.ERROR, "{0}: UpdateBuildState(): could not post state", GetType().Name);
}
}
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"
))
{
List<Release> releases = new List<Release>();
foreach (JSONObject jsonRelease in jsonReleases.Children)
{
GiteaRelease giteaRelease = new GiteaRelease(this, jsonRelease);
releases.Add(giteaRelease);
}
return releases.ToArray();
}
return new Release[0];
}
public override Release GetRelease(string tagName)
{
throw new NotImplementedException();
}
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 static HttpResponse WebHookHandler(CIService ciService, HttpRequest request)
{
HttpResponse response = new HttpResponse(request);
if (!request.Method.Equals("POST"))
{
response.StatusCode = 405;
} else if (!request.GetRequestHeader("content-type").Equals("application/json"))
{
response.StatusCode = 415;
response.ContentWriter.WriteLine("Unsupported Media Type, should be application/json");
} else {
JSONValue jsonRequest = JSONParser.Parse(request.ContentReader.ReadToEnd());
if (jsonRequest is JSONObject message)
{
try
{
string hookSecret = message["secret"].ToNative().ToString();
string owner = message["repository"]["owner"]["username"].ToNative().ToString();
string repoName = message["repository"]["name"].ToNative().ToString();
string htmlUrl = message["repository"]["html_url"].ToNative().ToString();
string repoPath = String.Format("/{0}/{1}", owner, repoName);
if (!htmlUrl.EndsWith(repoPath))
{
Logging.Log(LogLevel.WARNING, "unable to detect gitea base url from [{0}] for ({1}/{2})", htmlUrl, owner, repoName);
response.StatusCode = 500;
return response;
}
string giteaBaseUrl = htmlUrl.Remove(htmlUrl.Length - repoPath.Length);
SecretStorage secretStorage = new SecretStorage(Path.Combine(ciService.BaseDirectory,"secrets", String.Format("{0}.json", hookSecret)));
GiteaRepository giteaRepository = new GiteaRepository(giteaBaseUrl, owner, repoName, secretStorage.GetSecret(giteaBaseUrl)){
SecretStorage = secretStorage,
};
string eventType = request.GetRequestHeader("X-GITEA-EVENT");
List<string> buildRefs = new List<string>();
switch (eventType)
{
case "release":
buildRefs.Add(message["release"]["tag_name"].ToNative().ToString());
break;
case "push":
foreach (JSONObject jsonCommit in (message["commits"] as JSONArray).Children)
buildRefs.Add(jsonCommit["id"].ToNative().ToString());
break;
default:
Logging.Log(LogLevel.WARNING, "received webhook with unsupported event type [{0}]", eventType);
return response;
}
foreach (string _ref in buildRefs)
{
if (giteaRepository.ContainsFile("build.ln", _ref))
{
CIJob ciJob = new CIJob(ciService, giteaRepository)
.SetVariable("REPO_EVENT", eventType)
.SetVariable("REPO_OWNER", owner)
.SetVariable("REPO_NAME", repoName)
.SetVariable("REPO_REF", _ref)
.SetVariable("NOTIFY", message["pusher"]["email"].ToNative().ToString())
;
ciJob.Environment.SecretStorage = secretStorage;
ciJob.UpdateBuildState(BuildState.PENDING);
ciService.Enqueue(ciJob);
}
}
} catch (Exception e)
{
response.StatusCode = 500;
response.StatusMessage = "An exception occured";
response.ContentWriter.WriteLine("{0}", e.ToString());
Logging.Log(e);
}
} else {
response.StatusCode = 400;
}
}
return response;
}
}
}

View File

@ -0,0 +1,44 @@
using System.Collections.Generic;
using System.IO;
using System.IO.Enumeration;
using ln.json;
using ln.logging;
namespace ln.build.secrets
{
public class SecretStorage
{
public string FileName { get; }
Dictionary<string,string> secrets = new Dictionary<string, string>();
public SecretStorage(string filename)
{
FileName = filename;
LoadSecrets();
}
void LoadSecrets()
{
lock (this)
{
Logging.Log(LogLevel.INFO, "loading secrets from {0}", FileName);
if (File.Exists(FileName))
{
JSONObject secretsObject = JSONParser.ParseFile(FileName) as JSONObject;
foreach (string key in secretsObject.Keys)
{
secrets.Add(key, secretsObject[key].ToNative().ToString());
}
}
}
}
public string GetSecret(string key)
{
TryGetSecret(key, out string secret);
return secret;
}
public bool TryGetSecret(string key, out string secret) => secrets.TryGetValue(key, out secret);
}
}