diff --git a/src/Tgstation.Server.Host/Components/Chat/Providers/DiscordProvider.cs b/src/Tgstation.Server.Host/Components/Chat/Providers/DiscordProvider.cs
index 492eb8e332..013f22fc36 100644
--- a/src/Tgstation.Server.Host/Components/Chat/Providers/DiscordProvider.cs
+++ b/src/Tgstation.Server.Host/Components/Chat/Providers/DiscordProvider.cs
@@ -25,6 +25,7 @@ using Tgstation.Server.Api.Models;
using Tgstation.Server.Api.Models.Internal;
using Tgstation.Server.Common.Extensions;
using Tgstation.Server.Host.Components.Interop;
+using Tgstation.Server.Host.Configuration;
using Tgstation.Server.Host.Extensions;
using Tgstation.Server.Host.Jobs;
using Tgstation.Server.Host.Models;
@@ -69,6 +70,11 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
///
readonly IAssemblyInformationProvider assemblyInformationProvider;
+ ///
+ /// The for the .
+ ///
+ readonly GeneralConfiguration generalConfiguration;
+
///
/// The containing Discord services.
///
@@ -126,63 +132,6 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
/// The normalized mention .
static string NormalizeMentions(string fromDiscord) => fromDiscord.Replace("<@!", "<@", StringComparison.Ordinal);
- ///
- /// Create a of s for a discord update embed.
- ///
- /// The of the deployment.
- /// The of the deployment.
- /// The repository GitHub owner, if any.
- /// The repository GitHub name, if any.
- /// if the local deployment commit was pushed to the remote repository.
- /// A new of s to use.
- static List BuildUpdateEmbedFields(
- Models.RevisionInformation revisionInformation,
- ByondVersion byondVersion,
- string gitHubOwner,
- string gitHubRepo,
- bool localCommitPushed)
- {
- bool gitHub = gitHubOwner != null && gitHubRepo != null;
- var engineField = byondVersion.Engine.Value switch
- {
- EngineType.Byond => new EmbedField(
- "BYOND Version",
- $"{byondVersion.Version.Major}.{byondVersion.Version.Minor}{(byondVersion.Version.Build > 0 ? $".{byondVersion.Version.Build}" : String.Empty)}",
- true),
- EngineType.OpenDream => new EmbedField(
- "OpenDream Version",
- $"[{byondVersion.SourceCommittish[..7]}](https://github.com/OpenDreamProject/OpenDream/commit/{revisionInformation.CommitSha})",
- true),
- _ => throw new InvalidOperationException($"Invaild EngineType: {byondVersion.Engine.Value}"),
- };
-
- var fields = new List
- {
- engineField,
- new EmbedField(
- "Local Commit",
- localCommitPushed && gitHub
- ? $"[{revisionInformation.CommitSha[..7]}](https://github.com/{gitHubOwner}/{gitHubRepo}/commit/{revisionInformation.CommitSha})"
- : revisionInformation.CommitSha[..7],
- true),
- new EmbedField(
- "Branch Commit",
- gitHub
- ? $"[{revisionInformation.OriginCommitSha[..7]}](https://github.com/{gitHubOwner}/{gitHubRepo}/commit/{revisionInformation.OriginCommitSha})"
- : revisionInformation.OriginCommitSha[..7],
- true),
- };
-
- fields.AddRange((revisionInformation.ActiveTestMerges ?? Enumerable.Empty())
- .Select(x => x.TestMerge)
- .Select(x => new EmbedField(
- $"#{x.Number}",
- $"[{x.TitleAtMerge}]({x.Url}) by _[@{x.Author}](https://github.com/{x.Author})_{Environment.NewLine}Commit: [{x.TargetCommitSha[..7]}](https://github.com/{gitHubOwner}/{gitHubRepo}/commit/{x.TargetCommitSha}){(String.IsNullOrWhiteSpace(x.Comment) ? String.Empty : $"{Environment.NewLine}_**{x.Comment}**_")}",
- false)));
-
- return fields;
- }
-
///
/// Initializes a new instance of the class.
///
@@ -191,15 +140,18 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
/// The for the .
/// The value of .
/// The for the .
+ /// The value of .
public DiscordProvider(
IJobManager jobManager,
IAsyncDelayer asyncDelayer,
ILogger logger,
IAssemblyInformationProvider assemblyInformationProvider,
- ChatBot chatBot)
+ ChatBot chatBot,
+ GeneralConfiguration generalConfiguration)
: base(jobManager, asyncDelayer, logger, chatBot)
{
this.assemblyInformationProvider = assemblyInformationProvider ?? throw new ArgumentNullException(nameof(assemblyInformationProvider));
+ this.generalConfiguration = generalConfiguration ?? throw new ArgumentNullException(nameof(generalConfiguration));
mappedChannels = new List();
connectDisconnectLock = new object();
@@ -903,12 +855,69 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
return allAccessibleChannels;
}
+ ///
+ /// Create a of s for a discord update embed.
+ ///
+ /// The of the deployment.
+ /// The of the deployment.
+ /// The repository GitHub owner, if any.
+ /// The repository GitHub name, if any.
+ /// if the local deployment commit was pushed to the remote repository.
+ /// A new of s to use.
+ List BuildUpdateEmbedFields(
+ Models.RevisionInformation revisionInformation,
+ ByondVersion byondVersion,
+ string gitHubOwner,
+ string gitHubRepo,
+ bool localCommitPushed)
+ {
+ bool gitHub = gitHubOwner != null && gitHubRepo != null;
+ var engineField = byondVersion.Engine.Value switch
+ {
+ EngineType.Byond => new EmbedField(
+ "BYOND Version",
+ $"{byondVersion.Version.Major}.{byondVersion.Version.Minor}{(byondVersion.Version.Build > 0 ? $".{byondVersion.Version.Build}" : String.Empty)}",
+ true),
+ EngineType.OpenDream => new EmbedField(
+ "OpenDream Version",
+ $"[{byondVersion.SourceCommittish[..7]}]({generalConfiguration.OpenDreamGitUrl}/commit/{revisionInformation.CommitSha})",
+ true),
+ _ => throw new InvalidOperationException($"Invaild EngineType: {byondVersion.Engine.Value}"),
+ };
+
+ var fields = new List
+ {
+ engineField,
+ new EmbedField(
+ "Local Commit",
+ localCommitPushed && gitHub
+ ? $"[{revisionInformation.CommitSha[..7]}](https://github.com/{gitHubOwner}/{gitHubRepo}/commit/{revisionInformation.CommitSha})"
+ : revisionInformation.CommitSha[..7],
+ true),
+ new EmbedField(
+ "Branch Commit",
+ gitHub
+ ? $"[{revisionInformation.OriginCommitSha[..7]}](https://github.com/{gitHubOwner}/{gitHubRepo}/commit/{revisionInformation.OriginCommitSha})"
+ : revisionInformation.OriginCommitSha[..7],
+ true),
+ };
+
+ fields.AddRange((revisionInformation.ActiveTestMerges ?? Enumerable.Empty())
+ .Select(x => x.TestMerge)
+ .Select(x => new EmbedField(
+ $"#{x.Number}",
+ $"[{x.TitleAtMerge}]({x.Url}) by _[@{x.Author}](https://github.com/{x.Author})_{Environment.NewLine}Commit: [{x.TargetCommitSha[..7]}](https://github.com/{gitHubOwner}/{gitHubRepo}/commit/{x.TargetCommitSha}){(String.IsNullOrWhiteSpace(x.Comment) ? String.Empty : $"{Environment.NewLine}_**{x.Comment}**_")}",
+ false)));
+
+ return fields;
+ }
+
///
/// Convert a to an parameters.
///
/// The to convert.
/// The parameter for sending a single .
- #pragma warning disable CA1502
+#pragma warning disable CA1502
Optional> ConvertEmbed(ChatEmbed embed)
{
if (embed == null)
diff --git a/src/Tgstation.Server.Host/Components/Chat/Providers/ProviderFactory.cs b/src/Tgstation.Server.Host/Components/Chat/Providers/ProviderFactory.cs
index eeed8c1583..ed3608b92d 100644
--- a/src/Tgstation.Server.Host/Components/Chat/Providers/ProviderFactory.cs
+++ b/src/Tgstation.Server.Host/Components/Chat/Providers/ProviderFactory.cs
@@ -2,8 +2,10 @@
using System.Globalization;
using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
using Tgstation.Server.Api.Models;
+using Tgstation.Server.Host.Configuration;
using Tgstation.Server.Host.Jobs;
using Tgstation.Server.Host.System;
using Tgstation.Server.Host.Utils;
@@ -33,6 +35,11 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
///
readonly ILoggerFactory loggerFactory;
+ ///
+ /// The for the .
+ ///
+ readonly GeneralConfiguration generalConfiguration;
+
///
/// Initializes a new instance of the class.
///
@@ -40,16 +47,19 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
/// The value of .
/// The value of .
/// The value of .
+ /// The containing the value of .
public ProviderFactory(
IJobManager jobManager,
IAssemblyInformationProvider assemblyInformationProvider,
IAsyncDelayer asyncDelayer,
- ILoggerFactory loggerFactory)
+ ILoggerFactory loggerFactory,
+ IOptions generalConfigurationOptions)
{
this.jobManager = jobManager ?? throw new ArgumentNullException(nameof(jobManager));
this.loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory));
this.asyncDelayer = asyncDelayer ?? throw new ArgumentNullException(nameof(asyncDelayer));
this.assemblyInformationProvider = assemblyInformationProvider ?? throw new ArgumentNullException(nameof(assemblyInformationProvider));
+ generalConfiguration = generalConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(generalConfigurationOptions));
}
///
@@ -69,7 +79,8 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
asyncDelayer,
loggerFactory.CreateLogger(),
assemblyInformationProvider,
- settings),
+ settings,
+ generalConfiguration),
_ => throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Invalid ChatProvider: {0}", settings.Provider)),
};
}
diff --git a/src/Tgstation.Server.Host/Components/Deployment/DreamMaker.cs b/src/Tgstation.Server.Host/Components/Deployment/DreamMaker.cs
index 6a5f524225..8dd925f812 100644
--- a/src/Tgstation.Server.Host/Components/Deployment/DreamMaker.cs
+++ b/src/Tgstation.Server.Host/Components/Deployment/DreamMaker.cs
@@ -864,39 +864,29 @@ namespace Tgstation.Server.Host.Components.Deployment
async ValueTask RunDreamMaker(IEngineExecutableLock engineLock, Models.CompileJob job, CancellationToken cancellationToken)
{
var arguments = engineLock.FormatCompilerArguments($"{job.DmeName}.{DmeExtension}");
- bool result;
- if (arguments == null)
- {
- logger.LogTrace("Engine lock says compilation isn't necessary.");
- job.Output = $"{engineLock.Version.Engine} does not require compilation.";
- result = true;
- }
- else
- {
- await using var dm = processExecutor.LaunchProcess(
- engineLock.CompilerExePath,
- ioManager.ResolvePath(
- job.DirectoryName.ToString()),
- arguments,
- readStandardHandles: true,
- noShellExecute: true);
- if (sessionConfiguration.LowPriorityDeploymentProcesses)
- dm.AdjustPriority(false);
+ await using var dm = processExecutor.LaunchProcess(
+ engineLock.CompilerExePath,
+ ioManager.ResolvePath(
+ job.DirectoryName.ToString()),
+ arguments,
+ readStandardHandles: true,
+ noShellExecute: true);
- int exitCode;
- using (cancellationToken.Register(() => dm.Terminate()))
- exitCode = (await dm.Lifetime).Value;
- cancellationToken.ThrowIfCancellationRequested();
+ if (sessionConfiguration.LowPriorityDeploymentProcesses)
+ dm.AdjustPriority(false);
- logger.LogDebug("DreamMaker exit code: {exitCode}", exitCode);
- job.Output = $"{await dm.GetCombinedOutput(cancellationToken)}{Environment.NewLine}{Environment.NewLine}Exit Code: {exitCode}";
- logger.LogDebug("DreamMaker output: {newLine}{output}", Environment.NewLine, job.Output);
- result = exitCode == 0;
- }
+ int exitCode;
+ using (cancellationToken.Register(() => dm.Terminate()))
+ exitCode = (await dm.Lifetime).Value;
+ cancellationToken.ThrowIfCancellationRequested();
+
+ logger.LogDebug("DreamMaker exit code: {exitCode}", exitCode);
+ job.Output = $"{await dm.GetCombinedOutput(cancellationToken)}{Environment.NewLine}{Environment.NewLine}Exit Code: {exitCode}";
+ logger.LogDebug("DreamMaker output: {newLine}{output}", Environment.NewLine, job.Output);
currentDreamMakerOutput = job.Output;
- return result;
+ return exitCode == 0;
}
///
diff --git a/src/Tgstation.Server.Host/Components/Engine/DelegatingEngineInstaller.cs b/src/Tgstation.Server.Host/Components/Engine/DelegatingEngineInstaller.cs
new file mode 100644
index 0000000000..19fde5df73
--- /dev/null
+++ b/src/Tgstation.Server.Host/Components/Engine/DelegatingEngineInstaller.cs
@@ -0,0 +1,70 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+
+using Tgstation.Server.Api.Models;
+using Tgstation.Server.Api.Models.Internal;
+using Tgstation.Server.Common.Extensions;
+using Tgstation.Server.Host.Jobs;
+
+namespace Tgstation.Server.Host.Components.Engine
+{
+ ///
+ /// Implementation of that forwards calls to different based on their appropriate .
+ ///
+ sealed class DelegatingEngineInstaller : IEngineInstaller
+ {
+ ///
+ /// The mapping s to their appropriate .
+ ///
+ readonly IReadOnlyDictionary delegatedInstallers;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The value of .
+ public DelegatingEngineInstaller(IReadOnlyDictionary delegatedInstallers)
+ {
+ this.delegatedInstallers = delegatedInstallers ?? throw new ArgumentNullException(nameof(delegatedInstallers));
+ }
+
+ ///
+ public Task CleanCache(CancellationToken cancellationToken)
+ => Task.WhenAll(delegatedInstallers.Values.Select(installer => installer.CleanCache(cancellationToken)));
+
+ ///
+ public IEngineInstallation CreateInstallation(ByondVersion version, Task installationTask)
+ => DelegateCall(version, installer => installer.CreateInstallation(version, installationTask));
+
+ ///
+ public ValueTask DownloadVersion(ByondVersion version, JobProgressReporter jobProgressReporter, CancellationToken cancellationToken)
+ => DelegateCall(version, installer => installer.DownloadVersion(version, jobProgressReporter, cancellationToken));
+
+ ///
+ public ValueTask Install(ByondVersion version, string path, CancellationToken cancellationToken)
+ => DelegateCall(version, installer => installer.Install(version, path, cancellationToken));
+
+ ///
+ public ValueTask TrustDmbPath(string fullDmbPath, CancellationToken cancellationToken)
+ => ValueTaskExtensions.WhenAll(delegatedInstallers.Values.Select(installer => installer.TrustDmbPath(fullDmbPath, cancellationToken)));
+
+ ///
+ public ValueTask UpgradeInstallation(ByondVersion version, string path, CancellationToken cancellationToken)
+ => DelegateCall(version, installer => installer.UpgradeInstallation(version, path, cancellationToken));
+
+ ///
+ /// Delegate a given to its appropriate .
+ ///
+ /// The return of the call.
+ /// The used to perform delegate selection.
+ /// The that will be called with the correct based on .
+ /// The value of the delegated call.
+ TReturn DelegateCall(ByondVersion version, Func call)
+ {
+ ArgumentNullException.ThrowIfNull(version);
+ return call(delegatedInstallers[version.Engine.Value]);
+ }
+ }
+}
diff --git a/src/Tgstation.Server.Host/Components/Engine/IEngineInstallation.cs b/src/Tgstation.Server.Host/Components/Engine/IEngineInstallation.cs
index 75ebbab4c8..238b4fea11 100644
--- a/src/Tgstation.Server.Host/Components/Engine/IEngineInstallation.cs
+++ b/src/Tgstation.Server.Host/Components/Engine/IEngineInstallation.cs
@@ -59,7 +59,7 @@ namespace Tgstation.Server.Host.Components.Engine
/// Return the command line arguments for compiling a given if compilation is necessary.
///
/// The full path to the .dme to compile.
- /// An arguments if compilation is required, otherwise.
+ /// The formatted arguments .
string FormatCompilerArguments(string dmePath);
}
}
diff --git a/src/Tgstation.Server.Host/Components/Engine/OpenDreamInstallation.cs b/src/Tgstation.Server.Host/Components/Engine/OpenDreamInstallation.cs
index cdff39ed41..5a3d04d6d2 100644
--- a/src/Tgstation.Server.Host/Components/Engine/OpenDreamInstallation.cs
+++ b/src/Tgstation.Server.Host/Components/Engine/OpenDreamInstallation.cs
@@ -54,6 +54,10 @@ namespace Tgstation.Server.Host.Components.Engine
///
public string FormatServerArguments(IDmbProvider dmbProvider, IReadOnlyDictionary parameters, DreamDaemonLaunchParameters launchParameters, string logFilePath)
{
+ ArgumentNullException.ThrowIfNull(dmbProvider);
+ ArgumentNullException.ThrowIfNull(parameters);
+ ArgumentNullException.ThrowIfNull(launchParameters);
+
throw new NotImplementedException();
}
@@ -61,7 +65,7 @@ namespace Tgstation.Server.Host.Components.Engine
public string FormatCompilerArguments(string dmePath)
{
ArgumentNullException.ThrowIfNull(dmePath);
- return null;
+ throw new NotImplementedException();
}
}
}
diff --git a/src/Tgstation.Server.Host/Components/Engine/OpenDreamInstaller.cs b/src/Tgstation.Server.Host/Components/Engine/OpenDreamInstaller.cs
index 976131743c..9eb60f0dfe 100644
--- a/src/Tgstation.Server.Host/Components/Engine/OpenDreamInstaller.cs
+++ b/src/Tgstation.Server.Host/Components/Engine/OpenDreamInstaller.cs
@@ -8,6 +8,7 @@ using Microsoft.Extensions.Options;
using Tgstation.Server.Api.Models;
using Tgstation.Server.Api.Models.Internal;
+using Tgstation.Server.Common.Extensions;
using Tgstation.Server.Host.Common;
using Tgstation.Server.Host.Components.Repository;
using Tgstation.Server.Host.Configuration;
@@ -120,14 +121,20 @@ namespace Tgstation.Server.Host.Components.Engine
var progressSection2 = jobProgressReporter.CreateSection("Checking out OpenDream version", 0.5f);
+ var committish = version.SourceCommittish;
+ if (committish == null)
+ committish = $"{generalConfiguration.OpenDreamGitTagPrefix}{version.Version.Semver()}";
+
await repo.CheckoutObject(
- version.SourceCommittish,
+ committish,
null,
null,
true,
progressSection2,
cancellationToken);
+ version.SourceCommittish = repo.Head;
+
return new RepositoryEngineInstallationData(IOManager, repo, InstallationRepositorySubDirectory);
}
catch
diff --git a/src/Tgstation.Server.Host/Components/Events/NoopEventConsumer.cs b/src/Tgstation.Server.Host/Components/Events/NoopEventConsumer.cs
new file mode 100644
index 0000000000..a8b07e4d7e
--- /dev/null
+++ b/src/Tgstation.Server.Host/Components/Events/NoopEventConsumer.cs
@@ -0,0 +1,16 @@
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Tgstation.Server.Host.Components.Events
+{
+ ///
+ /// No-op implementation of .
+ ///
+ sealed class NoopEventConsumer : IEventConsumer
+ {
+ ///
+ public ValueTask HandleEvent(EventType eventType, IEnumerable parameters, bool deploymentPipeline, CancellationToken cancellationToken)
+ => ValueTask.CompletedTask;
+ }
+}
diff --git a/src/Tgstation.Server.Host/Components/InstanceFactory.cs b/src/Tgstation.Server.Host/Components/InstanceFactory.cs
index 0199f1de08..e45052ecd1 100644
--- a/src/Tgstation.Server.Host/Components/InstanceFactory.cs
+++ b/src/Tgstation.Server.Host/Components/InstanceFactory.cs
@@ -111,14 +111,9 @@ namespace Tgstation.Server.Host.Components
readonly IPlatformIdentifier platformIdentifier;
///
- /// The for the .
+ /// The for the .
///
- readonly ILibGit2RepositoryFactory repositoryFactory;
-
- ///
- /// The for the .
- ///
- readonly ILibGit2Commands repositoryCommands;
+ readonly IRepositoryManagerFactory repositoryManagerFactory;
///
/// The for the .
@@ -130,11 +125,6 @@ namespace Tgstation.Server.Host.Components
///
readonly IFileTransferTicketProvider fileTransferService;
- ///
- /// The for the .
- ///
- readonly IGitRemoteFeaturesFactory gitRemoteFeaturesFactory;
-
///
/// The for the .
///
@@ -182,11 +172,9 @@ namespace Tgstation.Server.Host.Components
/// The value of .
/// The value of .
/// The value of .
- /// The value of .
- /// The value of .
+ /// The value of .
/// The value of .
/// The value of .
- /// The value of .
/// The value of .
/// The value of .
/// The containing the value of .
@@ -208,11 +196,9 @@ namespace Tgstation.Server.Host.Components
IJobManager jobManager,
INetworkPromptReaper networkPromptReaper,
IPlatformIdentifier platformIdentifier,
- ILibGit2RepositoryFactory repositoryFactory,
- ILibGit2Commands repositoryCommands,
+ IRepositoryManagerFactory repositoryManagerFactory,
IServerPortProvider serverPortProvider,
IFileTransferTicketProvider fileTransferService,
- IGitRemoteFeaturesFactory gitRemoteFeaturesFactory,
IRemoteDeploymentManagerFactory remoteDeploymentManagerFactory,
IAsyncDelayer asyncDelayer,
IOptions generalConfigurationOptions,
@@ -234,11 +220,9 @@ namespace Tgstation.Server.Host.Components
this.jobManager = jobManager ?? throw new ArgumentNullException(nameof(jobManager));
this.networkPromptReaper = networkPromptReaper ?? throw new ArgumentNullException(nameof(networkPromptReaper));
this.platformIdentifier = platformIdentifier ?? throw new ArgumentNullException(nameof(platformIdentifier));
- this.repositoryFactory = repositoryFactory ?? throw new ArgumentNullException(nameof(repositoryFactory));
- this.repositoryCommands = repositoryCommands ?? throw new ArgumentNullException(nameof(repositoryCommands));
+ this.repositoryManagerFactory = repositoryManagerFactory ?? throw new ArgumentNullException(nameof(repositoryManagerFactory));
this.serverPortProvider = serverPortProvider ?? throw new ArgumentNullException(nameof(serverPortProvider));
this.fileTransferService = fileTransferService ?? throw new ArgumentNullException(nameof(fileTransferService));
- this.gitRemoteFeaturesFactory = gitRemoteFeaturesFactory ?? throw new ArgumentNullException(nameof(gitRemoteFeaturesFactory));
this.remoteDeploymentManagerFactory = remoteDeploymentManagerFactory ?? throw new ArgumentNullException(nameof(remoteDeploymentManagerFactory));
this.asyncDelayer = asyncDelayer ?? throw new ArgumentNullException(nameof(asyncDelayer));
generalConfiguration = generalConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(generalConfigurationOptions));
@@ -284,19 +268,10 @@ namespace Tgstation.Server.Host.Components
generalConfiguration,
sessionConfiguration);
var eventConsumer = new EventConsumer(configuration);
- var repoManager = new RepositoryManager(
- repositoryFactory,
- repositoryCommands,
- repoIoManager,
- eventConsumer,
- postWriteHandler,
- gitRemoteFeaturesFactory,
- loggerFactory.CreateLogger(),
- loggerFactory.CreateLogger(),
- generalConfiguration);
+ var repoManager = repositoryManagerFactory.CreateRepositoryManager(repoIoManager, eventConsumer);
try
{
- var byond = new EngineManager(byondIOManager, engineInstaller, eventConsumer, loggerFactory.CreateLogger());
+ var engineManager = new EngineManager(byondIOManager, engineInstaller, eventConsumer, loggerFactory.CreateLogger());
var dmbFactory = new DmbFactory(
databaseContextFactory,
@@ -307,7 +282,7 @@ namespace Tgstation.Server.Host.Components
metadata);
try
{
- var commandFactory = new CommandFactory(assemblyInformationProvider, byond, repoManager, databaseContextFactory, dmbFactory, metadata);
+ var commandFactory = new CommandFactory(assemblyInformationProvider, engineManager, repoManager, databaseContextFactory, dmbFactory, metadata);
var chatManager = chatFactory.CreateChatManager(commandFactory, metadata.ChatSettings);
try
@@ -321,7 +296,7 @@ namespace Tgstation.Server.Host.Components
var sessionControllerFactory = new SessionControllerFactory(
processExecutor,
- byond,
+ engineManager,
topicClientFactory,
cryptographySuite,
assemblyInformationProvider,
@@ -357,7 +332,7 @@ namespace Tgstation.Server.Host.Components
Instance instance = null;
var dreamMaker = new DreamMaker(
- byond,
+ engineManager,
gameIoManager,
configuration,
sessionControllerFactory,
@@ -375,7 +350,7 @@ namespace Tgstation.Server.Host.Components
instance = new Instance(
metadata,
repoManager,
- byond,
+ engineManager,
dreamMaker,
watchdog,
chatManager,
@@ -417,18 +392,12 @@ namespace Tgstation.Server.Host.Components
///
public Task StartAsync(CancellationToken cancellationToken)
- {
- CheckSystemCompatibility();
- return engineInstaller.CleanCache(cancellationToken);
- }
+ => Task.WhenAll(
+ repositoryManagerFactory.StartAsync(cancellationToken),
+ engineInstaller.CleanCache(cancellationToken));
///
- public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
-
- ///
- /// Test that the is functional.
- ///
- void CheckSystemCompatibility() => repositoryFactory.CreateInMemory();
+ public Task StopAsync(CancellationToken cancellationToken) => repositoryManagerFactory.StopAsync(cancellationToken);
///
/// Create the for a given set of instance .
diff --git a/src/Tgstation.Server.Host/Components/Repository/IRepositoryManagerFactory.cs b/src/Tgstation.Server.Host/Components/Repository/IRepositoryManagerFactory.cs
new file mode 100644
index 0000000000..0e8c54c70e
--- /dev/null
+++ b/src/Tgstation.Server.Host/Components/Repository/IRepositoryManagerFactory.cs
@@ -0,0 +1,19 @@
+using Tgstation.Server.Host.Components.Events;
+using Tgstation.Server.Host.IO;
+
+namespace Tgstation.Server.Host.Components.Repository
+{
+ ///
+ /// Factory for creating s.
+ ///
+ interface IRepositoryManagerFactory : IComponentService
+ {
+ ///
+ /// Create a .
+ ///
+ /// The to use.
+ /// The to use.
+ /// A new .
+ IRepositoryManager CreateRepositoryManager(IIOManager ioManager, IEventConsumer eventConsumer);
+ }
+}
diff --git a/src/Tgstation.Server.Host/Components/Repository/RepostoryManagerFactory.cs b/src/Tgstation.Server.Host/Components/Repository/RepostoryManagerFactory.cs
new file mode 100644
index 0000000000..cba66ff69a
--- /dev/null
+++ b/src/Tgstation.Server.Host/Components/Repository/RepostoryManagerFactory.cs
@@ -0,0 +1,100 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+
+using Tgstation.Server.Host.Components.Events;
+using Tgstation.Server.Host.Configuration;
+using Tgstation.Server.Host.IO;
+
+namespace Tgstation.Server.Host.Components.Repository
+{
+ ///
+ sealed class RepostoryManagerFactory : IRepositoryManagerFactory
+ {
+ ///
+ /// The for the .
+ ///
+ readonly ILibGit2RepositoryFactory repositoryFactory;
+
+ ///
+ /// The for the .
+ ///
+ readonly ILibGit2Commands repositoryCommands;
+
+ ///
+ /// The for the .
+ ///
+ readonly IPostWriteHandler postWriteHandler;
+
+ ///
+ /// The for the .
+ ///
+ readonly IGitRemoteFeaturesFactory gitRemoteFeaturesFactory;
+
+ ///
+ /// The for the .
+ ///
+ readonly ILoggerFactory loggerFactory;
+
+ ///
+ /// The for the .
+ ///
+ readonly GeneralConfiguration generalConfiguration;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The value of .
+ /// The value of .
+ /// The value of .
+ /// The value of .
+ /// The value of .
+ /// The containing the value of .
+ public RepostoryManagerFactory(
+ ILibGit2RepositoryFactory repositoryFactory,
+ ILibGit2Commands repositoryCommands,
+ IPostWriteHandler postWriteHandler,
+ IGitRemoteFeaturesFactory gitRemoteFeaturesFactory,
+ ILoggerFactory loggerFactory,
+ IOptions generalConfigurationOptions)
+ {
+ this.repositoryFactory = repositoryFactory ?? throw new ArgumentNullException(nameof(repositoryFactory));
+ this.repositoryCommands = repositoryCommands ?? throw new ArgumentNullException(nameof(repositoryCommands));
+ this.postWriteHandler = postWriteHandler ?? throw new ArgumentNullException(nameof(postWriteHandler));
+ this.gitRemoteFeaturesFactory = gitRemoteFeaturesFactory ?? throw new ArgumentNullException(nameof(gitRemoteFeaturesFactory));
+ this.loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory));
+ generalConfiguration = generalConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(generalConfigurationOptions));
+ }
+
+ ///
+ public IRepositoryManager CreateRepositoryManager(IIOManager ioManager, IEventConsumer eventConsumer)
+ => new RepositoryManager(
+ repositoryFactory,
+ repositoryCommands,
+ ioManager,
+ eventConsumer,
+ postWriteHandler,
+ gitRemoteFeaturesFactory,
+ loggerFactory.CreateLogger(),
+ loggerFactory.CreateLogger(),
+ generalConfiguration);
+
+ ///
+ public Task StartAsync(CancellationToken cancellationToken)
+ {
+ CheckSystemCompatibility();
+ return Task.CompletedTask;
+ }
+
+ ///
+ public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
+
+ ///
+ /// Test that the is functional.
+ ///
+ void CheckSystemCompatibility() => repositoryFactory.CreateInMemory();
+ }
+}
diff --git a/src/Tgstation.Server.Host/Configuration/GeneralConfiguration.cs b/src/Tgstation.Server.Host/Configuration/GeneralConfiguration.cs
index 668e158241..eecca9cd9f 100644
--- a/src/Tgstation.Server.Host/Configuration/GeneralConfiguration.cs
+++ b/src/Tgstation.Server.Host/Configuration/GeneralConfiguration.cs
@@ -66,6 +66,11 @@ namespace Tgstation.Server.Host.Configuration
///
const string DefaultOpenDreamGitUrl = "https://github.com/OpenDreamProject/OpenDream";
+ ///
+ /// The default value for .
+ ///
+ const string DefaultOpenDreamGitTagPrefix = "v";
+
///
/// The current .
///
@@ -132,6 +137,11 @@ namespace Tgstation.Server.Host.Configuration
///
public Uri OpenDreamGitUrl { get; set; } = new Uri(DefaultOpenDreamGitUrl);
+ ///
+ /// The prefix to the OpenDream semver as tags appear in the git repository.
+ ///
+ public string OpenDreamGitTagPrefix { get; set; } = DefaultOpenDreamGitTagPrefix;
+
///
/// Initializes a new instance of the class.
///
diff --git a/src/Tgstation.Server.Host/Controllers/ByondController.cs b/src/Tgstation.Server.Host/Controllers/ByondController.cs
index 34ab468431..b57df144fc 100644
--- a/src/Tgstation.Server.Host/Controllers/ByondController.cs
+++ b/src/Tgstation.Server.Host/Controllers/ByondController.cs
@@ -9,6 +9,7 @@ using Microsoft.Extensions.Logging;
using Tgstation.Server.Api;
using Tgstation.Server.Api.Models;
+using Tgstation.Server.Api.Models.Internal;
using Tgstation.Server.Api.Models.Request;
using Tgstation.Server.Api.Models.Response;
using Tgstation.Server.Api.Rights;
@@ -72,7 +73,7 @@ namespace Tgstation.Server.Host.Controllers
}
///
- /// Gets the active .
+ /// Gets the active .
///
/// A resulting in the for the operation.
/// Retrieved version information successfully.
@@ -91,7 +92,7 @@ namespace Tgstation.Server.Host.Controllers
: Conflict(new ErrorMessageResponse(ErrorCode.ResourceNotPresent))));
///
- /// Lists installed s.
+ /// Lists installed s.
///
/// The current page.
/// The page size.
@@ -139,15 +140,11 @@ namespace Tgstation.Server.Host.Controllers
#pragma warning restore CA1506
#pragma warning restore CA1502
{
- throw new NotImplementedException("Fix OD/BYOND model validation");
- ArgumentNullException.ThrowIfNull(model);
+ var earlyOut = ValidateByondVersion(model);
+ if (earlyOut != null)
+ return earlyOut;
var uploadingZip = model.UploadCustomZip == true;
- var isByondEngine = model.Engine.Value == EngineType.Byond;
-
- if ((isByondEngine && (model.Version.Revision != -1 || (uploadingZip && model.Version.Build > 0) || model.SourceCommittish != null))
- || (!isByondEngine && (model.Version != null || String.IsNullOrWhiteSpace(model.SourceCommittish))))
- return BadRequest(new ErrorMessageResponse(ErrorCode.ModelValidationFailure));
var userByondRights = AuthenticationContext.InstancePermissionSet.ByondRights.Value;
if ((!userByondRights.HasFlag(ByondRights.InstallOfficialOrChangeActiveByondVersion) && !uploadingZip)
@@ -201,7 +198,7 @@ namespace Tgstation.Server.Host.Controllers
Instance.Id);
// run the install through the job manager
- var job = new Job
+ var job = new Models.Job
{
Description = $"Install {(!uploadingZip ? String.Empty : "custom ")}{model.Engine.Value} version {model.Version}",
StartedBy = AuthenticationContext.User,
@@ -271,7 +268,7 @@ namespace Tgstation.Server.Host.Controllers
/// A resulting in the for the operation.
/// Created to delete target version successfully.
/// Attempted to delete the active BYOND .
- /// The specified was not installed.
+ /// The specified was not installed.
[HttpDelete]
[TgsAuthorize(ByondRights.DeleteInstall)]
[ProducesResponseType(typeof(JobResponse), 202)]
@@ -279,13 +276,9 @@ namespace Tgstation.Server.Host.Controllers
[ProducesResponseType(typeof(ErrorMessageResponse), 410)]
public async ValueTask Delete([FromBody] ByondVersionDeleteRequest model, CancellationToken cancellationToken)
{
- ArgumentNullException.ThrowIfNull(model);
-
- if (model.Version.Revision != -1)
- return BadRequest(new ErrorMessageResponse(ErrorCode.ModelValidationFailure));
-
- if (model.Engine == EngineType.Byond)
- model.Version = NormalizeByondVersion(model.Version);
+ var earlyOut = ValidateByondVersion(model);
+ if (earlyOut != null)
+ return earlyOut;
var notInstalledResponse = await WithComponentInstance(
instance =>
@@ -310,7 +303,7 @@ namespace Tgstation.Server.Host.Controllers
var isByondVersion = model.Engine.Value == EngineType.Byond;
// run the install through the job manager
- var job = new Job
+ var job = new Models.Job
{
Description = $"Delete installed {model.Engine.Value} version {model.Version}",
StartedBy = AuthenticationContext.User,
@@ -333,5 +326,30 @@ namespace Tgstation.Server.Host.Controllers
var apiResponse = job.ToApi();
return Accepted(apiResponse);
}
+
+ ///
+ /// Validate and normalize a given .
+ ///
+ /// The to validate and normalize.
+ /// The to return, if any.
+ BadRequestObjectResult ValidateByondVersion(ByondVersion version)
+ {
+ ArgumentNullException.ThrowIfNull(version);
+
+ var isByond = version.Engine.Value == EngineType.Byond;
+ if ((isByond
+ && (version.Version == null
+ || version.Version.Revision != -1
+ || version.SourceCommittish != null))
+ || (version.Engine.Value == EngineType.OpenDream &&
+ ((version.SourceCommittish == null && version.Version == null)
+ || (version.Version != null && (version.Version.Revision != -1 || version.Version.Build == -1 || version.SourceCommittish == null)))))
+ return BadRequest(new ErrorMessageResponse(ErrorCode.ModelValidationFailure));
+
+ if (isByond)
+ version.Version = NormalizeByondVersion(version.Version);
+
+ return null;
+ }
}
}
diff --git a/src/Tgstation.Server.Host/Core/Application.cs b/src/Tgstation.Server.Host/Core/Application.cs
index eaeeb9a954..b0fbbf3169 100644
--- a/src/Tgstation.Server.Host/Core/Application.cs
+++ b/src/Tgstation.Server.Host/Core/Application.cs
@@ -27,11 +27,13 @@ using Serilog.Formatting.Display;
using Serilog.Sinks.Elasticsearch;
using Tgstation.Server.Api;
+using Tgstation.Server.Api.Models;
using Tgstation.Server.Common.Http;
using Tgstation.Server.Host.Components;
using Tgstation.Server.Host.Components.Chat;
using Tgstation.Server.Host.Components.Deployment.Remote;
using Tgstation.Server.Host.Components.Engine;
+using Tgstation.Server.Host.Components.Events;
using Tgstation.Server.Host.Components.Interop;
using Tgstation.Server.Host.Components.Interop.Bridge;
using Tgstation.Server.Host.Components.Repository;
@@ -332,7 +334,7 @@ namespace Tgstation.Server.Host.Core
AddWatchdog(services, postSetupServices);
services.AddSingleton();
services.AddSingleton();
- services.AddSingleton();
+ services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
@@ -345,7 +347,7 @@ namespace Tgstation.Server.Host.Core
AddWatchdog(services, postSetupServices);
services.AddSingleton();
services.AddSingleton();
- services.AddSingleton();
+ services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
@@ -357,6 +359,29 @@ namespace Tgstation.Server.Host.Core
services.AddHostedService();
}
+ // only global repo manager should be for the OD repo
+ var openDreamRepositoryDirectory = ioManager.ConcatPath(
+ Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
+ assemblyInformationProvider.VersionPrefix,
+ "OpenDreamRepository");
+ services.AddSingleton(
+ services => services
+ .GetRequiredService()
+ .CreateRepositoryManager(
+ new ResolvingIOManager(
+ services.GetRequiredService(),
+ openDreamRepositoryDirectory),
+ new NoopEventConsumer()));
+
+ services.AddSingleton();
+ services.AddSingleton(
+ serviceProvider => new Dictionary
+ {
+ { EngineType.Byond, serviceProvider.GetRequiredService() },
+ { EngineType.OpenDream, serviceProvider.GetRequiredService() },
+ });
+ services.AddSingleton();
+
if (postSetupServices.InternalConfiguration.UsingSystemD)
services.AddHostedService();
@@ -378,6 +403,7 @@ namespace Tgstation.Server.Host.Core
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
+ services.AddSingleton();
services.AddSingleton();
services.AddChatProviderFactory();
services.AddSingleton();
diff --git a/src/Tgstation.Server.Host/appsettings.yml b/src/Tgstation.Server.Host/appsettings.yml
index bebeb79c04..3046339495 100644
--- a/src/Tgstation.Server.Host/appsettings.yml
+++ b/src/Tgstation.Server.Host/appsettings.yml
@@ -16,6 +16,8 @@ General:
HostApiDocumentation: false # Make HTTP API documentation available at /swagger/v1/swagger.json
SkipAddingByondFirewallException: false # Windows Only: Prevent running netsh.exe to add a firewall exception for installed DreamDaemon binaries
DeploymentDirectoryCopyTasksPerCore: 100 # Maximum number of concurrent file copy operations PER available CPU core
+ OpenDreamGitUrl: https://github.com/OpenDreamProject/OpenDream # The repository to retrieve OpenDream from
+ OpenDreamGitTagPrefix: v # The prefix to the OpenDream semver as tags appear in the git repository
Session:
HighPriorityLiveDreamDaemon: false # If DreamDaemon instances should run as higher priority processes
LowPriorityDeploymentProcesses: true # If TGS Deployments should run as lower priority processes
diff --git a/tests/Tgstation.Server.Host.Tests/Components/Chat/Providers/TestDiscordProvider.cs b/tests/Tgstation.Server.Host.Tests/Components/Chat/Providers/TestDiscordProvider.cs
index 210b31447e..b21b19f0ae 100644
--- a/tests/Tgstation.Server.Host.Tests/Components/Chat/Providers/TestDiscordProvider.cs
+++ b/tests/Tgstation.Server.Host.Tests/Components/Chat/Providers/TestDiscordProvider.cs
@@ -8,6 +8,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using Tgstation.Server.Api.Models;
+using Tgstation.Server.Host.Configuration;
using Tgstation.Server.Host.Jobs;
using Tgstation.Server.Host.Models;
using Tgstation.Server.Host.System;
@@ -52,15 +53,17 @@ namespace Tgstation.Server.Host.Components.Chat.Providers.Tests
ReconnectionInterval = 1,
};
- Assert.ThrowsException(() => new DiscordProvider(null, null, null, null, null));
- Assert.ThrowsException(() => new DiscordProvider(mockJobManager, null, null, null, null));
+ Assert.ThrowsException(() => new DiscordProvider(null, null, null, null, null, null));
+ Assert.ThrowsException(() => new DiscordProvider(mockJobManager, null, null, null, null, null));
var mockDel = Mock.Of();
- Assert.ThrowsException(() => new DiscordProvider(mockJobManager, mockDel, null, null, null));
+ Assert.ThrowsException(() => new DiscordProvider(mockJobManager, mockDel, null, null, null, null));
var mockLogger = Mock.Of>();
- Assert.ThrowsException(() => new DiscordProvider(mockJobManager, mockDel, mockLogger, null, null));
+ Assert.ThrowsException(() => new DiscordProvider(mockJobManager, mockDel, mockLogger, null, null, null));
var mockAss = Mock.Of();
- Assert.ThrowsException(() => new DiscordProvider(mockJobManager, mockDel, mockLogger, mockAss, null));
- await new DiscordProvider(mockJobManager, mockDel, mockLogger, mockAss, bot).DisposeAsync();
+ Assert.ThrowsException(() => new DiscordProvider(mockJobManager, mockDel, mockLogger, mockAss, null, null));
+ Assert.ThrowsException(() => new DiscordProvider(mockJobManager, mockDel, mockLogger, mockAss, bot, null));
+ var mockGen = new GeneralConfiguration();
+ await new DiscordProvider(mockJobManager, mockDel, mockLogger, mockAss, bot, mockGen).DisposeAsync();
}
static ValueTask InvokeConnect(IProvider provider, CancellationToken cancellationToken = default) => (ValueTask)provider.GetType().GetMethod("Connect", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(provider, new object[] { cancellationToken });
@@ -73,7 +76,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers.Tests
{
ReconnectionInterval = 1,
ConnectionString = "asdf"
- });
+ }, new GeneralConfiguration());
await Assert.ThrowsExceptionAsync(async () => await InvokeConnect(provider));
Assert.IsFalse(provider.Connected);
}
@@ -88,7 +91,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers.Tests
Assert.Fail("TGS_TEST_DISCORD_TOKEN is not a valid Discord connection string!");
var mockLogger = new Mock>();
- await using var provider = new DiscordProvider(mockJobManager, Mock.Of(), mockLogger.Object, Mock.Of(), testToken1);
+ await using var provider = new DiscordProvider(mockJobManager, Mock.Of(), mockLogger.Object, Mock.Of(), testToken1, new GeneralConfiguration());
Assert.IsFalse(provider.Connected);
await InvokeConnect(provider);
Assert.IsTrue(provider.Connected);