diff --git a/README.md b/README.md index 8b5432ccf0..333a62165f 100644 --- a/README.md +++ b/README.md @@ -185,16 +185,20 @@ If using manual configuration, before starting your container make sure the afor In order for TGS to use [OpenDream](https://github.com/OpenDreamProject/OpenDream), it requires the full .NET SDK to build whichever version your servers target. Whatever that is, it must be available using the `dotnet` command for whichever user runs TGS. -OpenDream currently requires [.NET SDK 7.0](https://dotnet.microsoft.com/en-us/download/dotnet/7.0) at the time of this writing. You must install this manually. +OpenDream currently requires [.NET SDK 8.0](https://dotnet.microsoft.com/en-us/download/dotnet/8.0) at the time of this writing. You must install this manually outside of TGS (i.e. using your package manager). -On Linux, as long as OpenDream and TGS do not use the same .NET major version, you cannot achieve this with the package manager as they will conflict. The 7.0 SDK can be added to an 8.0 runtime installation via the following steps. +
+ How to handle a different SDK version than the ASP.NET runtime of TGS. -1. Install `tgstation-server` using any of the above methods. -1. [Download the Linux SDK binaries](https://dotnet.microsoft.com/en-us/download/dotnet/7.0) for your selected architecture. -1. Extract everything EXCEPT the `dotnet` executable, `LICENSE.txt`, and `ThirdPartyNotices.txt` in the `.tar.gz` on top of the existing installation directory `/usr/share/dotnet/` -1. Run `sudo chown -R root /usr/share/dotnet` + On Linux, as long as OpenDream and TGS do not use the same .NET major version, you cannot achieve this with the package manager as they will conflict. For example, the 7.0 SDK can be added to an 8.0 runtime installation via the following steps. -You should now be able to run the `dotnet --list-sdks` command and see an entry for `7.0.XXX [/usr/share/dotnet/sdk]`. + 1. Install `tgstation-server` using any of the above methods. + 1. [Download the Linux SDK binaries](https://dotnet.microsoft.com/en-us/download/dotnet/7.0) for your selected architecture. + 1. Extract everything EXCEPT the `dotnet` executable, `LICENSE.txt`, and `ThirdPartyNotices.txt` in the `.tar.gz` on top of the existing installation directory `/usr/share/dotnet/` + 1. Run `sudo chown -R root /usr/share/dotnet` + + You should now be able to run the `dotnet --list-sdks` command and see an entry for `7.0.XXX [/usr/share/dotnet/sdk]`. +
### Configuring diff --git a/src/Tgstation.Server.Host/Components/Repository/Repository.cs b/src/Tgstation.Server.Host/Components/Repository/Repository.cs index 5518c1a44d..cd320cc892 100644 --- a/src/Tgstation.Server.Host/Components/Repository/Repository.cs +++ b/src/Tgstation.Server.Host/Components/Repository/Repository.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Globalization; +using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -84,7 +85,7 @@ namespace Tgstation.Server.Host.Components.Repository /// /// The for the . /// - readonly IIOManager ioMananger; + readonly IIOManager ioManager; /// /// The for the . @@ -106,6 +107,11 @@ namespace Tgstation.Server.Host.Components.Repository /// readonly IGitRemoteFeatures gitRemoteFeatures; + /// + /// The used for updating submodules. + /// + readonly ILibGit2RepositoryFactory submoduleFactory; + /// /// The for the . /// @@ -121,22 +127,24 @@ namespace Tgstation.Server.Host.Components.Repository /// /// The value of . /// The value of . - /// The value of . + /// The value of . /// The value of . /// The value of . /// The value of . /// The to provide the value of . + /// The value of . /// The value of . /// The value of . /// The action for the . public Repository( LibGit2Sharp.IRepository libGitRepo, ILibGit2Commands commands, - IIOManager ioMananger, + IIOManager ioManager, IEventConsumer eventConsumer, ICredentialsProvider credentialsProvider, IPostWriteHandler postWriteHandler, IGitRemoteFeaturesFactory gitRemoteFeaturesFactory, + ILibGit2RepositoryFactory submoduleFactory, ILogger logger, GeneralConfiguration generalConfiguration, Action disposeAction) @@ -144,11 +152,12 @@ namespace Tgstation.Server.Host.Components.Repository { this.libGitRepo = libGitRepo ?? throw new ArgumentNullException(nameof(libGitRepo)); this.commands = commands ?? throw new ArgumentNullException(nameof(commands)); - this.ioMananger = ioMananger ?? throw new ArgumentNullException(nameof(ioMananger)); + this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager)); this.eventConsumer = eventConsumer ?? throw new ArgumentNullException(nameof(eventConsumer)); this.credentialsProvider = credentialsProvider ?? throw new ArgumentNullException(nameof(credentialsProvider)); this.postWriteHandler = postWriteHandler ?? throw new ArgumentNullException(nameof(postWriteHandler)); ArgumentNullException.ThrowIfNull(gitRemoteFeaturesFactory); + this.submoduleFactory = submoduleFactory ?? throw new ArgumentNullException(nameof(submoduleFactory)); this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); this.generalConfiguration = generalConfiguration ?? throw new ArgumentNullException(nameof(generalConfiguration)); @@ -501,7 +510,7 @@ namespace Tgstation.Server.Host.Components.Repository { ArgumentNullException.ThrowIfNull(path); logger.LogTrace("Copying to {path}...", path); - await ioMananger.CopyDirectory( + await ioManager.CopyDirectory( new List { ".git" }, (src, dest) => { @@ -510,7 +519,7 @@ namespace Tgstation.Server.Host.Components.Repository return ValueTask.CompletedTask; }, - ioMananger.ResolvePath(), + ioManager.ResolvePath(), path, generalConfiguration.GetCopyDirectoryTaskThrottle(), cancellationToken); @@ -657,7 +666,7 @@ namespace Tgstation.Server.Host.Components.Repository EventType.RepoPreSynchronize, new List { - ioMananger.ResolvePath(), + ioManager.ResolvePath(), }, deploymentPipeline, cancellationToken); @@ -987,6 +996,17 @@ namespace Tgstation.Server.Host.Components.Repository }; } + /// + /// Gets the path of . + /// + /// The path of the target . + string GetRepositoryPath() + => ioManager.GetDirectoryName(libGitRepo + .Info + .Path + .TrimEnd(Path.DirectorySeparatorChar) + .TrimEnd(Path.AltDirectorySeparatorChar)); + /// /// Recusively update all s in the . /// @@ -996,86 +1016,106 @@ namespace Tgstation.Server.Host.Components.Repository /// If any events created should be marked as part of the deployment pipeline. /// The for the operation. /// A representing the running operation. - async ValueTask UpdateSubmodules( + ValueTask UpdateSubmodules( JobProgressReporter? progressReporter, string? username, string? password, bool deploymentPipeline, CancellationToken cancellationToken) { - var submoduleCount = libGitRepo.Submodules.Count(); - if (submoduleCount == 0) + logger.LogTrace("Updating submodules {withOrWithout} credentials...", username == null ? "without" : "with"); + + async ValueTask RecursiveUpdateSubmodules(LibGit2Sharp.IRepository parentRepository, JobProgressReporter? currentProgressReporter, string parentGitDirectory) { - logger.LogTrace("No submodules, skipping update"); - return; - } - - logger.LogTrace("Updating submodules with{orWithout} credentials...", username == null ? "out" : String.Empty); - - var factor = 1.0 / submoduleCount / 2; - foreach (var submodule in libGitRepo.Submodules) - { - var submoduleUpdateOptions = new SubmoduleUpdateOptions + var submoduleCount = libGitRepo.Submodules.Count(); + if (submoduleCount == 0) { - Init = true, - OnCheckoutNotify = (_, _) => !cancellationToken.IsCancellationRequested, - }; - - submoduleUpdateOptions.FetchOptions.Hydrate( - logger, - progressReporter?.CreateSection($"Fetch submodule {submodule.Name}", factor), - credentialsProvider.GenerateCredentialsHandler(username, password), - cancellationToken); - - if (progressReporter != null) - submoduleUpdateOptions.OnCheckoutProgress = CheckoutProgressHandler( - progressReporter.CreateSection($"Checkout submodule {submodule.Name}", factor)); - - logger.LogDebug("Updating submodule {submoduleName}...", submodule.Name); - Task RawSubModuleUpdate() => Task.Factory.StartNew( - () => libGitRepo.Submodules.Update(submodule.Name, submoduleUpdateOptions), - cancellationToken, - DefaultIOManager.BlockingTaskCreationOptions, - TaskScheduler.Current); - try - { - await RawSubModuleUpdate(); + logger.LogTrace("No submodules, skipping update"); + return; } - catch (LibGit2SharpException ex) + + var factor = 1.0 / submoduleCount / 3; + foreach (var submodule in parentRepository.Submodules) { - // workaround for https://github.com/libgit2/libgit2/issues/3820 - // kill off the modules/ folder in .git and try again - progressReporter?.ReportProgress(null); - credentialsProvider.CheckBadCredentialsException(ex); - logger.LogWarning(ex, "Initial update of submodule {submoduleName} failed. Deleting submodule directories and re-attempting...", submodule.Name); + logger.LogTrace("Entering submodule {name} ({path}) for recursive updates...", submodule.Name, submodule.Path); + var submoduleUpdateOptions = new SubmoduleUpdateOptions + { + Init = true, + OnCheckoutNotify = (_, _) => !cancellationToken.IsCancellationRequested, + }; - await Task.WhenAll( - ioMananger.DeleteDirectory($".git/modules/{submodule.Path}", cancellationToken), - ioMananger.DeleteDirectory(submodule.Path, cancellationToken)); + submoduleUpdateOptions.FetchOptions.Hydrate( + logger, + currentProgressReporter?.CreateSection($"Fetch submodule {submodule.Name}", factor), + credentialsProvider.GenerateCredentialsHandler(username, password), + cancellationToken); + + if (currentProgressReporter != null) + submoduleUpdateOptions.OnCheckoutProgress = CheckoutProgressHandler( + currentProgressReporter.CreateSection($"Checkout submodule {submodule.Name}", factor)); + + logger.LogDebug("Updating submodule {submoduleName}...", submodule.Name); + Task RawSubModuleUpdate() => Task.Factory.StartNew( + () => parentRepository.Submodules.Update(submodule.Name, submoduleUpdateOptions), + cancellationToken, + DefaultIOManager.BlockingTaskCreationOptions, + TaskScheduler.Current); - logger.LogTrace("Second update attempt for submodule {submoduleName}...", submodule.Name); try { await RawSubModuleUpdate(); } - catch (UserCancelledException) + catch (LibGit2SharpException ex) when (parentRepository == libGitRepo) { - cancellationToken.ThrowIfCancellationRequested(); - } - catch (LibGit2SharpException ex2) - { - credentialsProvider.CheckBadCredentialsException(ex2); - logger.LogTrace(ex2, "Retried update of submodule {submoduleName} failed!", submodule.Name); - throw new AggregateException(ex, ex2); - } - } + // workaround for https://github.com/libgit2/libgit2/issues/3820 + // kill off the modules/ folder in .git and try again + currentProgressReporter?.ReportProgress(null); + credentialsProvider.CheckBadCredentialsException(ex); + logger.LogWarning(ex, "Initial update of submodule {submoduleName} failed. Deleting submodule directories and re-attempting...", submodule.Name); - await eventConsumer.HandleEvent( - EventType.RepoSubmoduleUpdate, - new List { submodule.Name }, - deploymentPipeline, - cancellationToken); + await Task.WhenAll( + ioManager.DeleteDirectory($".git/modules/{submodule.Path}", cancellationToken), + ioManager.DeleteDirectory(submodule.Path, cancellationToken)); + + logger.LogTrace("Second update attempt for submodule {submoduleName}...", submodule.Name); + try + { + await RawSubModuleUpdate(); + } + catch (UserCancelledException) + { + cancellationToken.ThrowIfCancellationRequested(); + } + catch (LibGit2SharpException ex2) + { + credentialsProvider.CheckBadCredentialsException(ex2); + logger.LogTrace(ex2, "Retried update of submodule {submoduleName} failed!", submodule.Name); + throw new AggregateException(ex, ex2); + } + } + + await eventConsumer.HandleEvent( + EventType.RepoSubmoduleUpdate, + new List { submodule.Name }, + deploymentPipeline, + cancellationToken); + + var submodulePath = ioManager.ResolvePath( + ioManager.ConcatPath( + parentGitDirectory, + submodule.Path)); + + using var submoduleRepo = await submoduleFactory.CreateFromPath( + submodulePath, + cancellationToken); + await RecursiveUpdateSubmodules( + submoduleRepo, + currentProgressReporter?.CreateSection($"Entering submodule \"{submodule.Name}\"...", factor), + submodulePath); + } } + + return RecursiveUpdateSubmodules(libGitRepo, progressReporter, GetRepositoryPath()); } /// diff --git a/src/Tgstation.Server.Host/Components/Repository/RepositoryManager.cs b/src/Tgstation.Server.Host/Components/Repository/RepositoryManager.cs index 727d171e4e..94ae176951 100644 --- a/src/Tgstation.Server.Host/Components/Repository/RepositoryManager.cs +++ b/src/Tgstation.Server.Host/Components/Repository/RepositoryManager.cs @@ -231,6 +231,7 @@ namespace Tgstation.Server.Host.Components.Repository repositoryFactory, postWriteHandler, gitRemoteFeaturesFactory, + repositoryFactory, repositoryLogger, generalConfiguration, () => diff --git a/src/Tgstation.Server.Host/Components/Watchdog/WatchdogBase.cs b/src/Tgstation.Server.Host/Components/Watchdog/WatchdogBase.cs index e922322995..13f62157f1 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/WatchdogBase.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/WatchdogBase.cs @@ -1112,10 +1112,10 @@ namespace Tgstation.Server.Host.Components.Watchdog switch (++healthChecksMissed) { case 1: - Logger.LogDebug("DEFCON 4: DreamDaemon missed first health check!"); + Logger.LogDebug("DEFCON 4: Game server missed first health check!"); break; case 2: - const string message2 = "DEFCON 3: DreamDaemon has missed 2 health checks!"; + const string message2 = "DEFCON 3: Game server has missed 2 health checks!"; Logger.LogInformation(message2); Chat.QueueWatchdogMessage(message2); break; @@ -1123,7 +1123,7 @@ namespace Tgstation.Server.Host.Components.Watchdog var actionToTake = shouldShutdown ? "shutdown" : "be restarted"; - const string logTemplate1 = "DEFCON 2: DreamDaemon has missed 3 health checks! If it does not respond to the next one, the watchdog will {actionToTake}!"; + const string logTemplate1 = "DEFCON 2: Game server has missed 3 health checks! If it does not respond to the next one, the watchdog will {actionToTake}!"; Logger.LogWarning(logTemplate1, actionToTake); Chat.QueueWatchdogMessage( logTemplate1.Replace( diff --git a/tests/Tgstation.Server.Tests/TestRepository.cs b/tests/Tgstation.Server.Tests/TestRepository.cs index ad99eb2603..a9b3ca4305 100644 --- a/tests/Tgstation.Server.Tests/TestRepository.cs +++ b/tests/Tgstation.Server.Tests/TestRepository.cs @@ -39,6 +39,7 @@ namespace Tgstation.Server.Tests Mock.Of(), Mock.Of(), Mock.Of(), + Mock.Of(), Mock.Of>(), new GeneralConfiguration(), () => { });