mirror of
https://github.com/tgstation/tgstation-server.git
synced 2026-08-30 16:39:21 +01:00
Refactor deployment to be completely within the DreamMaker component.
This commit is contained in:
@@ -15,7 +15,7 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
/// <summary>
|
||||
/// Standard <see cref="IDmbFactory"/>
|
||||
/// </summary>
|
||||
sealed class DmbFactory : IDmbFactory, ICompileJobConsumer
|
||||
sealed class DmbFactory : IDmbFactory, ICompileJobSink
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public Task OnNewerDmb
|
||||
@@ -302,5 +302,13 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
await Task.WhenAll(tasks).ConfigureAwait(false);
|
||||
}
|
||||
#pragma warning restore CA1506
|
||||
|
||||
/// <inheritdoc />
|
||||
public CompileJob LatestCompileJob()
|
||||
{
|
||||
if (!DmbAvailable)
|
||||
return null;
|
||||
return LockNextDmb(0)?.CompileJob;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Octokit;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
@@ -13,8 +15,11 @@ using Tgstation.Server.Host.Components.Chat;
|
||||
using Tgstation.Server.Host.Components.Repository;
|
||||
using Tgstation.Server.Host.Components.Session;
|
||||
using Tgstation.Server.Host.Components.Watchdog;
|
||||
using Tgstation.Server.Host.Core;
|
||||
using Tgstation.Server.Host.Database;
|
||||
using Tgstation.Server.Host.IO;
|
||||
using Tgstation.Server.Host.Jobs;
|
||||
using Tgstation.Server.Host.Models;
|
||||
using Tgstation.Server.Host.System;
|
||||
|
||||
namespace Tgstation.Server.Host.Components.Deployment
|
||||
@@ -82,11 +87,31 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
/// </summary>
|
||||
readonly IWatchdog watchdog;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IRepositoryManager"/> for <see cref="DreamMaker"/>.
|
||||
/// </summary>
|
||||
readonly IRepositoryManager repositoryManager;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IGitHubClientFactory"/> for <see cref="DreamMaker"/>.
|
||||
/// </summary>
|
||||
readonly IGitHubClientFactory gitHubClientFactory;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ICompileJobSink"/> for <see cref="DreamMaker"/>.
|
||||
/// </summary>
|
||||
readonly ICompileJobSink compileJobConsumer;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ILogger"/> for <see cref="DreamMaker"/>
|
||||
/// </summary>
|
||||
readonly ILogger<DreamMaker> logger;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="Instance"/> <see cref="DreamMaker"/> belongs to.
|
||||
/// </summary>
|
||||
readonly Api.Models.Instance metadata;
|
||||
|
||||
/// <summary>
|
||||
/// <see langword="lock"/> <see cref="object"/> for <see cref="compiling"/>.
|
||||
/// </summary>
|
||||
@@ -108,7 +133,11 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
/// <param name="chatManager">The value of <see cref="chatManager"/></param>
|
||||
/// <param name="processExecutor">The value of <see cref="processExecutor"/></param>
|
||||
/// <param name="watchdog">The value of <see cref="watchdog"/></param>
|
||||
/// <param name="gitHubClientFactory">The value of <see cref="gitHubClientFactory"/>.</param>
|
||||
/// <param name="compileJobConsumer">The value of <see cref="compileJobConsumer"/>.</param>
|
||||
/// <param name="repositoryManager">The value of <see cref="repositoryManager"/>.</param>
|
||||
/// <param name="logger">The value of <see cref="logger"/></param>
|
||||
/// <param name="metadata">The value of <see cref="metadata"/>.</param>
|
||||
public DreamMaker(
|
||||
IByondManager byond,
|
||||
IIOManager ioManager,
|
||||
@@ -118,7 +147,11 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
IChatManager chatManager,
|
||||
IProcessExecutor processExecutor,
|
||||
IWatchdog watchdog,
|
||||
ILogger<DreamMaker> logger)
|
||||
IGitHubClientFactory gitHubClientFactory,
|
||||
ICompileJobSink compileJobConsumer,
|
||||
IRepositoryManager repositoryManager,
|
||||
ILogger<DreamMaker> logger,
|
||||
Api.Models.Instance metadata)
|
||||
{
|
||||
this.byond = byond ?? throw new ArgumentNullException(nameof(byond));
|
||||
this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
|
||||
@@ -128,7 +161,11 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
this.chatManager = chatManager ?? throw new ArgumentNullException(nameof(chatManager));
|
||||
this.processExecutor = processExecutor ?? throw new ArgumentNullException(nameof(processExecutor));
|
||||
this.watchdog = watchdog ?? throw new ArgumentNullException(nameof(watchdog));
|
||||
this.gitHubClientFactory = gitHubClientFactory ?? throw new ArgumentNullException(nameof(gitHubClientFactory));
|
||||
this.compileJobConsumer = compileJobConsumer ?? throw new ArgumentNullException(nameof(compileJobConsumer));
|
||||
this.repositoryManager = repositoryManager ?? throw new ArgumentNullException(nameof(repositoryManager));
|
||||
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
this.metadata = metadata ?? throw new ArgumentNullException(nameof(metadata));
|
||||
|
||||
compilingLock = new object();
|
||||
}
|
||||
@@ -162,7 +199,7 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
/// </summary>
|
||||
/// <param name="timeout">The timeout in seconds for validation</param>
|
||||
/// <param name="securityLevel">The <see cref="DreamDaemonSecurity"/> level to use to validate the API</param>
|
||||
/// <param name="job">The <see cref="Models.CompileJob"/> for the operation</param>
|
||||
/// <param name="job">The <see cref="CompileJob"/> for the operation</param>
|
||||
/// <param name="byondLock">The current <see cref="IByondExecutableLock"/></param>
|
||||
/// <param name="portToUse">The port to use for API validation</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
|
||||
@@ -231,7 +268,7 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
/// Compiles a .dme with DreamMaker
|
||||
/// </summary>
|
||||
/// <param name="dreamMakerPath">The path to the DreamMaker executable</param>
|
||||
/// <param name="job">The <see cref="Models.CompileJob"/> for the operation</param>
|
||||
/// <param name="job">The <see cref="CompileJob"/> for the operation</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
|
||||
/// <returns>A <see cref="Task"/> representing the running operation</returns>
|
||||
async Task<int> RunDreamMaker(string dreamMakerPath, Models.CompileJob job, CancellationToken cancellationToken)
|
||||
@@ -260,7 +297,7 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
/// <summary>
|
||||
/// Adds server side includes to the .dme being compiled
|
||||
/// </summary>
|
||||
/// <param name="job">The <see cref="Models.CompileJob"/> for the operation</param>
|
||||
/// <param name="job">The <see cref="CompileJob"/> for the operation</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
|
||||
/// <returns>A <see cref="Task"/> representing the running operation</returns>
|
||||
async Task ModifyDme(Models.CompileJob job, CancellationToken cancellationToken)
|
||||
@@ -314,7 +351,7 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
/// <summary>
|
||||
/// Cleans up a failed compile <paramref name="job"/>
|
||||
/// </summary>
|
||||
/// <param name="job">The running <see cref="Models.CompileJob"/></param>
|
||||
/// <param name="job">The running <see cref="CompileJob"/></param>
|
||||
/// <param name="cancelled">If the <paramref name="job"/> was cancelled</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
|
||||
/// <returns>A <see cref="Task"/> representing the running operation</returns>
|
||||
@@ -338,7 +375,7 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
/// <summary>
|
||||
/// Send a message to <see cref="chatManager"/> about a deployment
|
||||
/// </summary>
|
||||
/// <param name="revisionInformation">The <see cref="Models.RevisionInformation"/> for the deployment</param>
|
||||
/// <param name="revisionInformation">The <see cref="RevisionInformation"/> for the deployment</param>
|
||||
/// <param name="byondLock">The <see cref="IByondExecutableLock"/> for the deployment</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
|
||||
/// <returns>A <see cref="Task"/> representing the running operation</returns>
|
||||
@@ -396,7 +433,7 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
/// <summary>
|
||||
/// Executes and populate a given <paramref name="job"/>
|
||||
/// </summary>
|
||||
/// <param name="job">The <see cref="Models.CompileJob"/> to run and populate</param>
|
||||
/// <param name="job">The <see cref="CompileJob"/> to run and populate</param>
|
||||
/// <param name="dreamMakerSettings">The <see cref="Api.Models.DreamMaker"/> settings to use</param>
|
||||
/// <param name="byondLock">The <see cref="IByondExecutableLock"/> to use</param>
|
||||
/// <param name="repository">The <see cref="IRepository"/> to use</param>
|
||||
@@ -484,14 +521,6 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
|
||||
await Task.WhenAll(symATask, symBTask).ConfigureAwait(false);
|
||||
|
||||
await chatManager.SendUpdateMessage(
|
||||
String.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
"Deployment complete! Changes will be applied when DreamDaemon {0}.",
|
||||
watchdog.Running ? "reboots" : "is launched"),
|
||||
cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
logger.LogDebug("Compile complete!");
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -502,20 +531,180 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Models.CompileJob> Compile(Models.RevisionInformation revisionInformation, Api.Models.DreamMaker dreamMakerSettings, uint apiValidateTimeout, IRepository repository, Action<int> progressReporter, TimeSpan? estimatedDuration, CancellationToken cancellationToken)
|
||||
public async Task DeploymentProcess(
|
||||
Models.Job job,
|
||||
IDatabaseContext databaseContext,
|
||||
Action<int> progressReporter,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (revisionInformation == null)
|
||||
throw new ArgumentNullException(nameof(revisionInformation));
|
||||
|
||||
if (dreamMakerSettings == null)
|
||||
throw new ArgumentNullException(nameof(dreamMakerSettings));
|
||||
|
||||
if (repository == null)
|
||||
throw new ArgumentNullException(nameof(repository));
|
||||
|
||||
#pragma warning disable IDE0016 // Use 'throw' expression
|
||||
if (job == null)
|
||||
throw new ArgumentNullException(nameof(job));
|
||||
#pragma warning restore IDE0016 // Use 'throw' expression
|
||||
if (databaseContext == null)
|
||||
throw new ArgumentNullException(nameof(databaseContext));
|
||||
if (progressReporter == null)
|
||||
throw new ArgumentNullException(nameof(progressReporter));
|
||||
|
||||
var averageSpan = await CalculateExpectedDeploymentTime(databaseContext, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var ddSettings = await databaseContext
|
||||
.DreamDaemonSettings
|
||||
.Where(x => x.InstanceId == metadata.Id)
|
||||
.Select(x => new Models.DreamDaemonSettings
|
||||
{
|
||||
StartupTimeout = x.StartupTimeout,
|
||||
})
|
||||
.FirstOrDefaultAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
if (ddSettings == default)
|
||||
throw new JobException(ErrorCode.InstanceMissingDreamDaemonSettings);
|
||||
|
||||
var dreamMakerSettings = await databaseContext.DreamMakerSettings.Where(x => x.InstanceId == metadata.Id).FirstAsync(cancellationToken).ConfigureAwait(false);
|
||||
if (dreamMakerSettings == default)
|
||||
throw new JobException(ErrorCode.InstanceMissingDreamMakerSettings);
|
||||
|
||||
Models.RepositorySettings repositorySettings = null;
|
||||
string repoOwner = null;
|
||||
string repoName = null;
|
||||
Models.CompileJob compileJob;
|
||||
Models.RevisionInformation revInfo;
|
||||
|
||||
using (var repo = await repositoryManager.LoadRepository(cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
if (repo == null)
|
||||
throw new JobException(ErrorCode.RepoMissing);
|
||||
|
||||
if (repo.IsGitHubRepository)
|
||||
{
|
||||
repoOwner = repo.GitHubOwner;
|
||||
repoName = repo.GitHubRepoName;
|
||||
repositorySettings = await databaseContext
|
||||
.RepositorySettings
|
||||
.Where(x => x.InstanceId == metadata.Id)
|
||||
.Select(x => new Models.RepositorySettings
|
||||
{
|
||||
AccessToken = x.AccessToken,
|
||||
ShowTestMergeCommitters = x.ShowTestMergeCommitters,
|
||||
PushTestMergeCommits = x.PushTestMergeCommits,
|
||||
PostTestMergeComment = x.PostTestMergeComment
|
||||
})
|
||||
.FirstOrDefaultAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
if (repositorySettings == default)
|
||||
throw new JobException(ErrorCode.InstanceMissingRepositorySettings);
|
||||
}
|
||||
|
||||
var repoSha = repo.Head;
|
||||
revInfo = await databaseContext
|
||||
.RevisionInformations
|
||||
.Where(x => x.CommitSha == repoSha && x.Instance.Id == metadata.Id)
|
||||
.Include(x => x.ActiveTestMerges)
|
||||
.ThenInclude(x => x.TestMerge)
|
||||
.ThenInclude(x => x.MergedBy)
|
||||
.FirstOrDefaultAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (revInfo == default)
|
||||
{
|
||||
revInfo = new Models.RevisionInformation
|
||||
{
|
||||
CommitSha = repoSha,
|
||||
OriginCommitSha = repoSha,
|
||||
Instance = new Models.Instance
|
||||
{
|
||||
Id = metadata.Id
|
||||
}
|
||||
};
|
||||
|
||||
logger.LogWarning(Repository.Repository.OriginTrackingErrorTemplate, repoSha);
|
||||
databaseContext.Instances.Attach(revInfo.Instance);
|
||||
}
|
||||
|
||||
compileJob = await Compile(
|
||||
revInfo,
|
||||
dreamMakerSettings,
|
||||
ddSettings.StartupTimeout.Value,
|
||||
repo,
|
||||
progressReporter,
|
||||
averageSpan,
|
||||
cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
try
|
||||
{
|
||||
compileJob.Job = job;
|
||||
|
||||
databaseContext.CompileJobs.Add(compileJob);
|
||||
|
||||
await PostDeploymentComments(compileJob, repositorySettings, repoOwner, repoName).ConfigureAwait(false);
|
||||
|
||||
// The difficulty with compile jobs is they have a two part commit
|
||||
await databaseContext.Save(cancellationToken).ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
await compileJobConsumer.LoadCompileJob(compileJob, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// So we need to un-commit the compile job if the above throws
|
||||
databaseContext.CompileJobs.Remove(compileJob);
|
||||
await databaseContext.Save(default).ConfigureAwait(false);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await CleanupFailedCompile(compileJob, ex is OperationCanceledException, default).ConfigureAwait(false);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
await eventConsumer.HandleEvent(EventType.DeploymentComplete, null, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
await chatManager.SendUpdateMessage(
|
||||
String.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
"Deployment complete! Changes will be applied when DreamDaemon {0}.",
|
||||
watchdog.Running ? "reboots" : "is launched"),
|
||||
cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculate the average length of a deployment using a given <paramref name="databaseContext"/>.
|
||||
/// </summary>
|
||||
/// <param name="databaseContext">The <see cref="IDatabaseContext"/> to retrieve previous deployment <see cref="Job"/>s from.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in the average <see cref="TimeSpan"/> of the 10 previous deployments or <see langword="null"/> if there are none.</returns>
|
||||
async Task<TimeSpan?> CalculateExpectedDeploymentTime(IDatabaseContext databaseContext, CancellationToken cancellationToken)
|
||||
{
|
||||
var previousCompileJobs = await databaseContext.CompileJobs
|
||||
.Where(x => x.Job.Instance.Id == metadata.Id)
|
||||
.OrderByDescending(x => x.Job.StoppedAt)
|
||||
.Take(10)
|
||||
.Select(x => new Models.Job
|
||||
{
|
||||
StoppedAt = x.Job.StoppedAt,
|
||||
StartedAt = x.Job.StartedAt
|
||||
})
|
||||
.ToListAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
TimeSpan? averageSpan = null;
|
||||
if (previousCompileJobs.Count != 0)
|
||||
{
|
||||
var totalSpan = TimeSpan.Zero;
|
||||
foreach (var I in previousCompileJobs)
|
||||
totalSpan += I.StoppedAt.Value - I.StartedAt.Value;
|
||||
averageSpan = totalSpan / previousCompileJobs.Count;
|
||||
}
|
||||
|
||||
return averageSpan;
|
||||
}
|
||||
|
||||
async Task<Models.CompileJob> Compile(Models.RevisionInformation revisionInformation, Api.Models.DreamMaker dreamMakerSettings, uint apiValidateTimeout, IRepository repository, Action<int> progressReporter, TimeSpan? estimatedDuration, CancellationToken cancellationToken)
|
||||
{
|
||||
logger.LogTrace("Begin Compile");
|
||||
|
||||
lock (compilingLock)
|
||||
@@ -556,5 +745,99 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
await progressTask.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Post deployment GitHub comments.
|
||||
/// </summary>
|
||||
/// <param name="compileJob">The deployed <see cref="CompileJob"/>.</param>
|
||||
/// <param name="repositorySettings">The <see cref="RepositorySettings"/>.</param>
|
||||
/// <param name="repoOwner">The GitHub repostiory owner.</param>
|
||||
/// <param name="repoName">The GitHub repostiory name.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the running operation.</returns>
|
||||
async Task PostDeploymentComments(
|
||||
Models.CompileJob compileJob,
|
||||
Models.RepositorySettings repositorySettings,
|
||||
string repoOwner,
|
||||
string repoName)
|
||||
{
|
||||
if (repositorySettings?.AccessToken == null)
|
||||
return;
|
||||
|
||||
// potential for commenting on a test merge change
|
||||
var outgoingCompileJob = compileJobConsumer.LatestCompileJob();
|
||||
|
||||
if ((outgoingCompileJob != null && outgoingCompileJob.RevisionInformation.CommitSha == compileJob.RevisionInformation.CommitSha) || !repositorySettings.PostTestMergeComment.Value)
|
||||
return;
|
||||
|
||||
outgoingCompileJob ??= new Models.CompileJob
|
||||
{
|
||||
RevisionInformation = new Models.RevisionInformation
|
||||
{
|
||||
ActiveTestMerges = new List<RevInfoTestMerge>()
|
||||
}
|
||||
};
|
||||
|
||||
var gitHubClient = gitHubClientFactory.CreateClient(repositorySettings.AccessToken);
|
||||
|
||||
async Task CommentOnPR(int prNumber, string comment)
|
||||
{
|
||||
try
|
||||
{
|
||||
await gitHubClient.Issue.Comment.Create(repoOwner, repoName, prNumber, comment).ConfigureAwait(false);
|
||||
}
|
||||
catch (ApiException e)
|
||||
{
|
||||
logger.LogWarning("Error posting GitHub comment! Exception: {0}", e);
|
||||
}
|
||||
}
|
||||
|
||||
var tasks = new List<Task>();
|
||||
|
||||
string FormatTestMerge(Models.TestMerge testMerge, bool updated) => String.Format(CultureInfo.InvariantCulture, "#### Test Merge {4}{0}{0}##### Server Instance{0}{5}{1}{0}{0}##### Revision{0}Origin: {6}{0}Pull Request: {2}{0}Server: {7}{3}",
|
||||
Environment.NewLine,
|
||||
repositorySettings.ShowTestMergeCommitters.Value ? String.Format(CultureInfo.InvariantCulture, "{0}{0}##### Merged By{0}{1}", Environment.NewLine, testMerge.MergedBy.Name) : String.Empty,
|
||||
testMerge.PullRequestRevision,
|
||||
testMerge.Comment != null ? String.Format(CultureInfo.InvariantCulture, "{0}{0}##### Comment{0}{1}", Environment.NewLine, testMerge.Comment) : String.Empty,
|
||||
updated ? "Updated" : "Deployed",
|
||||
metadata.Name,
|
||||
compileJob.RevisionInformation.OriginCommitSha,
|
||||
compileJob.RevisionInformation.CommitSha);
|
||||
|
||||
// added prs
|
||||
foreach (var I in compileJob
|
||||
.RevisionInformation
|
||||
.ActiveTestMerges
|
||||
.Select(x => x.TestMerge)
|
||||
.Where(x => !outgoingCompileJob
|
||||
.RevisionInformation
|
||||
.ActiveTestMerges
|
||||
.Any(y => y.TestMerge.Number == x.Number)))
|
||||
tasks.Add(CommentOnPR(I.Number, FormatTestMerge(I, false)));
|
||||
|
||||
// removed prs
|
||||
foreach (var I in outgoingCompileJob
|
||||
.RevisionInformation
|
||||
.ActiveTestMerges
|
||||
.Select(x => x.TestMerge)
|
||||
.Where(x => !compileJob
|
||||
.RevisionInformation
|
||||
.ActiveTestMerges
|
||||
.Any(y => y.TestMerge.Number == x.Number)))
|
||||
tasks.Add(CommentOnPR(I.Number, "#### Test Merge Removed"));
|
||||
|
||||
// updated prs
|
||||
foreach (var I in compileJob
|
||||
.RevisionInformation
|
||||
.ActiveTestMerges
|
||||
.Select(x => x.TestMerge)
|
||||
.Where(x => outgoingCompileJob
|
||||
.RevisionInformation
|
||||
.ActiveTestMerges
|
||||
.Any(y => y.TestMerge.Number == x.Number)))
|
||||
tasks.Add(CommentOnPR(I.Number, FormatTestMerge(I, true)));
|
||||
|
||||
if (tasks.Any())
|
||||
await Task.WhenAll(tasks).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-5
@@ -1,6 +1,4 @@
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Tgstation.Server.Host.Models;
|
||||
|
||||
@@ -9,10 +7,10 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
/// <summary>
|
||||
/// Sink for <see cref="CompileJob"/>s
|
||||
/// </summary>
|
||||
public interface ICompileJobConsumer : IHostedService, IDisposable
|
||||
public interface ICompileJobSink : ILatestCompileJobProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Load a new <paramref name="job"/> into the <see cref="ICompileJobConsumer"/>
|
||||
/// Load a new <paramref name="job"/> into the <see cref="ICompileJobSink"/>
|
||||
/// </summary>
|
||||
/// <param name="job">The <see cref="CompileJob"/> to load</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Tgstation.Server.Host.Models;
|
||||
@@ -8,7 +9,7 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
/// <summary>
|
||||
/// Factory for <see cref="IDmbProvider"/>s
|
||||
/// </summary>
|
||||
public interface IDmbFactory
|
||||
public interface IDmbFactory : ILatestCompileJobProvider, IHostedService, IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Get a <see cref="Task"/> that completes when the result of a call to <see cref="LockNextDmb"/> will be different than the previous call if any
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Tgstation.Server.Host.Components.Repository;
|
||||
using Tgstation.Server.Host.Database;
|
||||
using Tgstation.Server.Host.Models;
|
||||
|
||||
namespace Tgstation.Server.Host.Components.Deployment
|
||||
{
|
||||
@@ -11,16 +12,17 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
public interface IDreamMaker
|
||||
{
|
||||
/// <summary>
|
||||
/// Starts a compile
|
||||
/// Create and a compile job and insert it into the database. Meant to be called by a <see cref="Jobs.IJobManager"/>.
|
||||
/// </summary>
|
||||
/// <param name="revisionInformation">The <see cref="Models.RevisionInformation"/> being compiled from the <paramref name="repository"/></param>
|
||||
/// <param name="dreamMakerSettings">The <see cref="Api.Models.DreamMaker"/> for the compile</param>
|
||||
/// <param name="apiValidateTimeout">The time in seconds to wait while validating the API</param>
|
||||
/// <param name="repository">The <see cref="IRepository"/> to copy from</param>
|
||||
/// <param name="progressReporter">The <see cref="Action{T1}"/> to report compilation progress</param>
|
||||
/// <param name="estimatedDuration">The estimated amount of time the compile will take</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in the partially populated <see cref="Models.CompileJob"/> for the operation. In particular, note the <see cref="Models.CompileJob.RevisionInformation"/> field will only have it's <see cref="Api.Models.Internal.RevisionInformation.CommitSha"/> field populated</returns>
|
||||
Task<Models.CompileJob> Compile(Models.RevisionInformation revisionInformation, Api.Models.DreamMaker dreamMakerSettings, uint apiValidateTimeout, IRepository repository, Action<int> progressReporter, TimeSpan? estimatedDuration, CancellationToken cancellationToken);
|
||||
/// <param name="job">The running <see cref="Job"/>.</param>
|
||||
/// <param name="databaseContext">The <see cref="IDatabaseContext"/> for the operation.</param>
|
||||
/// <param name="progressReporter">The <see cref="Action{T1}"/> to report compilation progress.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the running operation.</returns>
|
||||
Task DeploymentProcess(
|
||||
Job job,
|
||||
IDatabaseContext databaseContext,
|
||||
Action<int> progressReporter,
|
||||
CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using Tgstation.Server.Host.Models;
|
||||
|
||||
namespace Tgstation.Server.Host.Components.Deployment
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides the most recently deployed <see cref="CompileJob"/>.
|
||||
/// </summary>
|
||||
public interface ILatestCompileJobProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the latest <see cref="CompileJob"/>.
|
||||
/// </summary>
|
||||
/// <returns>The latest <see cref="CompileJob"/>.</returns>
|
||||
CompileJob LatestCompileJob();
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,19 @@
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Tgstation.Server.Host.Components.Byond;
|
||||
using Tgstation.Server.Host.Components.Chat;
|
||||
using Tgstation.Server.Host.Components.Deployment;
|
||||
using Tgstation.Server.Host.Components.Repository;
|
||||
using Tgstation.Server.Host.Components.StaticFiles;
|
||||
using Tgstation.Server.Host.Components.Watchdog;
|
||||
using Tgstation.Server.Host.Database;
|
||||
using Tgstation.Server.Host.Models;
|
||||
|
||||
namespace Tgstation.Server.Host.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// For interacting with the instance services
|
||||
/// </summary>
|
||||
public interface IInstance : IHostedService, IDisposable
|
||||
public interface IInstance : ILatestCompileJobProvider, IHostedService, IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="IRepositoryManager"/> for the <see cref="IInstance"/>
|
||||
@@ -27,6 +25,11 @@ namespace Tgstation.Server.Host.Components
|
||||
/// </summary>
|
||||
IByondManager ByondManager { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IDreamMaker"/> for the <see cref="IInstance"/>.
|
||||
/// </summary>
|
||||
IDreamMaker DreamMaker { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IWatchdog"/> for the <see cref="IInstance"/>
|
||||
/// </summary>
|
||||
@@ -42,12 +45,6 @@ namespace Tgstation.Server.Host.Components
|
||||
/// </summary>
|
||||
IConfiguration Configuration { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The latest staged <see cref="CompileJob"/>
|
||||
/// </summary>
|
||||
/// <returns>The latest <see cref="CompileJob"/> if it exists</returns>
|
||||
CompileJob LatestCompileJob();
|
||||
|
||||
/// <summary>
|
||||
/// Rename the <see cref="IInstance"/>
|
||||
/// </summary>
|
||||
@@ -60,15 +57,5 @@ namespace Tgstation.Server.Host.Components
|
||||
/// <param name="newInterval">The new auto update inteval</param>
|
||||
/// <returns>A <see cref="Task"/> representing the running operation</returns>
|
||||
Task SetAutoUpdateInterval(uint newInterval);
|
||||
|
||||
/// <summary>
|
||||
/// Run the compile job and insert it into the database. Meant to be called by a <see cref="Jobs.IJobManager"/>
|
||||
/// </summary>
|
||||
/// <param name="job">The running <see cref="Job"/></param>
|
||||
/// <param name="databaseContext">The <see cref="IDatabaseContext"/> for the operation</param>
|
||||
/// <param name="progressReporter">The <see cref="Action{T1}"/> to report compilation progress</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
|
||||
/// <returns>A <see cref="Task"/> representing the running operation</returns>
|
||||
Task CompileProcess(Job job, IDatabaseContext databaseContext, Action<int> progressReporter, CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,7 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Octokit;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@@ -13,7 +11,6 @@ using Tgstation.Server.Host.Components.Chat;
|
||||
using Tgstation.Server.Host.Components.Deployment;
|
||||
using Tgstation.Server.Host.Components.Repository;
|
||||
using Tgstation.Server.Host.Components.Watchdog;
|
||||
using Tgstation.Server.Host.Core;
|
||||
using Tgstation.Server.Host.Database;
|
||||
using Tgstation.Server.Host.Jobs;
|
||||
using Tgstation.Server.Host.Models;
|
||||
@@ -39,15 +36,8 @@ namespace Tgstation.Server.Host.Components
|
||||
/// <inheritdoc />
|
||||
public StaticFiles.IConfiguration Configuration { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IDreamMaker"/> for the <see cref="Instance"/>
|
||||
/// </summary>
|
||||
readonly IDreamMaker dreamMaker;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ICompileJobConsumer"/> for the <see cref="Instance"/>
|
||||
/// </summary>
|
||||
readonly ICompileJobConsumer compileJobConsumer;
|
||||
/// <inheritdoc />
|
||||
public IDreamMaker DreamMaker { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IDatabaseContextFactory"/> for the <see cref="Instance"/>
|
||||
@@ -69,11 +59,6 @@ namespace Tgstation.Server.Host.Components
|
||||
/// </summary>
|
||||
readonly IEventConsumer eventConsumer;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IGitHubClientFactory"/> for the <see cref="Instance"/>
|
||||
/// </summary>
|
||||
readonly IGitHubClientFactory gitHubClientFactory;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ILogger"/> for the <see cref="Instance"/>
|
||||
/// </summary>
|
||||
@@ -105,16 +90,14 @@ namespace Tgstation.Server.Host.Components
|
||||
/// <param name="metadata">The value of <see cref="metadata"/></param>
|
||||
/// <param name="repositoryManager">The value of <see cref="RepositoryManager"/></param>
|
||||
/// <param name="byondManager">The value of <see cref="ByondManager"/></param>
|
||||
/// <param name="dreamMaker">The value of <see cref="dreamMaker"/></param>
|
||||
/// <param name="dreamMaker">The value of <see cref="DreamMaker"/></param>
|
||||
/// <param name="watchdog">The value of <see cref="Watchdog"/></param>
|
||||
/// <param name="chat">The value of <see cref="Chat"/></param>
|
||||
/// <param name="configuration">The value of <see cref="Configuration"/></param>
|
||||
/// <param name="compileJobConsumer">The value of <see cref="compileJobConsumer"/></param>
|
||||
/// <param name="databaseContextFactory">The value of <see cref="databaseContextFactory"/></param>
|
||||
/// <param name="dmbFactory">The value of <see cref="dmbFactory"/></param>
|
||||
/// <param name="jobManager">The value of <see cref="jobManager"/></param>
|
||||
/// <param name="eventConsumer">The value of <see cref="eventConsumer"/></param>
|
||||
/// <param name="gitHubClientFactory">The value of <see cref="gitHubClientFactory"/></param>
|
||||
/// <param name="logger">The value of <see cref="logger"/></param>
|
||||
public Instance(
|
||||
Api.Models.Instance metadata,
|
||||
@@ -125,27 +108,23 @@ namespace Tgstation.Server.Host.Components
|
||||
IChatManager chat,
|
||||
StaticFiles.IConfiguration
|
||||
configuration,
|
||||
ICompileJobConsumer compileJobConsumer,
|
||||
IDatabaseContextFactory databaseContextFactory,
|
||||
IDmbFactory dmbFactory,
|
||||
IJobManager jobManager,
|
||||
IEventConsumer eventConsumer,
|
||||
IGitHubClientFactory gitHubClientFactory,
|
||||
ILogger<Instance> logger)
|
||||
{
|
||||
this.metadata = metadata ?? throw new ArgumentNullException(nameof(metadata));
|
||||
RepositoryManager = repositoryManager ?? throw new ArgumentNullException(nameof(repositoryManager));
|
||||
ByondManager = byondManager ?? throw new ArgumentNullException(nameof(byondManager));
|
||||
this.dreamMaker = dreamMaker ?? throw new ArgumentNullException(nameof(dreamMaker));
|
||||
DreamMaker = dreamMaker ?? throw new ArgumentNullException(nameof(dreamMaker));
|
||||
Watchdog = watchdog ?? throw new ArgumentNullException(nameof(watchdog));
|
||||
Chat = chat ?? throw new ArgumentNullException(nameof(chat));
|
||||
Configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
|
||||
this.compileJobConsumer = compileJobConsumer ?? throw new ArgumentNullException(nameof(compileJobConsumer));
|
||||
this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory));
|
||||
this.dmbFactory = dmbFactory ?? throw new ArgumentNullException(nameof(dmbFactory));
|
||||
this.jobManager = jobManager ?? throw new ArgumentNullException(nameof(jobManager));
|
||||
this.eventConsumer = eventConsumer ?? throw new ArgumentNullException(nameof(eventConsumer));
|
||||
this.gitHubClientFactory = gitHubClientFactory ?? throw new ArgumentNullException(nameof(gitHubClientFactory));
|
||||
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
|
||||
timerLock = new object();
|
||||
@@ -155,227 +134,13 @@ namespace Tgstation.Server.Host.Components
|
||||
public void Dispose()
|
||||
{
|
||||
timerCts?.Dispose();
|
||||
compileJobConsumer.Dispose();
|
||||
Configuration.Dispose();
|
||||
Chat.Dispose();
|
||||
Watchdog.Dispose();
|
||||
dmbFactory.Dispose();
|
||||
RepositoryManager.Dispose();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task CompileProcess(Job job, IDatabaseContext databaseContext, Action<int> progressReporter, CancellationToken cancellationToken)
|
||||
{
|
||||
#pragma warning disable IDE0016 // Use 'throw' expression
|
||||
if (job == null)
|
||||
throw new ArgumentNullException(nameof(job));
|
||||
#pragma warning restore IDE0016 // Use 'throw' expression
|
||||
if (databaseContext == null)
|
||||
throw new ArgumentNullException(nameof(databaseContext));
|
||||
if (progressReporter == null)
|
||||
throw new ArgumentNullException(nameof(progressReporter));
|
||||
|
||||
var ddSettings = await databaseContext.DreamDaemonSettings.Where(x => x.InstanceId == metadata.Id).Select(x => new DreamDaemonSettings
|
||||
{
|
||||
StartupTimeout = x.StartupTimeout,
|
||||
})
|
||||
.FirstOrDefaultAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
if (ddSettings == default)
|
||||
throw new JobException(Api.Models.ErrorCode.InstanceMissingDreamDaemonSettings);
|
||||
|
||||
var previousCompileJobs = await databaseContext.CompileJobs
|
||||
.Where(x => x.Job.Instance.Id == metadata.Id)
|
||||
.OrderByDescending(x => x.Job.StoppedAt)
|
||||
.Select(x => new Job
|
||||
{
|
||||
StoppedAt = x.Job.StoppedAt,
|
||||
StartedAt = x.Job.StartedAt
|
||||
})
|
||||
.Take(10)
|
||||
.ToListAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
var dreamMakerSettings = await databaseContext.DreamMakerSettings.Where(x => x.InstanceId == metadata.Id).FirstAsync(cancellationToken).ConfigureAwait(false);
|
||||
if (dreamMakerSettings == default)
|
||||
throw new JobException(Api.Models.ErrorCode.InstanceMissingDreamMakerSettings);
|
||||
|
||||
RepositorySettings repositorySettings = null;
|
||||
string repoOwner = null;
|
||||
string repoName = null;
|
||||
CompileJob compileJob;
|
||||
RevisionInformation revInfo;
|
||||
using (var repo = await RepositoryManager.LoadRepository(cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
if (repo == null)
|
||||
throw new JobException(Api.Models.ErrorCode.RepoMissing);
|
||||
|
||||
if (repo.IsGitHubRepository)
|
||||
{
|
||||
repoOwner = repo.GitHubOwner;
|
||||
repoName = repo.GitHubRepoName;
|
||||
repositorySettings = await databaseContext
|
||||
.RepositorySettings
|
||||
.Where(x => x.InstanceId == metadata.Id)
|
||||
.Select(x => new RepositorySettings
|
||||
{
|
||||
AccessToken = x.AccessToken,
|
||||
ShowTestMergeCommitters = x.ShowTestMergeCommitters,
|
||||
PushTestMergeCommits = x.PushTestMergeCommits,
|
||||
PostTestMergeComment = x.PostTestMergeComment
|
||||
})
|
||||
.FirstOrDefaultAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
if (repositorySettings == default)
|
||||
throw new JobException(Api.Models.ErrorCode.InstanceMissingRepositorySettings);
|
||||
}
|
||||
|
||||
var repoSha = repo.Head;
|
||||
revInfo = await databaseContext.RevisionInformations.Where(x => x.CommitSha == repoSha && x.Instance.Id == metadata.Id).Include(x => x.ActiveTestMerges).ThenInclude(x => x.TestMerge).ThenInclude(x => x.MergedBy).FirstOrDefaultAsync().ConfigureAwait(false);
|
||||
|
||||
if (revInfo == default)
|
||||
{
|
||||
revInfo = new RevisionInformation
|
||||
{
|
||||
CommitSha = repoSha,
|
||||
OriginCommitSha = repoSha,
|
||||
Instance = new Models.Instance
|
||||
{
|
||||
Id = metadata.Id
|
||||
}
|
||||
};
|
||||
logger.LogWarning(Repository.Repository.OriginTrackingErrorTemplate, repoSha);
|
||||
databaseContext.Instances.Attach(revInfo.Instance);
|
||||
}
|
||||
|
||||
TimeSpan? averageSpan = null;
|
||||
if (previousCompileJobs.Count != 0)
|
||||
{
|
||||
var totalSpan = TimeSpan.Zero;
|
||||
foreach (var I in previousCompileJobs)
|
||||
totalSpan += I.StoppedAt.Value - I.StartedAt.Value;
|
||||
averageSpan = totalSpan / previousCompileJobs.Count;
|
||||
}
|
||||
|
||||
compileJob = await dreamMaker.Compile(revInfo, dreamMakerSettings, ddSettings.StartupTimeout.Value, repo, progressReporter, averageSpan, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
compileJob.Job = job;
|
||||
|
||||
databaseContext.CompileJobs.Add(compileJob);
|
||||
|
||||
await PostDeploymentComments(compileJob, repositorySettings, repoOwner, repoName).ConfigureAwait(false);
|
||||
|
||||
// The difficulty with compile jobs is they have a two part commit
|
||||
await databaseContext.Save(cancellationToken).ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
await compileJobConsumer.LoadCompileJob(compileJob, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// So we need to un-commit the compile job if the above throws
|
||||
databaseContext.CompileJobs.Remove(compileJob);
|
||||
await databaseContext.Save(default).ConfigureAwait(false);
|
||||
throw;
|
||||
}
|
||||
|
||||
await eventConsumer.HandleEvent(EventType.DeploymentComplete, null, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Post deployment GitHub comments.
|
||||
/// </summary>
|
||||
/// <param name="compileJob">The deployed <see cref="CompileJob"/>.</param>
|
||||
/// <param name="repositorySettings">The <see cref="RepositorySettings"/>.</param>
|
||||
/// <param name="repoOwner">The GitHub repostiory owner.</param>
|
||||
/// <param name="repoName">The GitHub repostiory name.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the running operation.</returns>
|
||||
async Task PostDeploymentComments(
|
||||
CompileJob compileJob,
|
||||
RepositorySettings repositorySettings,
|
||||
string repoOwner,
|
||||
string repoName)
|
||||
{
|
||||
if (repositorySettings?.AccessToken == null)
|
||||
return;
|
||||
|
||||
// potential for commenting on a test merge change
|
||||
var outgoingCompileJob = LatestCompileJob();
|
||||
|
||||
if ((outgoingCompileJob != null && outgoingCompileJob.RevisionInformation.CommitSha == compileJob.RevisionInformation.CommitSha) || !repositorySettings.PostTestMergeComment.Value)
|
||||
return;
|
||||
|
||||
outgoingCompileJob ??= new CompileJob
|
||||
{
|
||||
RevisionInformation = new RevisionInformation
|
||||
{
|
||||
ActiveTestMerges = new List<RevInfoTestMerge>()
|
||||
}
|
||||
};
|
||||
|
||||
var gitHubClient = gitHubClientFactory.CreateClient(repositorySettings.AccessToken);
|
||||
|
||||
async Task CommentOnPR(int prNumber, string comment)
|
||||
{
|
||||
try
|
||||
{
|
||||
await gitHubClient.Issue.Comment.Create(repoOwner, repoName, prNumber, comment).ConfigureAwait(false);
|
||||
}
|
||||
catch (ApiException e)
|
||||
{
|
||||
logger.LogWarning("Error posting GitHub comment! Exception: {0}", e);
|
||||
}
|
||||
}
|
||||
|
||||
var tasks = new List<Task>();
|
||||
|
||||
string FormatTestMerge(TestMerge testMerge, bool updated) => String.Format(CultureInfo.InvariantCulture, "#### Test Merge {4}{0}{0}##### Server Instance{0}{5}{1}{0}{0}##### Revision{0}Origin: {6}{0}Pull Request: {2}{0}Server: {7}{3}",
|
||||
Environment.NewLine,
|
||||
repositorySettings.ShowTestMergeCommitters.Value ? String.Format(CultureInfo.InvariantCulture, "{0}{0}##### Merged By{0}{1}", Environment.NewLine, testMerge.MergedBy.Name) : String.Empty,
|
||||
testMerge.PullRequestRevision,
|
||||
testMerge.Comment != null ? String.Format(CultureInfo.InvariantCulture, "{0}{0}##### Comment{0}{1}", Environment.NewLine, testMerge.Comment) : String.Empty,
|
||||
updated ? "Updated" : "Deployed",
|
||||
metadata.Name,
|
||||
compileJob.RevisionInformation.OriginCommitSha,
|
||||
compileJob.RevisionInformation.CommitSha);
|
||||
|
||||
// added prs
|
||||
foreach (var I in compileJob
|
||||
.RevisionInformation
|
||||
.ActiveTestMerges
|
||||
.Select(x => x.TestMerge)
|
||||
.Where(x => !outgoingCompileJob
|
||||
.RevisionInformation
|
||||
.ActiveTestMerges
|
||||
.Any(y => y.TestMerge.Number == x.Number)))
|
||||
tasks.Add(CommentOnPR(I.Number, FormatTestMerge(I, false)));
|
||||
|
||||
// removed prs
|
||||
foreach (var I in outgoingCompileJob
|
||||
.RevisionInformation
|
||||
.ActiveTestMerges
|
||||
.Select(x => x.TestMerge)
|
||||
.Where(x => !compileJob
|
||||
.RevisionInformation
|
||||
.ActiveTestMerges
|
||||
.Any(y => y.TestMerge.Number == x.Number)))
|
||||
tasks.Add(CommentOnPR(I.Number, "#### Test Merge Removed"));
|
||||
|
||||
// updated prs
|
||||
foreach (var I in compileJob
|
||||
.RevisionInformation
|
||||
.ActiveTestMerges
|
||||
.Select(x => x.TestMerge)
|
||||
.Where(x => outgoingCompileJob
|
||||
.RevisionInformation
|
||||
.ActiveTestMerges
|
||||
.Any(y => y.TestMerge.Number == x.Number)))
|
||||
tasks.Add(CommentOnPR(I.Number, FormatTestMerge(I, true)));
|
||||
|
||||
if (tasks.Any())
|
||||
await Task.WhenAll(tasks).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pull the repository and compile for every set of given <paramref name="minutes"/>
|
||||
/// </summary>
|
||||
@@ -393,11 +158,11 @@ namespace Tgstation.Server.Host.Components
|
||||
await eventConsumer.HandleEvent(EventType.InstanceAutoUpdateStart, new List<string>(), cancellationToken).ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
Models.User user = null;
|
||||
User user = null;
|
||||
await databaseContextFactory.UseContext(
|
||||
async (db) => user = await db
|
||||
.Users
|
||||
.Where(x => x.CanonicalName == Models.User.CanonicalizeName(Api.Models.User.AdminName))
|
||||
.Where(x => x.CanonicalName == User.CanonicalizeName(Api.Models.User.AdminName))
|
||||
.FirstAsync(cancellationToken)
|
||||
.ConfigureAwait(false))
|
||||
.ConfigureAwait(false);
|
||||
@@ -582,7 +347,10 @@ namespace Tgstation.Server.Host.Components
|
||||
CancelRight = (ulong)DreamMakerRights.CancelCompile
|
||||
};
|
||||
|
||||
await jobManager.RegisterOperation(compileProcessJob, CompileProcess, cancellationToken).ConfigureAwait(false);
|
||||
await jobManager.RegisterOperation(
|
||||
compileProcessJob,
|
||||
DreamMaker.DeploymentProcess,
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
|
||||
await jobManager.WaitForJobCompletion(compileProcessJob, user, cancellationToken, default).ConfigureAwait(false);
|
||||
}
|
||||
@@ -617,7 +385,13 @@ namespace Tgstation.Server.Host.Components
|
||||
/// <inheritdoc />
|
||||
public async Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
await Task.WhenAll(SetAutoUpdateInterval(metadata.AutoUpdateInterval.Value), Configuration.StartAsync(cancellationToken), ByondManager.StartAsync(cancellationToken), Chat.StartAsync(cancellationToken), compileJobConsumer.StartAsync(cancellationToken)).ConfigureAwait(false);
|
||||
await Task.WhenAll(
|
||||
SetAutoUpdateInterval(metadata.AutoUpdateInterval.Value),
|
||||
Configuration.StartAsync(cancellationToken),
|
||||
ByondManager.StartAsync(cancellationToken),
|
||||
Chat.StartAsync(cancellationToken),
|
||||
dmbFactory.StartAsync(cancellationToken))
|
||||
.ConfigureAwait(false);
|
||||
|
||||
// dependent on so many things, its just safer this way
|
||||
await Watchdog.StartAsync(cancellationToken).ConfigureAwait(false);
|
||||
@@ -634,7 +408,7 @@ namespace Tgstation.Server.Host.Components
|
||||
Configuration.StopAsync(cancellationToken),
|
||||
ByondManager.StopAsync(cancellationToken),
|
||||
Chat.StopAsync(cancellationToken),
|
||||
compileJobConsumer.StopAsync(cancellationToken))
|
||||
dmbFactory.StopAsync(cancellationToken))
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@@ -668,11 +442,6 @@ namespace Tgstation.Server.Host.Components
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public CompileJob LatestCompileJob()
|
||||
{
|
||||
if (!dmbFactory.DmbAvailable)
|
||||
return null;
|
||||
return dmbFactory.LockNextDmb(0)?.CompileJob;
|
||||
}
|
||||
public CompileJob LatestCompileJob() => dmbFactory.LatestCompileJob();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -254,9 +254,37 @@ namespace Tgstation.Server.Host.Components
|
||||
commandFactory.SetWatchdog(watchdog);
|
||||
try
|
||||
{
|
||||
var dreamMaker = new DreamMaker(byond, gameIoManager, configuration, sessionControllerFactory, eventConsumer, chatManager, processExecutor, watchdog, loggerFactory.CreateLogger<DreamMaker>());
|
||||
Instance instance = null;
|
||||
var dreamMaker = new DreamMaker(
|
||||
byond,
|
||||
gameIoManager,
|
||||
configuration,
|
||||
sessionControllerFactory,
|
||||
eventConsumer,
|
||||
chatManager,
|
||||
processExecutor,
|
||||
watchdog,
|
||||
gitHubClientFactory,
|
||||
dmbFactory,
|
||||
repoManager,
|
||||
loggerFactory.CreateLogger<DreamMaker>(),
|
||||
metadata.CloneMetadata());
|
||||
|
||||
return new Instance(metadata.CloneMetadata(), repoManager, byond, dreamMaker, watchdog, chatManager, configuration, dmbFactory, databaseContextFactory, dmbFactory, jobManager, eventConsumer, gitHubClientFactory, loggerFactory.CreateLogger<Instance>());
|
||||
instance = new Instance(
|
||||
metadata.CloneMetadata(),
|
||||
repoManager,
|
||||
byond,
|
||||
dreamMaker,
|
||||
watchdog,
|
||||
chatManager,
|
||||
configuration,
|
||||
databaseContextFactory,
|
||||
dmbFactory,
|
||||
jobManager,
|
||||
eventConsumer,
|
||||
loggerFactory.CreateLogger<Instance>());
|
||||
|
||||
return instance;
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
@@ -127,7 +127,13 @@ namespace Tgstation.Server.Host.Controllers
|
||||
CancelRight = (ulong)DreamMakerRights.CancelCompile,
|
||||
Instance = Instance
|
||||
};
|
||||
await jobManager.RegisterOperation(job, instanceManager.GetInstance(Instance).CompileProcess, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
IInstance instance = instanceManager.GetInstance(Instance);
|
||||
await jobManager.RegisterOperation(
|
||||
job,
|
||||
instance.DreamMaker.DeploymentProcess,
|
||||
cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
return Accepted(job.ToApi());
|
||||
}
|
||||
|
||||
|
||||
@@ -115,9 +115,6 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Update="appsettings.Development.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="appsettings.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
|
||||
Reference in New Issue
Block a user