ln.build/ln.build/repositories/gitea/GiteaRepository.cs

255 lines
11 KiB
C#

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Net;
using ln.build.commands;
using ln.build.pipeline;
using ln.build.secrets;
using ln.build.semver;
using ln.http;
using ln.json;
using ln.logging;
using ln.threading;
using ln.type;
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; }
string AccessToken { get; set; }
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(string.Format("{0}/api/v1",baseURL));
}
public GiteaRepository(string baseURL, string owner, string name, string accessToken)
:this(baseURL, owner, name)
{
AccessToken = accessToken;
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,
"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()
{
if (HttpStatusCode.OK == Client.GetJson(
out JSONValue jsonReleases,
"repos", Owner, Name, "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 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, "repos", Owner, Name, "contents", string.Format("{0}?ref={1}",filename, _ref)) == HttpStatusCode.OK);
public override void CommitAndPush(string message, string[] addedPaths, string[] modifiedPaths, string[] removedPaths)
{
CommandRunner cr = new CommandRunner("git","add");
cr.AddArguments(addedPaths);
cr.AddArguments(modifiedPaths);
cr.Run();
cr = new CommandRunner("git","rm");
cr.AddArguments(removedPaths);
cr.Run();
cr = new CommandRunner("git","commit", "-m", message);
cr.Run();
URI baseUri = new URI(BaseURL);
if (!AccessToken?.Equals(String.Empty) ?? false)
baseUri = baseUri.WithUserInfo(AccessToken);
cr = new CommandRunner("git","push", baseUri.ToString(true));
cr.Run();
}
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 = ciService.GetSecretStorage(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>();
JSONObject jsonCommit = null;
switch (eventType)
{
case "release":
if (!message["action"].ToNative().ToString().Equals("published"))
return response;
buildRefs.Add(message["release"]["tag_name"].ToNative().ToString());
break;
case "push":
string currentCommitId = message["after"].ToNative().ToString();
foreach (JSONObject _jsonCommit in (message["commits"] as JSONArray).Children)
{
if (currentCommitId.Equals(_jsonCommit["id"].ToNative().ToString()))
{
buildRefs.Add(_jsonCommit["id"].ToNative().ToString());
jsonCommit = _jsonCommit;
break;
}
}
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("REPO_BASE", giteaRepository.BaseURL)
// .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":
if (jsonCommit != null)
{
string commitMessage = jsonCommit["message"].ToNative().ToString();
if (commitMessage.Contains("#ReleaseMajor"))
ciJob.OnJobCompleted += (job) => job.PublishRelease(SemVerLevels.MAJOR);
else if (commitMessage.Contains("#ReleaseMinor"))
ciJob.OnJobCompleted += (job) => job.PublishRelease(SemVerLevels.MINOR);
else if (commitMessage.Contains("#ReleasePatch"))
ciJob.OnJobCompleted += (job) => job.PublishRelease(SemVerLevels.PATCH);
}
break;
}
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;
}
}
}