Nullify RepositoryUpdateService

This commit is contained in:
Jordan Dominion
2023-12-23 11:54:16 -05:00
parent 695cc4645f
commit 874f16da4b
@@ -15,8 +15,6 @@ using Tgstation.Server.Host.Database;
using Tgstation.Server.Host.Jobs;
using Tgstation.Server.Host.Models;
#nullable disable
namespace Tgstation.Server.Host.Components.Repository
{
/// <summary>
@@ -87,16 +85,18 @@ namespace Tgstation.Server.Host.Components.Repository
IDatabaseContext databaseContext,
ILogger logger,
Models.Instance instance,
string lastOriginCommitSha,
Action<Models.RevisionInformation> revInfoSink,
string? lastOriginCommitSha,
Action<Models.RevisionInformation>? revInfoSink,
CancellationToken cancellationToken)
{
var repoSha = repository.Head;
IQueryable<Models.RevisionInformation> ApplyQuery(IQueryable<Models.RevisionInformation> query) => query
.Where(x => x.CommitSha == repoSha && x.Instance.Id == instance.Id)
.Where(x => x.CommitSha == repoSha && x.InstanceId == instance.Id)
.Include(x => x.CompileJobs)
.Include(x => x.ActiveTestMerges).ThenInclude(x => x.TestMerge).ThenInclude(x => x.MergedBy);
.Include(x => x.ActiveTestMerges!)
.ThenInclude(x => x.TestMerge)
.ThenInclude(x => x.MergedBy);
var revisionInfo = await ApplyQuery(databaseContext.RevisionInformations).FirstOrDefaultAsync(cancellationToken);
@@ -105,7 +105,7 @@ namespace Tgstation.Server.Host.Components.Repository
revisionInfo = databaseContext
.RevisionInformations
.Local
.Where(x => x.CommitSha == repoSha && x.Instance.Id == instance.Id)
.Where(x => x.CommitSha == repoSha && x.InstanceId == instance.Id)
.FirstOrDefault();
var needsDbUpdate = revisionInfo == default;
@@ -125,7 +125,7 @@ namespace Tgstation.Server.Host.Components.Repository
databaseContext.RevisionInformations.Add(revisionInfo);
}
revisionInfo.OriginCommitSha ??= lastOriginCommitSha;
revisionInfo!.OriginCommitSha ??= lastOriginCommitSha;
if (revisionInfo.OriginCommitSha == null)
{
revisionInfo.OriginCommitSha = repoSha;
@@ -147,13 +147,15 @@ namespace Tgstation.Server.Host.Components.Repository
/// <returns>A <see cref="ValueTask"/> representing the running operation.</returns>
#pragma warning disable CA1502, CA1506 // TODO: Decomplexify
public async ValueTask RepositoryUpdateJob(
IInstanceCore instance,
IInstanceCore? instance,
IDatabaseContextFactory databaseContextFactory,
Job job,
JobProgressReporter progressReporter,
CancellationToken cancellationToken)
#pragma warning restore CA1502, CA1506
{
ArgumentNullException.ThrowIfNull(instance);
_ = job; // shuts up an IDE warning
var repoManager = instance.RepositoryManager;
@@ -162,23 +164,23 @@ namespace Tgstation.Server.Host.Components.Repository
var startReference = repo.Reference;
var startSha = repo.Head;
string postUpdateSha = null;
string? postUpdateSha = null;
var newTestMerges = model.NewTestMerges != null && model.NewTestMerges.Count > 0;
if (newTestMerges && repo.RemoteGitProvider == RemoteGitProvider.Unknown)
throw new JobException(ErrorCode.RepoUnsupportedTestMergeRemote);
var committerName = currentModel.ShowTestMergeCommitters.Value
var committerName = (currentModel.ShowTestMergeCommitters!.Value
? initiatingUser.Name
: currentModel.CommitterName;
: currentModel.CommitterName)!;
var hardResettingToOriginReference = model.UpdateFromOrigin == true && model.Reference != null;
var numSteps = (model.NewTestMerges?.Count ?? 0) + (model.UpdateFromOrigin == true ? 1 : 0) + (!modelHasShaOrReference ? 2 : (hardResettingToOriginReference ? 3 : 1));
var progressFactor = 1.0 / numSteps;
JobProgressReporter NextProgressReporter(string stage)
JobProgressReporter NextProgressReporter(string? stage)
{
return progressReporter.CreateSection(stage, progressFactor);
}
@@ -186,19 +188,19 @@ namespace Tgstation.Server.Host.Components.Repository
progressReporter.ReportProgress(0);
// get a base line for where we are
Models.RevisionInformation lastRevisionInfo = null;
Models.RevisionInformation? lastRevisionInfo = null;
var attachedInstance = new Models.Instance
{
Id = instanceId,
};
ValueTask CallLoadRevInfo(Models.TestMerge testMergeToAdd = null, string lastOriginCommitSha = null) => databaseContextFactory
ValueTask CallLoadRevInfo(Models.TestMerge? testMergeToAdd = null, string? lastOriginCommitSha = null) => databaseContextFactory
.UseContext(
async databaseContext =>
{
databaseContext.Instances.Attach(attachedInstance);
var previousRevInfo = lastRevisionInfo;
var previousRevInfo = lastRevisionInfo!;
var needsUpdate = await LoadRevisionInformation(
repo,
databaseContext,
@@ -225,10 +227,11 @@ namespace Tgstation.Server.Host.Components.Repository
testMergeToAdd.MergedBy = mergedBy;
testMergeToAdd.MergedAt = DateTimeOffset.UtcNow;
foreach (var activeTestMerge in previousRevInfo.ActiveTestMerges)
lastRevisionInfo.ActiveTestMerges.Add(activeTestMerge);
var activeTestMerges = lastRevisionInfo!.ActiveTestMerges!;
foreach (var activeTestMerge in previousRevInfo.ActiveTestMerges!)
activeTestMerges.Add(activeTestMerge);
lastRevisionInfo.ActiveTestMerges.Add(new RevInfoTestMerge(testMergeToAdd, lastRevisionInfo));
activeTestMerges.Add(new RevInfoTestMerge(testMergeToAdd, lastRevisionInfo));
lastRevisionInfo.PrimaryTestMerge = testMergeToAdd;
needsUpdate = true;
@@ -241,7 +244,7 @@ namespace Tgstation.Server.Host.Components.Repository
await CallLoadRevInfo();
// apply new rev info, tracking applied test merges
ValueTask UpdateRevInfo(Models.TestMerge testMergeToAdd = null) => CallLoadRevInfo(testMergeToAdd, lastRevisionInfo.OriginCommitSha);
ValueTask UpdateRevInfo(Models.TestMerge? testMergeToAdd = null) => CallLoadRevInfo(testMergeToAdd, lastRevisionInfo!.OriginCommitSha);
try
{
@@ -262,12 +265,12 @@ namespace Tgstation.Server.Host.Components.Repository
var fastForward = await repo.MergeOrigin(
NextProgressReporter("Merge Origin"),
committerName,
currentModel.CommitterEmail,
currentModel.CommitterEmail!,
false,
cancellationToken);
if (!fastForward.HasValue)
throw new JobException(ErrorCode.RepoMergeConflict);
lastRevisionInfo.OriginCommitSha = await repo.GetOriginSha(cancellationToken);
lastRevisionInfo!.OriginCommitSha = await repo.GetOriginSha(cancellationToken);
await UpdateRevInfo();
if (fastForward.Value)
{
@@ -275,8 +278,8 @@ namespace Tgstation.Server.Host.Components.Repository
NextProgressReporter("Sychronize"),
currentModel.AccessUser,
currentModel.AccessToken,
currentModel.CommitterName,
currentModel.CommitterEmail,
currentModel.CommitterName!,
currentModel.CommitterEmail!,
true,
false,
cancellationToken);
@@ -287,7 +290,7 @@ namespace Tgstation.Server.Host.Components.Repository
}
}
var updateSubmodules = currentModel.UpdateSubmodules.Value;
var updateSubmodules = currentModel.UpdateSubmodules!.Value;
// checkout/hard reset
if (modelHasShaOrReference)
@@ -301,7 +304,7 @@ namespace Tgstation.Server.Host.Components.Repository
if (validCheckoutSha || validCheckoutReference)
{
var committish = model.CheckoutSha ?? model.Reference;
var committish = model.CheckoutSha ?? model.Reference!;
var isSha = await repo.IsSha(committish, cancellationToken);
if ((isSha && model.Reference != null) || (!isSha && model.CheckoutSha != null))
@@ -334,8 +337,8 @@ namespace Tgstation.Server.Host.Components.Repository
NextProgressReporter("Synchronize"),
currentModel.AccessUser,
currentModel.AccessToken,
currentModel.CommitterName,
currentModel.CommitterEmail,
currentModel.CommitterName!,
currentModel.CommitterEmail!,
true,
false,
cancellationToken);
@@ -343,7 +346,7 @@ namespace Tgstation.Server.Host.Components.Repository
// repo head is on origin so force this
// will update the db if necessary
lastRevisionInfo.OriginCommitSha = repo.Head;
lastRevisionInfo!.OriginCommitSha = repo.Head;
}
}
@@ -354,19 +357,20 @@ namespace Tgstation.Server.Host.Components.Repository
throw new JobException(ErrorCode.RepoTestMergeInvalidRemote);
// bit of sanitization
foreach (var newTestMergeWithoutTargetCommitSha in model.NewTestMerges.Where(x => String.IsNullOrWhiteSpace(x.TargetCommitSha)))
var newTestMergeModels = model.NewTestMerges!;
foreach (var newTestMergeWithoutTargetCommitSha in newTestMergeModels.Where(x => String.IsNullOrWhiteSpace(x.TargetCommitSha)))
newTestMergeWithoutTargetCommitSha.TargetCommitSha = null;
var repoOwner = repo.RemoteRepositoryOwner;
var repoName = repo.RemoteRepositoryName;
// optimization: if we've already merged these exact same commits in this fashion before, just find the rev info for it and check it out
Models.RevisionInformation revInfoWereLookingFor = null;
Models.RevisionInformation? revInfoWereLookingFor = null;
bool needToApplyRemainingPrs = true;
if (lastRevisionInfo.OriginCommitSha == lastRevisionInfo.CommitSha)
if (lastRevisionInfo!.OriginCommitSha == lastRevisionInfo.CommitSha)
{
bool cantSearch = false;
foreach (var newTestMerge in model.NewTestMerges)
foreach (var newTestMerge in newTestMergeModels)
{
if (newTestMerge.TargetCommitSha != null)
#pragma warning disable CA1308 // Normalize strings to uppercase
@@ -390,56 +394,58 @@ namespace Tgstation.Server.Host.Components.Repository
if (!cantSearch)
{
List<Models.RevisionInformation> dbPull = null;
List<Models.RevisionInformation>? dbPull = null;
await databaseContextFactory.UseContext(
async databaseContext =>
dbPull = await databaseContext.RevisionInformations
.AsQueryable()
.Where(x => x.Instance.Id == instanceId
&& x.OriginCommitSha == lastRevisionInfo.OriginCommitSha
&& x.ActiveTestMerges.Count <= model.NewTestMerges.Count
&& x.ActiveTestMerges.Count > 0)
.Include(x => x.ActiveTestMerges)
.ThenInclude(x => x.TestMerge)
.Where(x => x.InstanceId == instanceId
&& x.OriginCommitSha == lastRevisionInfo.OriginCommitSha
&& x.ActiveTestMerges!.Count <= newTestMergeModels.Count
&& x.ActiveTestMerges!.Count > 0)
.Include(x => x.ActiveTestMerges!)
.ThenInclude(x => x.TestMerge)
.ToListAsync(cancellationToken));
// split here cause this bit has to be done locally
revInfoWereLookingFor = dbPull
.Where(x => x.ActiveTestMerges.Count == model.NewTestMerges.Count
&& x.ActiveTestMerges.Select(y => y.TestMerge)
.All(y => model.NewTestMerges.Any(z =>
y.Number == z.Number
&& y.TargetCommitSha.StartsWith(z.TargetCommitSha, StringComparison.Ordinal)
&& (y.Comment?.Trim().ToUpperInvariant() == z.Comment?.Trim().ToUpperInvariant() || z.Comment == null))))
revInfoWereLookingFor = dbPull!
.Where(x => x.ActiveTestMerges!.Count == newTestMergeModels.Count
&& x.ActiveTestMerges
.Select(y => y.TestMerge)
.All(y => newTestMergeModels
.Any(z =>
y.Number == z.Number
&& y.TargetCommitSha!.StartsWith(z.TargetCommitSha!, StringComparison.Ordinal)
&& (y.Comment?.Trim().ToUpperInvariant() == z.Comment?.Trim().ToUpperInvariant() || z.Comment == null))))
.FirstOrDefault();
if (revInfoWereLookingFor == default && model.NewTestMerges.Count > 1)
if (revInfoWereLookingFor == default && newTestMergeModels.Count > 1)
{
// okay try to add at least SOME prs we've seen before
var listedNewTestMerges = model.NewTestMerges.ToList();
var listedNewTestMerges = newTestMergeModels.ToList();
var appliedTestMergeIds = new List<long>();
Models.RevisionInformation lastGoodRevInfo = null;
Models.RevisionInformation? lastGoodRevInfo = null;
do
{
foreach (var newTestMergeParameters in listedNewTestMerges)
{
revInfoWereLookingFor = dbPull
revInfoWereLookingFor = dbPull!
.Where(testRevInfo =>
{
if (testRevInfo.PrimaryTestMerge == null)
return false;
var testMergeMatch = model.NewTestMerges.Any(testTestMerge =>
var testMergeMatch = newTestMergeModels.Any(testTestMerge =>
{
var numberMatch = testRevInfo.PrimaryTestMerge.Number == testTestMerge.Number;
if (!numberMatch)
return false;
var shaMatch = testRevInfo.PrimaryTestMerge.TargetCommitSha.StartsWith(
testTestMerge.TargetCommitSha,
var shaMatch = testRevInfo.PrimaryTestMerge.TargetCommitSha!.StartsWith(
testTestMerge.TargetCommitSha!,
StringComparison.Ordinal);
if (!shaMatch)
return false;
@@ -452,7 +458,7 @@ namespace Tgstation.Server.Host.Components.Repository
return false;
var previousTestMergesMatch = testRevInfo
.ActiveTestMerges
.ActiveTestMerges!
.Select(previousRevInfoTestMerge => previousRevInfoTestMerge.TestMerge)
.All(previousTestMerge => appliedTestMergeIds.Contains(previousTestMerge.Id));
@@ -463,7 +469,7 @@ namespace Tgstation.Server.Host.Components.Repository
if (revInfoWereLookingFor != null)
{
lastGoodRevInfo = revInfoWereLookingFor;
appliedTestMergeIds.Add(revInfoWereLookingFor.PrimaryTestMerge.Id);
appliedTestMergeIds.Add(revInfoWereLookingFor.PrimaryTestMerge!.Id);
listedNewTestMerges.Remove(newTestMergeParameters);
break;
}
@@ -484,16 +490,17 @@ namespace Tgstation.Server.Host.Components.Repository
if (revInfoWereLookingFor != null)
{
// goteem
logger.LogDebug("Reusing existing SHA {sha}...", revInfoWereLookingFor.CommitSha);
await repo.ResetToSha(revInfoWereLookingFor.CommitSha, NextProgressReporter($"Reset to {revInfoWereLookingFor.CommitSha[..7]}"), cancellationToken);
var commitSha = revInfoWereLookingFor.CommitSha!;
logger.LogDebug("Reusing existing SHA {sha}...", commitSha);
await repo.ResetToSha(commitSha, NextProgressReporter($"Reset to {commitSha[..7]}"), cancellationToken);
lastRevisionInfo = revInfoWereLookingFor;
}
if (needToApplyRemainingPrs)
{
foreach (var newTestMerge in model.NewTestMerges)
foreach (var newTestMerge in newTestMergeModels)
{
if (lastRevisionInfo.ActiveTestMerges.Any(x => x.TestMerge.Number == newTestMerge.Number))
if (lastRevisionInfo.ActiveTestMerges!.Any(x => x.TestMerge.Number == newTestMerge.Number))
throw new JobException(ErrorCode.RepoDuplicateTestMerge);
var fullTestMergeTask = repo.GetTestMerge(newTestMerge, currentModel, cancellationToken);
@@ -501,7 +508,7 @@ namespace Tgstation.Server.Host.Components.Repository
var mergeResult = await repo.AddTestMerge(
newTestMerge,
committerName,
currentModel.CommitterEmail,
currentModel.CommitterEmail!,
currentModel.AccessUser,
currentModel.AccessToken,
updateSubmodules,
@@ -512,7 +519,7 @@ namespace Tgstation.Server.Host.Components.Repository
throw new JobException(
ErrorCode.RepoTestMergeConflict,
new JobException(
$"Test Merge #{newTestMerge.Number} at {newTestMerge.TargetCommitSha[..7]} conflicted! Conflicting files:{Environment.NewLine}{String.Join(Environment.NewLine, mergeResult.ConflictingFiles.Select(file => $"\t- /{file}"))}"));
$"Test Merge #{newTestMerge.Number} at {newTestMerge.TargetCommitSha![..7]} conflicted! Conflicting files:{Environment.NewLine}{String.Join(Environment.NewLine, mergeResult.ConflictingFiles!.Select(file => $"\t- /{file}"))}"));
Models.TestMerge fullTestMerge;
try
@@ -543,14 +550,14 @@ namespace Tgstation.Server.Host.Components.Repository
}
var currentHead = repo.Head;
if (currentModel.PushTestMergeCommits.Value && (startSha != currentHead || (postUpdateSha != null && postUpdateSha != currentHead)))
if (currentModel.PushTestMergeCommits!.Value && (startSha != currentHead || (postUpdateSha != null && postUpdateSha != currentHead)))
{
await repo.Synchronize(
NextProgressReporter("Synchronize"),
currentModel.AccessUser,
currentModel.AccessToken,
currentModel.CommitterName,
currentModel.CommitterEmail,
currentModel.CommitterName!,
currentModel.CommitterEmail!,
false,
false,
cancellationToken);