add auto-updates from github

This commit is contained in:
zznty
2022-10-15 15:33:57 +07:00
parent 197d04a661
commit c5acf61f7c
9 changed files with 245 additions and 105 deletions

View File

@@ -32,7 +32,22 @@ namespace Torch
UGCServiceType UgcServiceType { get; set; }
bool EntityManagerEnabled { get; set; }
string LoginToken { get; set; }
UpdateSource UpdateSource { get; set; }
void Save(string path = null);
}
public class UpdateSource
{
public UpdateSourceType SourceType { get; set; }
public string Url { get; set; }
public string Repository { get; set; }
public string Branch { get; set; }
}
public enum UpdateSourceType
{
Github,
Jenkins
}
}

View File

@@ -18,6 +18,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="JorgeSerrano.Json.JsonSnakeCaseNamingPolicy" Version="0.9.0" />
<PackageReference Include="NLog" Version="5.0.4" />
<PackageReference Include="SemanticVersioning" Version="2.0.2" />
<PackageReference Include="SpaceEngineersDedicated.ReferenceAssemblies" Version="1.201.13">

View File

@@ -0,0 +1,128 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Json;
using System.Text.Json;
using System.Threading.Tasks;
using JorgeSerrano.Json;
using Version = SemanticVersioning.Version;
namespace Torch.API.WebAPI;
public class GithubQuery : IUpdateQuery
{
private readonly HttpClient _client;
public GithubQuery(string url)
{
if (url == null) throw new ArgumentNullException(nameof(url));
_client = new()
{
BaseAddress = new(url),
DefaultVersionPolicy = HttpVersionPolicy.RequestVersionOrHigher,
DefaultRequestHeaders =
{
{"User-Agent", "TorchAPI"}
}
};
}
public void Dispose()
{
_client?.Dispose();
}
public async Task<UpdateRelease> GetLatestReleaseAsync(string repository, string branch = null)
{
var response = await _client.GetFromJsonAsync<Release>($"/repos/{repository}/releases/latest", new JsonSerializerOptions(JsonSerializerDefaults.Web)
{
PropertyNamingPolicy = new JsonSnakeCaseNamingPolicy()
});
if (response is null)
throw new($"Unable to get latest release for {repository}");
return new(Version.Parse(response.TagName), response.Assets.First(b => b.Name == "torch-server.zip").BrowserDownloadUrl);
}
private record Asset(
string Url,
int Id,
string NodeId,
string Name,
string Label,
Uploader Uploader,
string ContentType,
string State,
int Size,
int DownloadCount,
DateTime CreatedAt,
DateTime UpdatedAt,
string BrowserDownloadUrl
);
private record Author(
string Login,
int Id,
string NodeId,
string AvatarUrl,
string GravatarId,
string Url,
string HtmlUrl,
string FollowersUrl,
string FollowingUrl,
string GistsUrl,
string StarredUrl,
string SubscriptionsUrl,
string OrganizationsUrl,
string ReposUrl,
string EventsUrl,
string ReceivedEventsUrl,
string Type,
bool SiteAdmin
);
private record Release(
string Url,
string AssetsUrl,
string UploadUrl,
string HtmlUrl,
int Id,
Author Author,
string NodeId,
string TagName,
string TargetCommitish,
string Name,
bool Draft,
bool Prerelease,
DateTime CreatedAt,
DateTime PublishedAt,
IReadOnlyList<Asset> Assets,
string TarballUrl,
string ZipballUrl,
string Body
);
private record Uploader(
string Login,
int Id,
string NodeId,
string AvatarUrl,
string GravatarId,
string Url,
string HtmlUrl,
string FollowersUrl,
string FollowingUrl,
string GistsUrl,
string StarredUrl,
string SubscriptionsUrl,
string OrganizationsUrl,
string ReposUrl,
string EventsUrl,
string ReceivedEventsUrl,
string Type,
bool SiteAdmin
);
}

View File

@@ -0,0 +1,9 @@
using System;
using System.Threading.Tasks;
namespace Torch.API.WebAPI;
public interface IUpdateQuery : IDisposable
{
Task<UpdateRelease> GetLatestReleaseAsync(string repository, string branch = null);
}

View File

@@ -1,82 +1,52 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Json;
using System.Text;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using NLog;
using Torch.API.Utils;
using Version = SemanticVersioning.Version;
namespace Torch.API.WebAPI
{
public class JenkinsQuery
public class JenkinsQuery : IUpdateQuery
{
private const string BRANCH_QUERY = "http://136.243.80.164:2690/job/Torch/job/{0}/" + API_PATH;
private const string ARTIFACT_PATH = "artifact/bin/torch-server.zip";
private const string API_PATH = "api/json";
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
private static JenkinsQuery _instance;
public static JenkinsQuery Instance => _instance ??= new JenkinsQuery();
private HttpClient _client;
private JenkinsQuery()
private const string ApiPath = "api/json";
private readonly HttpClient _client;
public JenkinsQuery(string url)
{
_client = new HttpClient();
}
public async Task<Job> GetLatestVersion(string branch)
{
var h = await _client.GetAsync(string.Format(BRANCH_QUERY, branch));
if (!h.IsSuccessStatusCode)
{
Log.Error($"'{branch}' Branch query failed with code {h.StatusCode}");
if(h.StatusCode == HttpStatusCode.NotFound)
Log.Error("This likely means you're trying to update a branch that is not public on Jenkins. Sorry :(");
return null;
}
var branchResponse = await h.Content.ReadFromJsonAsync<BranchResponse>();
if (branchResponse is null)
{
Log.Error("Error reading branch response");
return null;
}
if (url == null) throw new ArgumentNullException(nameof(url));
h = await _client.GetAsync($"{branchResponse.LastStableBuild.Url}{API_PATH}");
if (h.IsSuccessStatusCode)
return await h.Content.ReadFromJsonAsync<Job>();
Log.Error($"Job query failed with code {h.StatusCode}");
return null;
}
public async Task<bool> DownloadRelease(Job job, string path)
{
var h = await _client.GetAsync(job.Url + ARTIFACT_PATH);
if (!h.IsSuccessStatusCode)
_client = new()
{
Log.Error($"Job download failed with code {h.StatusCode}");
return false;
}
var s = await h.Content.ReadAsStreamAsync();
#if !NETFRAMEWORK
await using var fs = new FileStream(path, FileMode.Create);
#else
using var fs = new FileStream(path, FileMode.Create);
#endif
await s.CopyToAsync(fs);
return true;
BaseAddress = new(url)
};
}
public async Task<UpdateRelease> GetLatestReleaseAsync(string repository, string branch = null)
{
branch ??= "master";
var response = await _client.GetFromJsonAsync<BranchResponse>($"/job/{repository}/job/{branch}/{ApiPath}");
if (response is null)
throw new($"Unable to get latest release for {repository}");
var job = await _client.GetFromJsonAsync<Job>(
$"/job/{repository}/job/{branch}/{response.LastBuild.Number}/{ApiPath}");
if (job is null)
throw new($"Unable to get latest release for job {repository}/{response.LastBuild.Number}");
return new(job.Version, job.Url + job.Artifacts.First(b => b.FileName == "torch-server.zip").RelativePath);
}
public void Dispose()
{
_client?.Dispose();
}
}
public record BranchResponse(string Name, string Url, Build LastBuild, Build LastStableBuild);
@@ -84,5 +54,12 @@ namespace Torch.API.WebAPI
public record Build(int Number, string Url);
public record Job(int Number, bool Building, string Description, string Result, string Url,
[property: JsonConverter(typeof(SemanticVersionConverter))] Version Version);
[property: JsonConverter(typeof(SemanticVersionConverter))] Version Version,
IReadOnlyList<Artifact> Artifacts);
public record Artifact(
string DisplayPath,
string FileName,
string RelativePath
);
}

View File

@@ -0,0 +1,5 @@
using SemanticVersioning;
namespace Torch.API.WebAPI;
public record UpdateRelease(Version Version, string ArtifactUrl);