From b9565185ac4355e7b38d521a616885242bd9076a Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Wed, 1 Sep 2021 10:28:04 -0400 Subject: [PATCH 1/7] Support for describing a job's current stage --- build/Version.props | 8 +- .../Models/Response/JobResponse.cs | 6 + .../Components/Deployment/DreamMaker.cs | 46 +++++-- .../Components/Deployment/IDreamMaker.cs | 10 +- .../Components/Instance.cs | 8 +- .../Components/Repository/IRepository.cs | 40 ++++-- .../Repository/IRepositoryManager.cs | 8 +- .../Components/Repository/Repository.cs | 120 +++++++++++------- .../Repository/RepositoryManager.cs | 4 +- .../Controllers/JobController.cs | 4 +- .../Controllers/RepositoryController.cs | 14 +- src/Tgstation.Server.Host/Jobs/IJobManager.cs | 9 +- .../Jobs/JobEntrypoint.cs | 7 +- src/Tgstation.Server.Host/Jobs/JobHandler.cs | 5 + src/Tgstation.Server.Host/Jobs/JobManager.cs | 24 ++-- .../Jobs/JobProgressReporter.cs | 13 ++ .../Tgstation.Server.Tests/IntegrationTest.cs | 2 +- 17 files changed, 213 insertions(+), 115 deletions(-) create mode 100644 src/Tgstation.Server.Host/Jobs/JobProgressReporter.cs diff --git a/build/Version.props b/build/Version.props index 0d6b6a923c..e527b4fbfb 100644 --- a/build/Version.props +++ b/build/Version.props @@ -3,11 +3,11 @@ - 4.14.1 + 4.15.0 4.0.0 - 9.2.0 - 9.2.0 - 10.2.0 + 9.3.0 + 9.3.0 + 10.3.0 6.0.4 5.3.0 1.1.1 diff --git a/src/Tgstation.Server.Api/Models/Response/JobResponse.cs b/src/Tgstation.Server.Api/Models/Response/JobResponse.cs index 6c04aa919c..eb194af473 100644 --- a/src/Tgstation.Server.Api/Models/Response/JobResponse.cs +++ b/src/Tgstation.Server.Api/Models/Response/JobResponse.cs @@ -21,5 +21,11 @@ /// [ResponseOptions] public int? Progress { get; set; } + + /// + /// Optional description of the job's current . + /// + [ResponseOptions] + public string? Stage { get; set; } } } diff --git a/src/Tgstation.Server.Host/Components/Deployment/DreamMaker.cs b/src/Tgstation.Server.Host/Components/Deployment/DreamMaker.cs index b9e01435b7..408d27f61e 100644 --- a/src/Tgstation.Server.Host/Components/Deployment/DreamMaker.cs +++ b/src/Tgstation.Server.Host/Components/Deployment/DreamMaker.cs @@ -113,6 +113,11 @@ namespace Tgstation.Server.Host.Components.Deployment /// string currentDreamMakerOutput; + /// + /// Current stage to report on the job. + /// + string currentStage; + /// /// If a compile job is running. /// @@ -178,7 +183,7 @@ namespace Tgstation.Server.Host.Components.Deployment public async Task DeploymentProcess( Models.Job job, IDatabaseContextFactory databaseContextFactory, - Action progressReporter, + JobProgressReporter progressReporter, CancellationToken cancellationToken) { if (job == null) @@ -450,7 +455,7 @@ namespace Tgstation.Server.Host.Components.Deployment /// The API validation timeout. /// The . /// The . - /// The progress reporting . + /// The to report progress of the operation. /// The optional estimated of the compilation. /// Whether or not the 's current commit exists on the remote repository. /// The for the operation. @@ -461,7 +466,7 @@ namespace Tgstation.Server.Host.Components.Deployment uint apiValidateTimeout, IRepository repository, IRemoteDeploymentManager remoteDeploymentManager, - Action progressReporter, + JobProgressReporter progressReporter, TimeSpan? estimatedDuration, bool localCommitExistsOnRemote, CancellationToken cancellationToken) @@ -469,7 +474,9 @@ namespace Tgstation.Server.Host.Components.Deployment logger.LogTrace("Begin Compile"); using var progressCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); - var progressTask = estimatedDuration.HasValue ? ProgressTask(progressReporter, estimatedDuration.Value, progressCts.Token) : Task.CompletedTask; + + currentStage = "Reserving BYOND version"; + var progressTask = ProgressTask(progressReporter, estimatedDuration, progressCts.Token); try { using var byondLock = await byond.UseExecutables(null, cancellationToken).ConfigureAwait(false); @@ -490,6 +497,7 @@ namespace Tgstation.Server.Host.Components.Deployment RepositoryOrigin = repository.Origin.ToString(), }; + currentStage = "Creating remote deployment notification"; await remoteDeploymentManager.StartDeployment( repository, job, @@ -525,6 +533,7 @@ namespace Tgstation.Server.Host.Components.Deployment catch (OperationCanceledException) { // DCT: Cancellation token is for job, delaying here is fine + currentStage = "Running CompileCancelled event"; await eventConsumer.HandleEvent(EventType.CompileCancelled, Enumerable.Empty(), default).ConfigureAwait(false); throw; } @@ -561,7 +570,8 @@ namespace Tgstation.Server.Host.Components.Deployment try { // copy the repository - logger.LogTrace("Copying repository to game directory..."); + logger.LogTrace("Copying repository to game directory"); + currentStage = "Copying repository"; var resolvedOutputDirectory = ioManager.ResolvePath(outputDirectory); var repoOrigin = repository.Origin; using (repository) @@ -570,6 +580,7 @@ namespace Tgstation.Server.Host.Components.Deployment // repository closed now // run precompile scripts + currentStage = "Running PreCompile event"; await eventConsumer.HandleEvent( EventType.CompileStart, new List @@ -582,9 +593,10 @@ namespace Tgstation.Server.Host.Components.Deployment .ConfigureAwait(false); // determine the dme + currentStage = "Determining .dme"; if (job.DmeName == null) { - logger.LogTrace("Searching for available .dmes..."); + logger.LogTrace("Searching for available .dmes"); var foundPaths = await ioManager.GetFilesWithExtension(resolvedOutputDirectory, DmeExtension, true, cancellationToken).ConfigureAwait(false); var foundPath = foundPaths.FirstOrDefault(); if (foundPath == default) @@ -603,9 +615,11 @@ namespace Tgstation.Server.Host.Components.Deployment logger.LogDebug("Selected {0}.dme for compilation!", job.DmeName); + currentStage = "Modifying .dme"; await ModifyDme(job, cancellationToken).ConfigureAwait(false); // run precompile scripts + currentStage = "Running PreDreamMaker event"; await eventConsumer.HandleEvent( EventType.PreDreamMaker, new List @@ -618,6 +632,7 @@ namespace Tgstation.Server.Host.Components.Deployment .ConfigureAwait(false); // run compiler + currentStage = "Running DreamMaker"; var exitCode = await RunDreamMaker(byondLock.DreamMakerPath, job, cancellationToken).ConfigureAwait(false); // verify api @@ -628,6 +643,7 @@ namespace Tgstation.Server.Host.Components.Deployment ErrorCode.DreamMakerExitCode, new JobException($"Exit code: {exitCode}{Environment.NewLine}{Environment.NewLine}{job.Output}")); + currentStage = "Validating DMAPI"; await VerifyApi( apiValidateTimeout, dreamMakerSettings.ApiValidationSecurityLevel.Value, @@ -641,6 +657,7 @@ namespace Tgstation.Server.Host.Components.Deployment catch (JobException) { // DD never validated or compile failed + currentStage = "Running CompileFailure event"; await eventConsumer.HandleEvent( EventType.CompileFailure, new List @@ -654,6 +671,7 @@ namespace Tgstation.Server.Host.Components.Deployment throw; } + currentStage = "Running CompileComplete event"; await eventConsumer.HandleEvent( EventType.CompileComplete, new List @@ -665,6 +683,7 @@ namespace Tgstation.Server.Host.Components.Deployment .ConfigureAwait(false); logger.LogTrace("Applying static game file symlinks..."); + currentStage = "Symlinking GameStaticFiles"; // symlink in the static data await configuration.SymlinkStaticFilesTo(resolvedOutputDirectory, cancellationToken).ConfigureAwait(false); @@ -673,6 +692,7 @@ namespace Tgstation.Server.Host.Components.Deployment } catch (Exception ex) { + currentStage = "Cleaning output directory"; await CleanupFailedCompile(job, remoteDeploymentManager, ex).ConfigureAwait(false); throw; } @@ -681,22 +701,22 @@ namespace Tgstation.Server.Host.Components.Deployment /// /// Gradually triggers a given over a given . /// - /// The to report progress. - /// A representing the duration to give progress over. + /// The to report progress of the operation. + /// A representing the duration to give progress over if any. /// The for the operation. /// A representing the running operation. - async Task ProgressTask(Action progressReporter, TimeSpan estimatedDuration, CancellationToken cancellationToken) + async Task ProgressTask(JobProgressReporter progressReporter, TimeSpan? estimatedDuration, CancellationToken cancellationToken) { - progressReporter(0); - var sleepInterval = estimatedDuration / 100; + progressReporter(currentStage, estimatedDuration.HasValue ? 0 : null); + var sleepInterval = estimatedDuration.HasValue ? estimatedDuration.Value / 100 : TimeSpan.FromMilliseconds(250); logger.LogDebug("Compile is expected to take: {0}", estimatedDuration); try { - for (var iteration = 0; iteration < 99; ++iteration) + for (var iteration = 0; iteration < (estimatedDuration.HasValue ? 99 : Int32.MaxValue); ++iteration) { await Task.Delay(sleepInterval, cancellationToken).ConfigureAwait(false); - progressReporter(iteration + 1); + progressReporter(currentStage, estimatedDuration.HasValue ? iteration + 1 : null); } } catch (OperationCanceledException) diff --git a/src/Tgstation.Server.Host/Components/Deployment/IDreamMaker.cs b/src/Tgstation.Server.Host/Components/Deployment/IDreamMaker.cs index 091a5ab750..0f5421691d 100644 --- a/src/Tgstation.Server.Host/Components/Deployment/IDreamMaker.cs +++ b/src/Tgstation.Server.Host/Components/Deployment/IDreamMaker.cs @@ -1,8 +1,8 @@ -using System; -using System.Threading; +using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Host.Database; +using Tgstation.Server.Host.Jobs; using Tgstation.Server.Host.Models; namespace Tgstation.Server.Host.Components.Deployment @@ -13,17 +13,17 @@ namespace Tgstation.Server.Host.Components.Deployment public interface IDreamMaker { /// - /// Create and a compile job and insert it into the database. Meant to be called by a . + /// Create and a compile job and insert it into the database. Meant to be called by a . /// /// The running . /// The for the operation. - /// The to report compilation progress. + /// The to report compilation progress. /// The for the operation. /// A representing the running operation. Task DeploymentProcess( Job job, IDatabaseContextFactory databaseContextFactory, - Action progressReporter, + JobProgressReporter progressReporter, CancellationToken cancellationToken); } } diff --git a/src/Tgstation.Server.Host/Components/Instance.cs b/src/Tgstation.Server.Host/Components/Instance.cs index b591c8f401..3e1f8eab93 100644 --- a/src/Tgstation.Server.Host/Components/Instance.cs +++ b/src/Tgstation.Server.Host/Components/Instance.cs @@ -256,7 +256,7 @@ namespace Tgstation.Server.Host.Components IInstanceCore core, IDatabaseContextFactory databaseContextFactory, Job job, - Action progressReporter, + JobProgressReporter progressReporter, CancellationToken cancellationToken) => databaseContextFactory.UseContext( async databaseContext => @@ -277,11 +277,11 @@ namespace Tgstation.Server.Host.Components const int NumSteps = 3; var doneSteps = 0; - Action NextProgressReporter() + JobProgressReporter NextProgressReporter() { var tmpDoneSteps = doneSteps; ++doneSteps; - return progress => progressReporter((progress + (100 * tmpDoneSteps)) / NumSteps); + return (status, progress) => progressReporter(status, (progress + (100 * tmpDoneSteps)) / NumSteps); } using var repo = await RepositoryManager.LoadRepository(cancellationToken).ConfigureAwait(false); @@ -457,7 +457,7 @@ namespace Tgstation.Server.Host.Components throw; } - progressReporter(5 * ProgressStep); + progressReporter(null, 5 * ProgressStep); }); #pragma warning restore CA1502 // Cyclomatic complexity diff --git a/src/Tgstation.Server.Host/Components/Repository/IRepository.cs b/src/Tgstation.Server.Host/Components/Repository/IRepository.cs index 0ac50e0cf9..73574da2af 100644 --- a/src/Tgstation.Server.Host/Components/Repository/IRepository.cs +++ b/src/Tgstation.Server.Host/Components/Repository/IRepository.cs @@ -3,6 +3,7 @@ using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api.Models; +using Tgstation.Server.Host.Jobs; namespace Tgstation.Server.Host.Components.Repository { @@ -46,7 +47,7 @@ namespace Tgstation.Server.Host.Components.Repository /// The username used for fetching from submodule repositories. /// The password used for fetching from submodule repositories. /// If a submodule update should be attempted after the merge. - /// to report 0-100 progress of the operation. + /// The to report progress of the operation. /// The for the operation. /// A representing the running operation. Task CheckoutObject( @@ -54,7 +55,7 @@ namespace Tgstation.Server.Host.Components.Repository string username, string password, bool updateSubmodules, - Action progressReporter, + JobProgressReporter progressReporter, CancellationToken cancellationToken); /// @@ -66,7 +67,7 @@ namespace Tgstation.Server.Host.Components.Repository /// The username used to fetch from the origin and submodule repositories. /// The password used to fetch from the origin and submodule repositories. /// If a submodule update should be attempted after the merge. - /// to report 0-100 progress of the operation. + /// The to report progress of the operation. /// The for the operation. /// A resulting in a representing the merge result that is after a fast forward or up to date, on a non-fast-forward, on a conflict. Task AddTestMerge( @@ -76,7 +77,7 @@ namespace Tgstation.Server.Host.Components.Repository string username, string password, bool updateSubmodules, - Action progressReporter, + JobProgressReporter progressReporter, CancellationToken cancellationToken); /// @@ -84,10 +85,14 @@ namespace Tgstation.Server.Host.Components.Repository /// /// The username to fetch from the origin repository. /// The password to fetch from the origin repository. - /// to report 0-100 progress of the operation. + /// The to report progress of the operation. /// The for the operation. /// A representing the running operation. - Task FetchOrigin(string username, string password, Action progressReporter, CancellationToken cancellationToken); + Task FetchOrigin( + string username, + string password, + JobProgressReporter progressReporter, + CancellationToken cancellationToken); /// /// Requires the current HEAD to be a tracked reference. Hard resets the reference to what it tracks on the origin repository. @@ -95,34 +100,34 @@ namespace Tgstation.Server.Host.Components.Repository /// The username used for fetching from submodule repositories. /// The password used for fetching from submodule repositories. /// If a submodule update should be attempted after the merge. - /// to report 0-100 progress of the operation. + /// The to report progress of the operation. /// The for the operation. /// A resulting in the SHA of the new HEAD. Task ResetToOrigin( string username, string password, bool updateSubmodules, - Action progressReporter, + JobProgressReporter progressReporter, CancellationToken cancellationToken); /// /// Requires the current HEAD to be a reference. Hard resets the reference to the given sha. /// /// The sha hash to reset to. - /// to report 0-100 progress of the operation. + /// The to report progress of the operation. /// The for the operation. /// A resulting in the SHA of the new HEAD. - Task ResetToSha(string sha, Action progressReporter, CancellationToken cancellationToken); + Task ResetToSha(string sha, JobProgressReporter progressReporter, CancellationToken cancellationToken); /// /// Requires the current HEAD to be a tracked reference. Merges the reference to what it tracks on the origin repository. /// /// The name of the merge committer. /// The e-mail of the merge committer. - /// to report 0-100 progress of the operation. + /// The to report progress of the operation. /// The for the operation. /// A resulting in a representing the merge result that is after a fast forward, on a merge or up to date, on a conflict. - Task MergeOrigin(string committerName, string committerEmail, Action progressReporter, CancellationToken cancellationToken); + Task MergeOrigin(string committerName, string committerEmail, JobProgressReporter progressReporter, CancellationToken cancellationToken); /// /// Runs the synchronize event script and attempts to push any changes made to the if on a tracked branch. @@ -131,11 +136,18 @@ namespace Tgstation.Server.Host.Components.Repository /// The password to fetch from the origin repository. /// The name of the potential committer. /// The e-mail of the potential committer. - /// to report 0-100 progress of the operation. + /// The to report progress of the operation. /// If the synchronizations should be made to the tracked reference as opposed to a temporary branch. /// The for the operation. /// A resulting in if commits were pushed to the tracked origin reference, otherwise. - Task Sychronize(string username, string password, string committerName, string committerEmail, Action progressReporter, bool synchronizeTrackedBranch, CancellationToken cancellationToken); + Task Sychronize( + string username, + string password, + string committerName, + string committerEmail, + JobProgressReporter progressReporter, + bool synchronizeTrackedBranch, + CancellationToken cancellationToken); /// /// Copies the current working directory to a given . diff --git a/src/Tgstation.Server.Host/Components/Repository/IRepositoryManager.cs b/src/Tgstation.Server.Host/Components/Repository/IRepositoryManager.cs index 9c239ea773..03a28fe41b 100644 --- a/src/Tgstation.Server.Host/Components/Repository/IRepositoryManager.cs +++ b/src/Tgstation.Server.Host/Components/Repository/IRepositoryManager.cs @@ -2,6 +2,8 @@ using System.Threading; using System.Threading.Tasks; +using Tgstation.Server.Host.Jobs; + namespace Tgstation.Server.Host.Components.Repository { /// @@ -15,7 +17,7 @@ namespace Tgstation.Server.Host.Components.Repository bool InUse { get; } /// - /// If a operation is in progress. + /// If a operation is in progress. /// bool CloneInProgress { get; } @@ -33,7 +35,7 @@ namespace Tgstation.Server.Host.Components.Repository /// The branch to clone. /// The username to clone from . /// The password to clone from . - /// A function to report 0-100 progress of the clone. + /// The for progress of the clone. /// If submodules should be recusively cloned and initialized. /// The for the operation. /// The newly cloned , if one already exists. @@ -42,7 +44,7 @@ namespace Tgstation.Server.Host.Components.Repository string initialBranch, string username, string password, - Action progressReporter, + JobProgressReporter progressReporter, bool recurseSubmodules, CancellationToken cancellationToken); diff --git a/src/Tgstation.Server.Host/Components/Repository/Repository.cs b/src/Tgstation.Server.Host/Components/Repository/Repository.cs index a8a1903301..c3f932e4e6 100644 --- a/src/Tgstation.Server.Host/Components/Repository/Repository.cs +++ b/src/Tgstation.Server.Host/Components/Repository/Repository.cs @@ -114,20 +114,22 @@ namespace Tgstation.Server.Host.Components.Repository /// /// Converts a given to a . /// - /// to report 0-100 progress of the operation. + /// The of the operation. + /// The stage argument for . /// A based on . - static CheckoutProgressHandler CheckoutProgressHandler(Action progressReporter) => (a, completedSteps, totalSteps) => progressReporter((int)(((float)completedSteps) / totalSteps * 100)); + static CheckoutProgressHandler CheckoutProgressHandler(JobProgressReporter progressReporter, string stage) => (a, completedSteps, totalSteps) => progressReporter(stage, (int)(((float)completedSteps) / totalSteps * 100)); /// /// Generate a from a given and . /// - /// to report 0-100 progress of the operation. + /// The of the operation. + /// The stage argument for . /// The for the operation. /// A new based on . - static TransferProgressHandler TransferProgressHandler(Action progressReporter, CancellationToken cancellationToken) => (transferProgress) => + static TransferProgressHandler TransferProgressHandler(JobProgressReporter progressReporter, string stage, CancellationToken cancellationToken) => (transferProgress) => { var percentage = 100 * (((float)transferProgress.IndexedObjects + transferProgress.ReceivedObjects) / (transferProgress.TotalObjects * 2)); - progressReporter((int)percentage); + progressReporter(stage, (int)percentage); return !cancellationToken.IsCancellationRequested; }; @@ -204,7 +206,7 @@ namespace Tgstation.Server.Host.Components.Repository string username, string password, bool updateSubmodules, - Action progressReporter, + JobProgressReporter progressReporter, CancellationToken cancellationToken) { if (testMergeParameters == null) @@ -258,7 +260,8 @@ namespace Tgstation.Server.Host.Components.Repository logger.LogTrace("Fetching refspec {0}...", refSpec); var remote = libGitRepo.Network.Remotes.First(); - progressReporter(0); + var stage = $"Fetch {refSpec}"; + progressReporter(stage, 0); commands.Fetch( libGitRepo, refSpecList, @@ -267,7 +270,10 @@ namespace Tgstation.Server.Host.Components.Repository { Prune = true, OnProgress = (a) => !cancellationToken.IsCancellationRequested, - OnTransferProgress = TransferProgressHandler(percentage => progressReporter(percentage / 2), cancellationToken), + OnTransferProgress = TransferProgressHandler( + (lambdaStage, progress) => progressReporter(lambdaStage, progress / 2), + stage, + cancellationToken), OnUpdateTips = (a, b, c) => !cancellationToken.IsCancellationRequested, CredentialsProvider = credentialsProvider.GenerateCredentialsHandler(username, password), }, @@ -299,7 +305,9 @@ namespace Tgstation.Server.Host.Components.Repository FailOnConflict = true, FastForwardStrategy = FastForwardStrategy.NoFastForward, SkipReuc = true, - OnCheckoutProgress = CheckoutProgressHandler(percentage => progressReporter(50 + (percentage / 2))), + OnCheckoutProgress = CheckoutProgressHandler( + (lambdaStage, progress) => progressReporter(lambdaStage, 50 + (progress / 2)), + $"Merge {testMergeParameters.TargetCommitSha}"), }); } finally @@ -354,7 +362,11 @@ namespace Tgstation.Server.Host.Components.Repository .ConfigureAwait(false); if (updateSubmodules) - await UpdateSubmodules(percentage => progressReporter(66 + (percentage / 3)), username, password, cancellationToken).ConfigureAwait(false); + await UpdateSubmodules( + (stage, progress) => progressReporter(stage, 66 + (progress.Value / 3)), + username, + password, + cancellationToken).ConfigureAwait(false); } await eventConsumer.HandleEvent( @@ -378,7 +390,7 @@ namespace Tgstation.Server.Host.Components.Repository string username, string password, bool updateSubmodules, - Action progressReporter, + JobProgressReporter progressReporter, CancellationToken cancellationToken) { if (committish == null) @@ -391,7 +403,10 @@ namespace Tgstation.Server.Host.Components.Repository () => { libGitRepo.RemoveUntrackedFiles(); - RawCheckout(committish, percentage => progressReporter(percentage * (updateSubmodules ? 2 : 3) / 3), cancellationToken); + RawCheckout( + committish, + (stage, progress) => progressReporter(stage, progress * (updateSubmodules ? 2 : 3) / 3), + cancellationToken); }, cancellationToken, DefaultIOManager.BlockingTaskCreationOptions, @@ -399,11 +414,15 @@ namespace Tgstation.Server.Host.Components.Repository .ConfigureAwait(false); if (updateSubmodules) - await UpdateSubmodules(percentage => progressReporter(66 + (percentage / 3)), username, password, cancellationToken).ConfigureAwait(false); + await UpdateSubmodules( + (stage, progress) => progressReporter(stage, 66 + (progress / 3)), + username, + password, + cancellationToken).ConfigureAwait(false); } /// - public async Task FetchOrigin(string username, string password, Action progressReporter, CancellationToken cancellationToken) + public async Task FetchOrigin(string username, string password, JobProgressReporter progressReporter, CancellationToken cancellationToken) { if (progressReporter == null) throw new ArgumentNullException(nameof(progressReporter)); @@ -425,7 +444,7 @@ namespace Tgstation.Server.Host.Components.Repository { Prune = true, OnProgress = (a) => !cancellationToken.IsCancellationRequested, - OnTransferProgress = TransferProgressHandler(progressReporter, cancellationToken), + OnTransferProgress = TransferProgressHandler(progressReporter, "Fetch Origin", cancellationToken), OnUpdateTips = (a, b, c) => !cancellationToken.IsCancellationRequested, CredentialsProvider = credentialsProvider.GenerateCredentialsHandler(username, password), }, @@ -447,7 +466,12 @@ namespace Tgstation.Server.Host.Components.Repository } /// - public async Task ResetToOrigin(string username, string password, bool updateSubmodules, Action progressReporter, CancellationToken cancellationToken) + public async Task ResetToOrigin( + string username, + string password, + bool updateSubmodules, + JobProgressReporter progressReporter, + CancellationToken cancellationToken) { if (progressReporter == null) throw new ArgumentNullException(nameof(progressReporter)); @@ -458,16 +482,16 @@ namespace Tgstation.Server.Host.Components.Repository await eventConsumer.HandleEvent(EventType.RepoResetOrigin, new List { trackedBranch.FriendlyName, trackedBranch.Tip.Sha }, cancellationToken).ConfigureAwait(false); await ResetToSha( trackedBranch.Tip.Sha, - percentage => progressReporter(percentage / (updateSubmodules ? 2 : 1)), + (stage, progress) => progressReporter(stage, progress / (updateSubmodules ? 2 : 1)), cancellationToken) .ConfigureAwait(false); if (updateSubmodules) - await UpdateSubmodules(percentage => progressReporter(50 + (percentage / 2)), username, password, cancellationToken).ConfigureAwait(false); + await UpdateSubmodules((stage, progress) => progressReporter(stage, 50 + (progress / 2)), username, password, cancellationToken).ConfigureAwait(false); } /// - public Task ResetToSha(string sha, Action progressReporter, CancellationToken cancellationToken) => Task.Factory.StartNew( + public Task ResetToSha(string sha, JobProgressReporter progressReporter, CancellationToken cancellationToken) => Task.Factory.StartNew( () => { if (sha == null) @@ -488,7 +512,7 @@ namespace Tgstation.Server.Host.Components.Repository libGitRepo.Reset(ResetMode.Hard, gitObject.Peel(), new CheckoutOptions { - OnCheckoutProgress = CheckoutProgressHandler(progressReporter), + OnCheckoutProgress = CheckoutProgressHandler(progressReporter, $"Reset to {gitObject.Sha}"), }); }, cancellationToken, @@ -520,7 +544,11 @@ namespace Tgstation.Server.Host.Components.Repository TaskScheduler.Current); /// - public async Task MergeOrigin(string committerName, string committerEmail, Action progressReporter, CancellationToken cancellationToken) + public async Task MergeOrigin( + string committerName, + string committerEmail, + JobProgressReporter progressReporter, + CancellationToken cancellationToken) { if (progressReporter == null) throw new ArgumentNullException(nameof(progressReporter)); @@ -553,7 +581,7 @@ namespace Tgstation.Server.Host.Components.Repository FailOnConflict = true, FastForwardStrategy = FastForwardStrategy.Default, SkipReuc = true, - OnCheckoutProgress = CheckoutProgressHandler(progressReporter), + OnCheckoutProgress = CheckoutProgressHandler(progressReporter, "Merge Origin"), }); cancellationToken.ThrowIfCancellationRequested(); @@ -563,7 +591,7 @@ namespace Tgstation.Server.Host.Components.Repository logger.LogDebug("Merge conflict, aborting and reverting to {0}", oldHead.FriendlyName); libGitRepo.Reset(ResetMode.Hard, oldTip, new CheckoutOptions { - OnCheckoutProgress = CheckoutProgressHandler(progressReporter), + OnCheckoutProgress = CheckoutProgressHandler(progressReporter, $"Hard Reset to {oldHead.FriendlyName}"), }); cancellationToken.ThrowIfCancellationRequested(); } @@ -590,7 +618,7 @@ namespace Tgstation.Server.Host.Components.Repository string password, string committerName, string committerEmail, - Action progressReporter, + JobProgressReporter progressReporter, bool synchronizeTrackedBranch, CancellationToken cancellationToken) { @@ -648,7 +676,7 @@ namespace Tgstation.Server.Host.Components.Repository { libGitRepo.Reset(ResetMode.Hard, libGitRepo.Head.Tip, new CheckoutOptions { - OnCheckoutProgress = CheckoutProgressHandler(progress => progressReporter(progress / 10)), + OnCheckoutProgress = CheckoutProgressHandler((stage, progress) => progressReporter(stage, progress / 10), "Hard reset and remove untracked files"), }); cancellationToken.ThrowIfCancellationRequested(); libGitRepo.RemoveUntrackedFiles(); @@ -659,7 +687,7 @@ namespace Tgstation.Server.Host.Components.Repository .ConfigureAwait(false); } - void FinalReporter(int progress) => progressReporter((int)(((float)progress) / 100 * 90)); + void FinalReporter(string stage, int? progress) => progressReporter(stage, (int)(((float)progress) / 100 * 90)); if (!synchronizeTrackedBranch) { @@ -803,19 +831,20 @@ namespace Tgstation.Server.Host.Components.Repository /// Runs a blocking force checkout to . /// /// The committish to checkout. - /// Progress reporter . + /// The for the operation. /// The for the operation. - void RawCheckout(string committish, Action progressReporter, CancellationToken cancellationToken) + void RawCheckout(string committish, JobProgressReporter progressReporter, CancellationToken cancellationToken) { logger.LogTrace("Checkout: {0}", committish); - progressReporter(0); + var stage = $"Checkout {committish}"; + progressReporter(stage, 0); cancellationToken.ThrowIfCancellationRequested(); var checkoutOptions = new CheckoutOptions { CheckoutModifiers = CheckoutModifiers.Force, - OnCheckoutProgress = CheckoutProgressHandler(progressReporter), + OnCheckoutProgress = CheckoutProgressHandler(progressReporter, stage), }; void RunCheckout() => commands.Checkout( @@ -855,10 +884,10 @@ namespace Tgstation.Server.Host.Components.Repository /// /// The username to fetch from the origin repository. /// The password to fetch from the origin repository. - /// to report 0-100 progress of the operation. + /// of the operation. /// The for the operation. /// A representing the running operation. - Task PushHeadToTemporaryBranch(string username, string password, Action progressReporter, CancellationToken cancellationToken) => Task.Factory.StartNew( + Task PushHeadToTemporaryBranch(string username, string password, JobProgressReporter progressReporter, CancellationToken cancellationToken) => Task.Factory.StartNew( () => { logger.LogInformation("Pushing changes to temporary remote branch..."); @@ -870,9 +899,9 @@ namespace Tgstation.Server.Host.Components.Repository try { var forcePushString = String.Format(CultureInfo.InvariantCulture, "+{0}:{0}", branch.CanonicalName); - libGitRepo.Network.Push(remote, forcePushString, GeneratePushOptions(progress => progressReporter((int)(0.9f * progress)), username, password, cancellationToken)); + libGitRepo.Network.Push(remote, forcePushString, GeneratePushOptions((stage, progress) => progressReporter(stage, (int)(0.9f * progress)), username, password, cancellationToken)); var removalString = String.Format(CultureInfo.InvariantCulture, ":{0}", branch.CanonicalName); - libGitRepo.Network.Push(remote, removalString, GeneratePushOptions(progress => progressReporter(90 + (int)(0.1f * progress)), username, password, cancellationToken)); + libGitRepo.Network.Push(remote, removalString, GeneratePushOptions((stage, progress) => progressReporter(stage, 90 + (int)(0.1f * progress)), username, password, cancellationToken)); } catch (UserCancelledException) { @@ -895,23 +924,23 @@ namespace Tgstation.Server.Host.Components.Repository /// /// Generate a standard set of . /// - /// to report 0-100 progress of the operation. + /// of the operation. /// The username for the . /// The password for the . /// The for the operation. /// A new set of . - PushOptions GeneratePushOptions(Action progressReporter, string username, string password, CancellationToken cancellationToken) => new PushOptions + PushOptions GeneratePushOptions(JobProgressReporter progressReporter, string username, string password, CancellationToken cancellationToken) => new PushOptions { OnPackBuilderProgress = (stage, current, total) => { var baseProgress = stage == PackBuilderStage.Counting ? 0 : 25; - progressReporter(baseProgress + ((int)(25 * ((float)current) / total))); + progressReporter("Push", baseProgress + ((int)(25 * ((float)current) / total))); return !cancellationToken.IsCancellationRequested; }, OnNegotiationCompletedBeforePush = (a) => !cancellationToken.IsCancellationRequested, OnPushTransferProgress = (a, sentBytes, totalBytes) => { - progressReporter(50 + ((int)(50 * ((float)sentBytes) / totalBytes))); + progressReporter("Push", 50 + ((int)(50 * ((float)sentBytes) / totalBytes))); return !cancellationToken.IsCancellationRequested; }, CredentialsProvider = credentialsProvider.GenerateCredentialsHandler(username, password), @@ -920,12 +949,12 @@ namespace Tgstation.Server.Host.Components.Repository /// /// Recusively update all s in the . /// - /// to report 0-100 progress of the operation. + /// of the operation. /// The username for the . /// The password for the . /// The for the operation. /// A representing the running operation. - async Task UpdateSubmodules(Action progressReporter, string username, string password, CancellationToken cancellationToken) + async Task UpdateSubmodules(JobProgressReporter progressReporter, string username, string password, CancellationToken cancellationToken) { var submoduleCount = libGitRepo.Submodules.Count(); if (submoduleCount == 0) @@ -940,15 +969,20 @@ namespace Tgstation.Server.Host.Components.Repository var factor = 100 / submoduleCount; foreach (var submodule in libGitRepo.Submodules) { - void LocalProgressReporter(int percentage) => progressReporter((iteration * factor) + (percentage / submoduleCount)); + void LocalProgressReporter(string stage, int percentage) => progressReporter(stage, (iteration * factor) + (percentage / submoduleCount)); var submoduleUpdateOptions = new SubmoduleUpdateOptions { Init = true, - OnTransferProgress = TransferProgressHandler(percentage => LocalProgressReporter(percentage / 2), cancellationToken), + OnTransferProgress = TransferProgressHandler( + (stage, progress) => LocalProgressReporter(stage, progress.Value / 2), + $"Fetch submodule {submodule.Name}", + cancellationToken), OnProgress = output => !cancellationToken.IsCancellationRequested, OnUpdateTips = (a, b, c) => !cancellationToken.IsCancellationRequested, CredentialsProvider = credentialsProvider.GenerateCredentialsHandler(username, password), - OnCheckoutProgress = CheckoutProgressHandler(percentage => LocalProgressReporter(50 + (percentage / 2))), + OnCheckoutProgress = CheckoutProgressHandler( + (stage, progress) => LocalProgressReporter(stage, 50 + (progress.Value / 2)), + $"Checkout submodule {submodule.Name}"), }; logger.LogDebug("Updating submodule {0}...", submodule.Name); diff --git a/src/Tgstation.Server.Host/Components/Repository/RepositoryManager.cs b/src/Tgstation.Server.Host/Components/Repository/RepositoryManager.cs index 449e05b038..41cd9eb7b7 100644 --- a/src/Tgstation.Server.Host/Components/Repository/RepositoryManager.cs +++ b/src/Tgstation.Server.Host/Components/Repository/RepositoryManager.cs @@ -104,7 +104,7 @@ namespace Tgstation.Server.Host.Components.Repository string initialBranch, string username, string password, - Action progressReporter, + JobProgressReporter progressReporter, bool recurseSubmodules, CancellationToken cancellationToken) { @@ -136,7 +136,7 @@ namespace Tgstation.Server.Host.Components.Repository OnTransferProgress = (a) => { var percentage = 100 * (((float)a.IndexedObjects + a.ReceivedObjects) / (a.TotalObjects * 2)); - progressReporter((int)percentage); + progressReporter("Cloning", (int)percentage); return !cancellationToken.IsCancellationRequested; }, RecurseSubmodules = recurseSubmodules, diff --git a/src/Tgstation.Server.Host/Controllers/JobController.cs b/src/Tgstation.Server.Host/Controllers/JobController.cs index fe8e8322a9..02395593ee 100644 --- a/src/Tgstation.Server.Host/Controllers/JobController.cs +++ b/src/Tgstation.Server.Host/Controllers/JobController.cs @@ -167,7 +167,7 @@ namespace Tgstation.Server.Host.Controllers if (job == default) return NotFound(); var api = job.ToApi(); - api.Progress = jobManager.JobProgress(job); + jobManager.SetJobProgress(api); return Json(api); } @@ -178,7 +178,7 @@ namespace Tgstation.Server.Host.Controllers /// A representing the running operation. private Task AddJobProgressResponseTransformer(JobResponse jobResponse) { - jobResponse.Progress = jobManager.JobProgress(jobResponse); + jobManager.SetJobProgress(jobResponse); return Task.CompletedTask; } } diff --git a/src/Tgstation.Server.Host/Controllers/RepositoryController.cs b/src/Tgstation.Server.Host/Controllers/RepositoryController.cs index 33cfe8d614..15c8b8e14a 100644 --- a/src/Tgstation.Server.Host/Controllers/RepositoryController.cs +++ b/src/Tgstation.Server.Host/Controllers/RepositoryController.cs @@ -452,7 +452,7 @@ namespace Tgstation.Server.Host.Controllers async Task RepositoryUpdateJobOhGodPleaseSomeoneRefactorThisItsTooFuckingBig( IInstanceCore instance, IDatabaseContextFactory databaseContextFactory, - Action progressReporter, + JobProgressReporter progressReporter, CancellationToken ct) { var repoManager = instance.RepositoryManager; @@ -478,14 +478,14 @@ namespace Tgstation.Server.Host.Controllers var numSteps = (model.NewTestMerges?.Count ?? 0) + (model.UpdateFromOrigin == true ? 1 : 0) + (!modelHasShaOrReference ? 2 : (hardResettingToOriginReference ? 3 : 1)); var doneSteps = 0; - Action NextProgressReporter() + JobProgressReporter NextProgressReporter() { var tmpDoneSteps = doneSteps; ++doneSteps; - return progress => progressReporter((progress + (100 * tmpDoneSteps)) / numSteps); + return (status, progress) => progressReporter(status, (progress + (100 * tmpDoneSteps)) / numSteps); } - progressReporter(0); + progressReporter(null, 0); // get a base line for where we are Models.RevisionInformation lastRevisionInfo = null; @@ -577,7 +577,7 @@ namespace Tgstation.Server.Host.Controllers postUpdateSha = repo.Head; } else - NextProgressReporter()(100); + NextProgressReporter()(null, 100); } } @@ -612,7 +612,7 @@ namespace Tgstation.Server.Host.Controllers await CallLoadRevInfo().ConfigureAwait(false); // we've either seen origin before or what we're checking out is on origin } else - NextProgressReporter()(100); + NextProgressReporter()(null, 100); if (hardResettingToOriginReference) { @@ -882,7 +882,7 @@ namespace Tgstation.Server.Host.Controllers if (startReference != null && repo.Head != startSha) await repo.ResetToSha(startSha, NextProgressReporter(), default).ConfigureAwait(false); else - progressReporter(100); + progressReporter(null, 100); throw; } } diff --git a/src/Tgstation.Server.Host/Jobs/IJobManager.cs b/src/Tgstation.Server.Host/Jobs/IJobManager.cs index f7cef9993d..b9130deb4d 100644 --- a/src/Tgstation.Server.Host/Jobs/IJobManager.cs +++ b/src/Tgstation.Server.Host/Jobs/IJobManager.cs @@ -2,7 +2,7 @@ using System.Threading.Tasks; using Microsoft.Extensions.Hosting; - +using Tgstation.Server.Api.Models.Response; using Tgstation.Server.Host.Models; namespace Tgstation.Server.Host.Jobs @@ -13,11 +13,10 @@ namespace Tgstation.Server.Host.Jobs public interface IJobManager : IHostedService { /// - /// Get the for a . + /// Set the and for a given . /// - /// The to get for. - /// The of . - int? JobProgress(Api.Models.Internal.Job job); + /// The to update. + void SetJobProgress(JobResponse apiResponse); /// /// Registers a given and begins running it. diff --git a/src/Tgstation.Server.Host/Jobs/JobEntrypoint.cs b/src/Tgstation.Server.Host/Jobs/JobEntrypoint.cs index b2bcc3bc2d..5835675bf6 100644 --- a/src/Tgstation.Server.Host/Jobs/JobEntrypoint.cs +++ b/src/Tgstation.Server.Host/Jobs/JobEntrypoint.cs @@ -1,5 +1,4 @@ -using System; -using System.Threading; +using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Host.Components; @@ -14,13 +13,13 @@ namespace Tgstation.Server.Host.Jobs /// The the job is running on. only when performing an instance move operation. /// The for the operation. /// The running . - /// A that will update the progress of the job. + /// The for the job. /// The for the operation. /// A representing the running operation. public delegate Task JobEntrypoint( IInstanceCore instance, IDatabaseContextFactory databaseContextFactory, Job job, - Action progressReporter, + JobProgressReporter progressReporter, CancellationToken cancellationToken); } diff --git a/src/Tgstation.Server.Host/Jobs/JobHandler.cs b/src/Tgstation.Server.Host/Jobs/JobHandler.cs index b3e40be60d..19941fbefd 100644 --- a/src/Tgstation.Server.Host/Jobs/JobHandler.cs +++ b/src/Tgstation.Server.Host/Jobs/JobHandler.cs @@ -44,6 +44,11 @@ namespace Tgstation.Server.Host.Jobs /// public int? Progress { get; set; } + /// + /// The stage of the job. + /// + public string Stage { get; set; } + /// /// Wait for to complete. /// diff --git a/src/Tgstation.Server.Host/Jobs/JobManager.cs b/src/Tgstation.Server.Host/Jobs/JobManager.cs index d12fbfd894..21b6daacf7 100644 --- a/src/Tgstation.Server.Host/Jobs/JobManager.cs +++ b/src/Tgstation.Server.Host/Jobs/JobManager.cs @@ -7,7 +7,7 @@ using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Serilog.Context; - +using Tgstation.Server.Api.Models.Response; using Tgstation.Server.Host.Components; using Tgstation.Server.Host.Database; using Tgstation.Server.Host.Extensions; @@ -221,15 +221,16 @@ namespace Tgstation.Server.Host.Jobs } /// - public int? JobProgress(Api.Models.Internal.Job job) + public void SetJobProgress(JobResponse apiResponse) { - if (job == null) - throw new ArgumentNullException(nameof(job)); + if (apiResponse == null) + throw new ArgumentNullException(nameof(apiResponse)); lock (synchronizationLock) { - if (!jobs.TryGetValue(job.Id.Value, out var handler)) - return null; - return handler.Progress; + if (!jobs.TryGetValue(apiResponse.Id.Value, out var handler)) + return; + apiResponse.Progress = handler.Progress; + apiResponse.Stage = handler.Stage; } } @@ -293,11 +294,18 @@ namespace Tgstation.Server.Host.Jobs var oldJob = job; job = new Job { Id = oldJob.Id }; - void UpdateProgress(int progress) + void UpdateProgress(string stage, int? progress) { + if (progress.HasValue + && (progress.Value < 0 || progress.Value > 100)) + throw new ArgumentOutOfRangeException(nameof(progress), "Progress must be a value from 0-100!"); + lock (synchronizationLock) if (jobs.TryGetValue(oldJob.Id.Value, out var handler)) + { + handler.Stage = stage; handler.Progress = progress; + } } await activationTcs.Task.WithToken(cancellationToken).ConfigureAwait(false); diff --git a/src/Tgstation.Server.Host/Jobs/JobProgressReporter.cs b/src/Tgstation.Server.Host/Jobs/JobProgressReporter.cs new file mode 100644 index 0000000000..e715659dc9 --- /dev/null +++ b/src/Tgstation.Server.Host/Jobs/JobProgressReporter.cs @@ -0,0 +1,13 @@ +using Tgstation.Server.Host.Models; + +namespace Tgstation.Server.Host.Jobs +{ + /// + /// Progress reporter for a . + /// + /// A description of what the job is currently doing. + /// The progress of the job on a scale from 0-100. + public delegate void JobProgressReporter( + string stage, + int? progress); +} diff --git a/tests/Tgstation.Server.Tests/IntegrationTest.cs b/tests/Tgstation.Server.Tests/IntegrationTest.cs index 1aa222d0bf..3ac21f4037 100644 --- a/tests/Tgstation.Server.Tests/IntegrationTest.cs +++ b/tests/Tgstation.Server.Tests/IntegrationTest.cs @@ -1061,7 +1061,7 @@ namespace Tgstation.Server.Tests () => { }); const string StartSha = "af4da8beb9f9b374b04a3cc4d65acca662e8cc1a"; - await repo.CheckoutObject(StartSha, null, null, true, progress => { }, default); + await repo.CheckoutObject(StartSha, null, null, true, (stage, progress) => { }, default); var result = await repo.ShaIsParent("2f8588a3ca0f6b027704a2a04381215619de3412", default); Assert.IsTrue(result); Assert.AreEqual(StartSha, repo.Head); From a850d305a1f035ef1038c19cb0ddd76e55532aa7 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Wed, 8 Sep 2021 14:46:57 -0400 Subject: [PATCH 2/7] Add a workflow to automatically merge dev into V5 --- .github/workflows/v5-merge.yml | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 .github/workflows/v5-merge.yml diff --git a/.github/workflows/v5-merge.yml b/.github/workflows/v5-merge.yml new file mode 100644 index 0000000000..79227ffe94 --- /dev/null +++ b/.github/workflows/v5-merge.yml @@ -0,0 +1,28 @@ +name: 'V5 Merge' + +on: + push: + branches: + - dev + +jobs: + master-merge: + + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v1 + + - name: Merge dev into V5 + uses: robotology/gh-action-nightly-merge@v1.2.0 + with: + stable_branch: 'dev' + development_branch: 'V5' + allow_ff: true + user_name: tgstation-server + user_email: tgstation-server@users.noreply.github.com + push_token: DEV_PUSH_TOKEN + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + DEV_PUSH_TOKEN: ${{ secrets.DEV_PUSH_TOKEN }} From 8aff5e1a3fd3a5113061d320967418d3c7268268 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Wed, 8 Sep 2021 17:51:18 -0400 Subject: [PATCH 3/7] A couple of changes to ensure a smooth transition to V5 Closes #1295 --- src/Tgstation.Server.Api/Models/ErrorCode.cs | 8 +- .../Controllers/AdministrationController.cs | 4 +- .../AdministrationTest.cs | 4 +- tools/ReleaseNotes/Program.cs | 77 ++++++++----------- 4 files changed, 40 insertions(+), 53 deletions(-) diff --git a/src/Tgstation.Server.Api/Models/ErrorCode.cs b/src/Tgstation.Server.Api/Models/ErrorCode.cs index 2b6b268cc5..323d769038 100644 --- a/src/Tgstation.Server.Api/Models/ErrorCode.cs +++ b/src/Tgstation.Server.Api/Models/ErrorCode.cs @@ -48,7 +48,7 @@ namespace Tgstation.Server.Api.Models /// /// Generic database integrity failure. /// - [Description("The operation could not be performed as it would violate database integrity. Please retry the request, making sure to not duplicate field names with existing entities.")] + [Description("The operation could not be performed as it would violate database integrity. Please retry the request, making sure to not duplicate field names with existing entities!")] DatabaseIntegrityConflict, /// @@ -58,9 +58,9 @@ namespace Tgstation.Server.Api.Models MissingHostWatchdog, /// - /// Attempted to change to a suite other than TGS4. + /// Attempted to change to a major version TGS4. /// - [Description("Cannot update to a different tgstation-server suite version.")] + [Description("Cannot downgrade to tgstation-server major version <4!")] CannotChangeServerSuite, /// @@ -72,7 +72,7 @@ namespace Tgstation.Server.Api.Models /// /// A server update was requested while another was in progress. /// - [Description("A server update was requested while another was in progress")] + [Description("A server update was requested while another was in progress!")] ServerUpdateInProgress, /// diff --git a/src/Tgstation.Server.Host/Controllers/AdministrationController.cs b/src/Tgstation.Server.Host/Controllers/AdministrationController.cs index 6b653b2eed..a7dd4c41d8 100644 --- a/src/Tgstation.Server.Host/Controllers/AdministrationController.cs +++ b/src/Tgstation.Server.Host/Controllers/AdministrationController.cs @@ -166,7 +166,7 @@ namespace Tgstation.Server.Host.Controllers foreach (var release in releases) if (Version.TryParse(release.TagName.Replace(updatesConfiguration.GitTagPrefix, String.Empty, StringComparison.Ordinal), out var version) - && version.Major == assemblyInformationProvider.Version.Major + && version.Major > 3 // Forward/backward compatible but not before TGS4 && (greatestVersion == null || version > greatestVersion)) greatestVersion = version; repoUrl = new Uri((await repositoryTask.ConfigureAwait(false)).HtmlUrl); @@ -225,7 +225,7 @@ namespace Tgstation.Server.Host.Controllers AdditionalData = "newVersion is required!", }); - if (model.NewVersion.Major != assemblyInformationProvider.Version.Major) + if (model.NewVersion.Major < 3) return BadRequest(new ErrorMessageResponse(ErrorCode.CannotChangeServerSuite)); if (!serverControl.WatchdogPresent) diff --git a/tests/Tgstation.Server.Tests/AdministrationTest.cs b/tests/Tgstation.Server.Tests/AdministrationTest.cs index 2ea67f7c8d..a3eb17075b 100644 --- a/tests/Tgstation.Server.Tests/AdministrationTest.cs +++ b/tests/Tgstation.Server.Tests/AdministrationTest.cs @@ -1,4 +1,4 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using System.Linq; using System.Threading; @@ -70,7 +70,7 @@ namespace Tgstation.Server.Tests } //we've released a few 4.x versions now, check the release checker is at least somewhat functional - Assert.AreEqual(4, model.LatestVersion.Major); + Assert.IsTrue(4 <= model.LatestVersion.Major); } } } diff --git a/tools/ReleaseNotes/Program.cs b/tools/ReleaseNotes/Program.cs index 9597fdf9df..d431b2568e 100644 --- a/tools/ReleaseNotes/Program.cs +++ b/tools/ReleaseNotes/Program.cs @@ -201,8 +201,6 @@ namespace ReleaseNotes var releases = await releasesTask.ConfigureAwait(false); - var releasingSuite = version.Major; - Version highestReleaseVersion = null; Release highestRelease = null; foreach (var I in releases) @@ -213,7 +211,7 @@ namespace ReleaseNotes continue; } - if (currentReleaseVersion.Major == releasingSuite && (highestReleaseVersion == null || currentReleaseVersion > highestReleaseVersion) && version != currentReleaseVersion) + if (currentReleaseVersion.Major == version.Major && (highestReleaseVersion == null || currentReleaseVersion > highestReleaseVersion) && version != currentReleaseVersion) { highestReleaseVersion = currentReleaseVersion; highestRelease = I; @@ -222,7 +220,7 @@ namespace ReleaseNotes if (highestReleaseVersion == null) { - Console.WriteLine("Unable to determine highest release version for suite " + releasingSuite + "!"); + Console.WriteLine("Unable to determine highest release version for major version " + version.Major + "!"); return 6; } @@ -247,49 +245,38 @@ namespace ReleaseNotes oldNotes = String.Join('\n', splits); string prefix; - switch (releasingSuite) + const string PropsPath = "build/Version.props"; + const string ControlPanelPropsPath = "build/ControlPanelVersion.props"; + + var doc = XDocument.Load(PropsPath); + var project = doc.Root; + var xmlNamespace = project.GetDefaultNamespace(); + var versionsPropertyGroup = project.Elements().First(x => x.Name == xmlNamespace + "PropertyGroup"); + + var doc2 = XDocument.Load(ControlPanelPropsPath); + var project2 = doc2.Root; + var controlPanelXmlNamespace = project2.GetDefaultNamespace(); + var controlPanelVersionsPropertyGroup = project2.Elements().First(x => x.Name == controlPanelXmlNamespace + "PropertyGroup"); + + var coreVersion = Version.Parse(versionsPropertyGroup.Element(xmlNamespace + "TgsCoreVersion").Value); + if (coreVersion != version) { - case 4: - const string PropsPath = "build/Version.props"; - const string ControlPanelPropsPath = "build/ControlPanelVersion.props"; - - var doc = XDocument.Load(PropsPath); - var project = doc.Root; - var xmlNamespace = project.GetDefaultNamespace(); - var versionsPropertyGroup = project.Elements().First(x => x.Name == xmlNamespace + "PropertyGroup"); - - var doc2 = XDocument.Load(ControlPanelPropsPath); - var project2 = doc2.Root; - var controlPanelXmlNamespace = project2.GetDefaultNamespace(); - var controlPanelVersionsPropertyGroup = project2.Elements().First(x => x.Name == controlPanelXmlNamespace + "PropertyGroup"); - - var coreVersion = Version.Parse(versionsPropertyGroup.Element(xmlNamespace + "TgsCoreVersion").Value); - if (coreVersion != version) - { - Console.WriteLine("Received a different version on command line than in Version.props!"); - return 10; - } - - var apiVersion = Version.Parse(versionsPropertyGroup.Element(xmlNamespace + "TgsApiVersion").Value); - var configVersion = Version.Parse(versionsPropertyGroup.Element(xmlNamespace + "TgsConfigVersion").Value); - var dmApiVersion = Version.Parse(versionsPropertyGroup.Element(xmlNamespace + "TgsDmapiVersion").Value); - var interopVersion = Version.Parse(versionsPropertyGroup.Element(xmlNamespace + "TgsInteropVersion").Value); - var webControlVersion = Version.Parse(controlPanelVersionsPropertyGroup.Element(controlPanelXmlNamespace + "TgsControlPanelVersion").Value); - var hostWatchdogVersion = Version.Parse(versionsPropertyGroup.Element(xmlNamespace + "TgsHostWatchdogVersion").Value); - - if (webControlVersion.Major == 0) - postControlPanelMessage = true; - - prefix = $"Please refer to the [README](https://github.com/tgstation/tgstation-server#setup) for setup instructions.{Environment.NewLine}{Environment.NewLine}#### Component Versions\nCore: {coreVersion}\nConfiguration: {configVersion}\nHTTP API: {apiVersion}\nDreamMaker API: {dmApiVersion} (Interop: {interopVersion})\n[Web Control Panel](https://github.com/tgstation/tgstation-server-webpanel): {webControlVersion}\nHost Watchdog: {hostWatchdogVersion}"; - break; - case 3: - prefix = "The /tg/station server suite"; - break; - default: - prefix = "See https://tgstation.github.io/tgstation-server for installation instructions"; - break; + Console.WriteLine("Received a different version on command line than in Version.props!"); + return 10; } + var apiVersion = Version.Parse(versionsPropertyGroup.Element(xmlNamespace + "TgsApiVersion").Value); + var configVersion = Version.Parse(versionsPropertyGroup.Element(xmlNamespace + "TgsConfigVersion").Value); + var dmApiVersion = Version.Parse(versionsPropertyGroup.Element(xmlNamespace + "TgsDmapiVersion").Value); + var interopVersion = Version.Parse(versionsPropertyGroup.Element(xmlNamespace + "TgsInteropVersion").Value); + var webControlVersion = Version.Parse(controlPanelVersionsPropertyGroup.Element(controlPanelXmlNamespace + "TgsControlPanelVersion").Value); + var hostWatchdogVersion = Version.Parse(versionsPropertyGroup.Element(xmlNamespace + "TgsHostWatchdogVersion").Value); + + if (webControlVersion.Major == 0) + postControlPanelMessage = true; + + prefix = $"Please refer to the [README](https://github.com/tgstation/tgstation-server#setup) for setup instructions.{Environment.NewLine}{Environment.NewLine}#### Component Versions\nCore: {coreVersion}\nConfiguration: {configVersion}\nHTTP API: {apiVersion}\nDreamMaker API: {dmApiVersion} (Interop: {interopVersion})\n[Web Control Panel](https://github.com/tgstation/tgstation-server-webpanel): {webControlVersion}\nHost Watchdog: {hostWatchdogVersion}"; + var newNotes = new StringBuilder(prefix); if (postControlPanelMessage) { @@ -453,7 +440,7 @@ namespace ReleaseNotes newNotes.Append(Environment.NewLine); - if (version != new Version(4, 1, 0)) + if (version.Minor != 0 && version.Build != 0) newNotes.Append(oldNotes); const string OutputPath = "release_notes.md"; From 5c31b265617a1f468bccaca79346a123f541692f Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Wed, 8 Sep 2021 18:04:40 -0400 Subject: [PATCH 4/7] Remove more V4 references --- .github/CONTRIBUTING.md | 20 ++--- .github/workflows/ci-suite.yml | 76 +++++++++---------- README.md | 24 +++--- build/Dockerfile | 2 +- docs/API.dox | 10 +-- docs/Architecture.dox | 16 ++-- docs/Features.dox | 11 +-- src/DMAPI/tgs/README.md | 2 +- src/DMAPI/tgs/v5/api.dm | 2 +- src/Tgstation.Server.Api/Models/ErrorCode.cs | 2 +- src/Tgstation.Server.Host.Service/Program.cs | 4 +- .../Controllers/HomeController.cs | 2 +- src/Tgstation.Server.Host/Core/Application.cs | 2 +- .../Database/DatabaseContext.cs | 2 +- .../Extensions/ServiceCollectionExtensions.cs | 2 +- .../Setup/SetupWizard.cs | 2 +- .../Chat/Providers/TestDiscordProvider.cs | 6 +- .../AdministrationTest.cs | 4 +- .../Instance/ChatTest.cs | 10 +-- .../Instance/RepositoryTest.cs | 6 +- .../Tgstation.Server.Tests/IntegrationTest.cs | 16 ++-- tests/Tgstation.Server.Tests/TestingServer.cs | 16 ++-- tools/ReleaseNotes/Program.cs | 2 +- 23 files changed, 121 insertions(+), 118 deletions(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index a096c248c4..b322d8425b 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -39,14 +39,14 @@ You need the Dotnet 3.1 SDK and npm>=v5.7 (in your PATH) to compile the server. The recommended IDE is Visual Studio 2019 which has installation options for both of these. In order to run the integration tests you must have the following environment variables set: -- `TGS4_TEST_DATABASE_TYPE`: `MySql`, `MariaDB`, `PostgresSql`, or `SqlServer`. -- `TGS4_TEST_CONNECTION_STRING`: To a valid database connection string. You can use the setup wizard to create one. -- `TSG4_TEST_DISCORD_TOKEN`: To a valid discord bot token. -- `TGS4_TEST_DISCORD_CHANNEL`: To a valid discord channel ID that the above bot can access. -- `TGS4_TEST_IRC_CONNECTION_STRING`: To a valid TGS4 IRC connection string. See the code for [IrcConnectionStringBuilder](../src/Tgstation.Server.Api/Models/IrcConnectionStringBuilder.cs) for details. -- `TGS4_TEST_IRC_CHANNEL`: To a valid IRC channel accessible with the above connection. -- `TGS4_TEST_BRANCH`: Should be either `dev` or `master` depending on what you are working off of. Used for repository tests. -- (Optional) `TGS4_TEST_GITHUB_TOKEN`: A GitHub personal access token with no scopes used to bypass rate limits. +- `TGS_TEST_DATABASE_TYPE`: `MySql`, `MariaDB`, `PostgresSql`, or `SqlServer`. +- `TGS_TEST_CONNECTION_STRING`: To a valid database connection string. You can use the setup wizard to create one. +- `TSG_TEST_DISCORD_TOKEN`: To a valid discord bot token. +- `TGS_TEST_DISCORD_CHANNEL`: To a valid discord channel ID that the above bot can access. +- `TGS_TEST_IRC_CONNECTION_STRING`: To a valid IRC connection string. See the code for [IrcConnectionStringBuilder](../src/Tgstation.Server.Api/Models/IrcConnectionStringBuilder.cs) for details. +- `TGS_TEST_IRC_CHANNEL`: To a valid IRC channel accessible with the above connection. +- `TGS_TEST_BRANCH`: Should be either `dev` or `master` depending on what you are working off of. Used for repository tests. +- (Optional) `TGS_TEST_GITHUB_TOKEN`: A GitHub personal access token with no scopes used to bypass rate limits. ### Know your Code @@ -240,9 +240,9 @@ The NuGet package Tgstation.Server.Client is another part of the suite which sho _This section mainly applies to people with write access to the repository. Anyone is free to propose their work and maintainers will triage it appropriately._ -When issues affecting the server come in, they should be lebeled appropriately and either put into the `V4 Backlog` milestone or current patch milestone depending on if it's a feature request or bug. +When issues affecting the server come in, they should be lebeled appropriately and either put into the `Backlog` milestone or current patch milestone depending on if it's a feature request or bug. -After a minor release, the team should decide at that time what will go into it and setup the milestone accordingly. At this point the `Backlog` label should be removed and replaced with `Ready` and the milestone changed from `V4 Backlog` to `v4.X.0` with X being the minor release version. +After a minor release, the team should decide at that time what will go into it and setup the milestone accordingly. At this point the `Backlog` label should be removed and replaced with `Ready` and the milestone changed from `Backlog` to `vX.Y.0` with X/Y being the major/minor release versions respectively. Assign work before beginning on it. When work is started, replace the `Ready` label with the `Work In Progress` label. diff --git a/.github/workflows/ci-suite.yml b/.github/workflows/ci-suite.yml index 7d4dcaaf04..63a9677b57 100644 --- a/.github/workflows/ci-suite.yml +++ b/.github/workflows/ci-suite.yml @@ -1,4 +1,4 @@ -name: 'CI' +name: 'CI' on: push: @@ -11,13 +11,13 @@ on: - master env: - TGS4_DOTNET_VERSION: 3.1.x - TGS4_TEST_DISCORD_CHANNEL: ${{ secrets.DISCORD_CHANNEL_ID }} - TGS4_TEST_DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }} - TGS4_TEST_IRC_CHANNEL: ${{ secrets.IRC_CHANNEL }} - TGS4_TEST_IRC_CONNECTION_STRING: ${{ secrets.IRC_CONNECTION_STRING }} - TGS4_TEST_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - TGS4_RELEASE_NOTES_TOKEN: ${{ secrets.DEV_PUSH_TOKEN }} + TGS_DOTNET_VERSION: 3.1.x + TGS_TEST_DISCORD_CHANNEL: ${{ secrets.DISCORD_CHANNEL_ID }} + TGS_TEST_DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }} + TGS_TEST_IRC_CHANNEL: ${{ secrets.IRC_CHANNEL }} + TGS_TEST_IRC_CONNECTION_STRING: ${{ secrets.IRC_CONNECTION_STRING }} + TGS_TEST_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TGS_RELEASE_NOTES_TOKEN: ${{ secrets.DEV_PUSH_TOKEN }} jobs: dmapi-build: @@ -134,7 +134,7 @@ jobs: - name: Setup dotnet uses: actions/setup-dotnet@v1 with: - dotnet-version: ${{ env.TGS4_DOTNET_VERSION }} + dotnet-version: ${{ env.TGS_DOTNET_VERSION }} - name: Checkout uses: actions/checkout@v1 @@ -161,7 +161,7 @@ jobs: - name: Setup dotnet uses: actions/setup-dotnet@v1 with: - dotnet-version: ${{ env.TGS4_DOTNET_VERSION }} + dotnet-version: ${{ env.TGS_DOTNET_VERSION }} - name: Checkout uses: actions/checkout@v1 @@ -185,8 +185,8 @@ jobs: name: Windows Integration Test needs: dmapi-build env: - TGS4_TEST_DATABASE_TYPE: SqlServer - TGS4_TEST_DUMP_API_SPEC: yes + TGS_TEST_DATABASE_TYPE: SqlServer + TGS_TEST_DUMP_API_SPEC: yes concurrency: integration-windows-${{ github.head_ref }} strategy: max-parallel: 2 @@ -198,35 +198,35 @@ jobs: - name: Setup dotnet uses: actions/setup-dotnet@v1 with: - dotnet-version: ${{ env.TGS4_DOTNET_VERSION }} + dotnet-version: ${{ env.TGS_DOTNET_VERSION }} - name: Set General__UseBasicWatchdog if: ${{ matrix.watchdog-type == 'Basic' }} run: echo "General__UseBasicWatchdog=true" >> $Env:GITHUB_ENV - - name: Set TGS4_TEST_CONNECTION_STRING + - name: Set TGS_TEST_CONNECTION_STRING shell: bash run: | - TGS4_CONNSTRING_VALUE="Server=(localdb)\MSSQLLocalDB;Integrated Security=true;Initial Catalog=TGS_${{ matrix.watchdog-type }}_${{ matrix.configuration }};Application Name=tgstation-server" - echo "TGS4_TEST_CONNECTION_STRING=$(echo $TGS4_CONNSTRING_VALUE)" >> $GITHUB_ENV + TGS_CONNSTRING_VALUE="Server=(localdb)\MSSQLLocalDB;Integrated Security=true;Initial Catalog=TGS_${{ matrix.watchdog-type }}_${{ matrix.configuration }};Application Name=tgstation-server" + echo "TGS_TEST_CONNECTION_STRING=$(echo $TGS_CONNSTRING_VALUE)" >> $GITHUB_ENV - name: Checkout uses: actions/checkout@v1 - - name: Set TGS4_TEST_PULL_REQUEST_NUMBER + - name: Set TGS_TEST_PULL_REQUEST_NUMBER if: ${{ github.event_name == 'pull_request' }} - run: echo "TGS4_TEST_PULL_REQUEST_NUMBER=${{ github.event.number }}" >> $Env:GITHUB_ENV + run: echo "TGS_TEST_PULL_REQUEST_NUMBER=${{ github.event.number }}" >> $Env:GITHUB_ENV - - name: Set TGS4_GITHUB_REF for PR + - name: Set TGS_GITHUB_REF for PR if: ${{ github.event_name == 'pull_request' }} - run: echo "TGS4_GITHUB_REF=${{ github.base_ref }}" >> $Env:GITHUB_ENV + run: echo "TGS_GITHUB_REF=${{ github.base_ref }}" >> $Env:GITHUB_ENV - - name: Set TGS4_GITHUB_REF for push + - name: Set TGS_GITHUB_REF for push if: ${{ github.event_name == 'push' }} shell: bash run: | TEMP_GITHUB_REF="${{ github.event.ref }}" - echo "TGS4_GITHUB_REF=${TEMP_GITHUB_REF##*/}" >> $GITHUB_ENV + echo "TGS_GITHUB_REF=${TEMP_GITHUB_REF##*/}" >> $GITHUB_ENV - name: Clean package cache as a temporary workaround for actions/setup-dotnet#155 run: dotnet clean && dotnet nuget locals all --clear @@ -329,31 +329,31 @@ jobs: - name: Setup dotnet uses: actions/setup-dotnet@v1 with: - dotnet-version: ${{ env.TGS4_DOTNET_VERSION }} + dotnet-version: ${{ env.TGS_DOTNET_VERSION }} - name: Set Sqlite Connection Info if: ${{ matrix.database-type == 'Sqlite' }} run: | - echo "TGS4_TEST_DATABASE_TYPE=Sqlite" >> $GITHUB_ENV - echo "TGS4_TEST_CONNECTION_STRING=Data Source=TGS_${{ matrix.watchdog-type }}_${{ matrix.configuration }}.sqlite3;Mode=ReadWriteCreate" >> $GITHUB_ENV + echo "TGS_TEST_DATABASE_TYPE=Sqlite" >> $GITHUB_ENV + echo "TGS_TEST_CONNECTION_STRING=Data Source=TGS_${{ matrix.watchdog-type }}_${{ matrix.configuration }}.sqlite3;Mode=ReadWriteCreate" >> $GITHUB_ENV - name: Set PostgresSql Connection Info if: ${{ matrix.database-type == 'PostgresSql' }} run: | - echo "TGS4_TEST_DATABASE_TYPE=PostgresSql" >> $GITHUB_ENV - echo "TGS4_TEST_CONNECTION_STRING=Application Name=tgstation-server;Host=127.0.0.1;Username=postgres;Password=postgres;Database=TGS__${{ matrix.watchdog-type }}_${{ matrix.configuration }}" >> $GITHUB_ENV + echo "TGS_TEST_DATABASE_TYPE=PostgresSql" >> $GITHUB_ENV + echo "TGS_TEST_CONNECTION_STRING=Application Name=tgstation-server;Host=127.0.0.1;Username=postgres;Password=postgres;Database=TGS__${{ matrix.watchdog-type }}_${{ matrix.configuration }}" >> $GITHUB_ENV - name: Set MariaDB Connection Info if: ${{ matrix.database-type == 'MariaDB' }} run: | - echo "TGS4_TEST_DATABASE_TYPE=MariaDB" >> $GITHUB_ENV - echo "TGS4_TEST_CONNECTION_STRING=Server=127.0.0.1;uid=root;pwd=mariadb;database=tgs__${{ matrix.watchdog-type }}_${{ matrix.configuration }}" >> $GITHUB_ENV + echo "TGS_TEST_DATABASE_TYPE=MariaDB" >> $GITHUB_ENV + echo "TGS_TEST_CONNECTION_STRING=Server=127.0.0.1;uid=root;pwd=mariadb;database=tgs__${{ matrix.watchdog-type }}_${{ matrix.configuration }}" >> $GITHUB_ENV - name: Set MySQL Connection Info if: ${{ matrix.database-type == 'MySql' }} run: | - echo "TGS4_TEST_DATABASE_TYPE=MySql" >> $GITHUB_ENV - echo "TGS4_TEST_CONNECTION_STRING=Server=127.0.0.1;Port=3307;uid=root;pwd=mysql;database=tgs__${{ matrix.watchdog-type }}_${{ matrix.configuration }}" >> $GITHUB_ENV + echo "TGS_TEST_DATABASE_TYPE=MySql" >> $GITHUB_ENV + echo "TGS_TEST_CONNECTION_STRING=Server=127.0.0.1;Port=3307;uid=root;pwd=mysql;database=tgs__${{ matrix.watchdog-type }}_${{ matrix.configuration }}" >> $GITHUB_ENV echo "Database__ServerVersion=5.7.31" >> $GITHUB_ENV - name: Set General__UseBasicWatchdog @@ -363,20 +363,20 @@ jobs: - name: Checkout uses: actions/checkout@v1 - - name: Set TGS4_TEST_PULL_REQUEST_NUMBER + - name: Set TGS_TEST_PULL_REQUEST_NUMBER if: ${{ github.event_name == 'pull_request' }} - run: echo "TGS4_TEST_PULL_REQUEST_NUMBER=${{ github.event.number }}" >> $GITHUB_ENV + run: echo "TGS_TEST_PULL_REQUEST_NUMBER=${{ github.event.number }}" >> $GITHUB_ENV - - name: Set TGS4_GITHUB_REF for PR + - name: Set TGS_GITHUB_REF for PR if: ${{ github.event_name == 'pull_request' }} - run: echo "TGS4_GITHUB_REF=${{ github.base_ref }}" >> $GITHUB_ENV + run: echo "TGS_GITHUB_REF=${{ github.base_ref }}" >> $GITHUB_ENV - - name: Set TGS4_GITHUB_REF for push + - name: Set TGS_GITHUB_REF for push if: ${{ github.event_name == 'push' }} shell: bash run: | TEMP_GITHUB_REF="${{ github.event.ref }}" - echo "TGS4_GITHUB_REF=${TEMP_GITHUB_REF##*/}" >> $GITHUB_ENV + echo "TGS_GITHUB_REF=${TEMP_GITHUB_REF##*/}" >> $GITHUB_ENV - name: Run Integration Test run: | @@ -658,7 +658,7 @@ jobs: - name: Setup dotnet uses: actions/setup-dotnet@v1 with: - dotnet-version: ${{ env.TGS4_DOTNET_VERSION }} + dotnet-version: ${{ env.TGS_DOTNET_VERSION }} - name: Checkout uses: actions/checkout@v1 diff --git a/README.md b/README.md index 80a4a5d4d7..91f5ec9a98 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# tgstation-server v4: +# tgstation-server: ![CI](https://github.com/tgstation/tgstation-server/workflows/CI/badge.svg) [![codecov](https://codecov.io/gh/tgstation/tgstation-server/branch/master/graph/badge.svg)](https://codecov.io/gh/tgstation/tgstation-server) @@ -12,7 +12,7 @@ This is a toolset to manage production BYOND servers. It includes the ability to ### Legacy Servers -Older server versions can be found in the V# branches of this repository. Note that V4 is nearly fully incompatible with existing installations. Only some static files may be copied over: https://github.com/tgstation/tgstation-server#static-files +Older server versions can be found in the V# branches of this repository. Note that the current server fully incompatible with installations before version 4. Only some static files may be copied over: https://github.com/tgstation/tgstation-server#static-files ## Setup @@ -23,7 +23,7 @@ Older server versions can be found in the V# branches of this repository. Note t ### Installation -1. [Download the latest V4 release .zip](https://github.com/tgstation/tgstation-server/releases/latest). The `ServerService` package will only work on Windows. Choose `ServerConsole` if that is not your target OS or you prefer not to use the Windows service. +1. [Download the latest release .zip](https://github.com/tgstation/tgstation-server/releases/latest). The `ServerService` package will only work on Windows. Choose `ServerConsole` if that is not your target OS or you prefer not to use the Windows service. 2. Extract the .zip file to where you want the server to run from. Note the account running the server must have write and delete access to the `lib` subdirectory. #### Windows @@ -67,7 +67,7 @@ docker run \ -p 5000:5000 \ # Port bridge for accessing TGS, you can change this if you need -p 0.0.0.0:: \ # Port bridge for accessing DreamDaemon -v /path/to/your/configfile/directory:/config_data \ # Recommended, create a volume mapping for server configuration - -v /path/to/store/instances:/tgs4_instances \ # Recommended, create a volume mapping for server instances + -v /path/to/store/instances:/tgs_instances \ # Recommended, create a volume mapping for server instances -v /path/to/your/log/folder:/tgs_logs \ # Recommended, create a volume mapping for server logs tgstation/server[:] ``` @@ -79,7 +79,7 @@ Important note about port exposure: The internal port used by DreamDaemon _**MUS Note although `/app/lib` is specified as a volume mount point in the `Dockerfile`, unless you REALLY know what you're doing. Do not mount any volumes over this for fear of breaking your container. -The configuration option `General:ValidInstancePaths` will be preconfigured to point to `/tgs4_instances`. It is recommended you don't change this. +The configuration option `General:ValidInstancePaths` will be preconfigured to point to `/tgs_instances`. It is recommended you don't change this. Note that this container is meant to be long running. Updates are handled internally as opposed to at the container level. @@ -89,7 +89,7 @@ If using manual configuration, before starting your container make sure the afor ### Configuring -The first time you run TGS4 you should be prompted with a configuration wizard which will guide you through setting up your `appsettings.Production.yml` +The first time you run TGS you should be prompted with a configuration wizard which will guide you through setting up your `appsettings.Production.yml` This wizard will, generally, run whenever the server is launched without detecting the config yml. Follow the instructions below to perform this process manually. @@ -107,7 +107,7 @@ The latter two are not recommended as they cannot be dynamically changed at runt Create an `appsettings.Production.yml` file next to `appsettings.yml`. This will override the default settings in `appsettings.yml` with your production settings. There are a few keys meant to be changed by hosts. Modifying any config files while the server is running will trigger a safe restart (Keeps DreamDaemon instances running). Note these are all case-sensitive: -- `General:ConfigVersion`: Suppresses warnings about out of date config versions. You should change this after updating TGS to one with a new config version. The current version can be found on the releases page for your server version (This field did not exist before v4.4.0). +- `General:ConfigVersion`: Suppresses warnings about out of date config versions. You should change this after updating TGS to one with a new config version. The current version can be found on the releases page for your server version. - `General:MinimumPasswordLength`: Minimum password length requirement for database users @@ -137,7 +137,7 @@ Create an `appsettings.Production.yml` file next to `appsettings.yml`. This will - `ControlPanel:AllowedOrigins`: Set the Access-Control-Allow-Origin headers to this list of origins for all responses (also enables all headers and methods). This is overridden by `ControlPanel:AllowAnyOrigin` -- `Elasticsearch`: tgstation-server-v4 also supports automatically ingesting its logs to ElasticSearch. You can set this up in the setup wizard, or with the following configuration: +- `Elasticsearch`: tgstation-server also supports automatically ingesting its logs to ElasticSearch. You can set this up in the setup wizard, or with the following configuration: ```yml Elasticsearch: Enable: true @@ -356,11 +356,13 @@ Instances can be either part of a swarm or not. Once in the database they cannot ## Usage -tgstation-server v4 is controlled via a RESTful HTTP json API. Documentation on this API can be found [here](https://tgstation.github.io/tgstation-server/api.html). This section serves to document the concepts of the server. The API is versioned separately from the release version. A specification for it can be found in the api-vX.X.X git releases/tags. +tgstation-server is controlled via a RESTful HTTP json API. Documentation on this API can be found [here](https://tgstation.github.io/tgstation-server/api.html). This section serves to document the concepts of the server. The API is versioned separately from the release version. A specification for it can be found in the api-vX.X.X git releases/tags. ### Updating -TGS 4 can self update without stopping your DreamDaemon servers. Any V4 release made to this repository is bound by a contract that allows changes of the runtime assemblies without stopping your servers. Database migrations are automatically applied as well. Because of this REVERTING TO LOWER VERSIONS IS NOT OFFICIALLY SUPPORTED, do so at your own risk (check changes made to `/src/Tgstation.Server.Host/Models/Migrations`). +TGS can self update without stopping your DreamDaemon servers. Releases made to this repository are bound by a contract that allows changes of the runtime assemblies without stopping your servers. Database migrations are automatically applied as well. Because of this REVERTING TO LOWER VERSIONS IS NOT OFFICIALLY SUPPORTED, do so at your own risk (check changes made to `/src/Tgstation.Server.Host/Models/Migrations`). + +Major version updates may require additional action on the part of the user (apart from the configuration changes). #### Notifications @@ -446,7 +448,7 @@ Any files and folders contained in this root level of this folder will be symbol ### Clients -Here are tools for interacting with the TGS 4 web API +Here are tools for interacting with the TGS web API - [tgstation-server-webpanel](https://github.com/tgstation/tgstation-server-webpanel): Official client and included with the server (WIP). A react web app for using tgstation-server. - [Tgstation.Server.ControlPanel](https://github.com/tgstation/Tgstation.Server.ControlPanel): Official client. A cross platform GUI for using tgstation-server. Feature complete but lacks OAuth login options. diff --git a/build/Dockerfile b/build/Dockerfile index 6a1d46b45d..507d8339c6 100644 --- a/build/Dockerfile +++ b/build/Dockerfile @@ -71,7 +71,7 @@ RUN apt-get update \ EXPOSE 5000 -ENV General__ValidInstancePaths__0 /tgs4_instances +ENV General__ValidInstancePaths__0 /tgs_instances ENV FileLogging__Directory /tgs_logs WORKDIR /app diff --git a/docs/API.dox b/docs/API.dox index 66f79a3174..bb0167c9d1 100644 --- a/docs/API.dox +++ b/docs/API.dox @@ -5,7 +5,7 @@ @section api_swag OpenAPI Spec -TGS4 has a, from code, generated OpenAPI 3.0 specification. It is much more authorative than these documents. +TGS has a, from code, generated OpenAPI 3.0 specification. It is much more authorative than these documents. The most up to date version should be found in the most recent appveyor build artifacts. It is also included in release artifacts. @@ -13,7 +13,7 @@ You can use the API explorer SwaggerUI to interact with it: https://petstore.swa @section api_intro Introduction -The TGS4 API is designed to be a fully realized RESTful service. Once hosted, follow the specified protocol for developing new clients or one off requests that provide full control over the server +The TGS API is designed to be a fully realized RESTful service. Once hosted, follow the specified protocol for developing new clients or one off requests that provide full control over the server Routes and their usages are defined as follows @@ -21,7 +21,7 @@ Routes and their usages are defined as follows @section api_lib Official Libraries -The TGS4 API's canonical definitions are provided as a .NET Standard library in the form of a nuget package located here: https://www.nuget.org/packages/Tgstation.Server.Api +The TGS API's canonical definitions are provided as a .NET Standard library in the form of a nuget package located here: https://www.nuget.org/packages/Tgstation.Server.Api An all inclusive TAP interface for using the API is also provided in this package: https://www.nuget.org/packages/Tgstation.Server.Client @@ -38,7 +38,7 @@ This document will reference the canonical C# models in the @ref Tgstation.Serve @section api_header Headers -TGS4 expects this set of headers. Failure to provide them will result in 400 error responses +TGS expects this set of headers. Failure to provide them will result in 400 error responses - User-Agent: The user agent product header value of the calling program - Api: Another product header value representing the version of the API to use. Currently this must be: Tgstation.Server.Api/4.0.0.0 @@ -124,7 +124,7 @@ Continue to use this token until you begin to recieve 401 responses from the API @subsection api_auth_o OAuth 2.0 -TGS4 supports OAuth 2.0 with select providers for authentication. +TGS supports OAuth 2.0 with select providers for authentication. The flow for this is as follows: diff --git a/docs/Architecture.dox b/docs/Architecture.dox index 6c5bdeea1d..136a3f27c2 100644 --- a/docs/Architecture.dox +++ b/docs/Architecture.dox @@ -7,7 +7,7 @@ @section arch_intro Introduction -This is meant to be a brief overview of the TGS4 architecture to give new coders direction on where to code and the curious some insight to their questions. Given that this document is seperate from the authorative code it may fall out of date. For clairity, please contact project maintainers. +This is meant to be a brief overview of the TGS architecture to give new coders direction on where to code and the curious some insight to their questions. Given that this document is seperate from the authorative code it may fall out of date. For clairity, please contact project maintainers. @section arch_hwatchdog Host Watchdog @@ -29,7 +29,7 @@ The @ref Tgstation.Server.Host.Core.Application class has two methods called by - Respond with 503 if the application is still starting or shutting down - Authenticate the JWT in Authentication header if present and run @ref Tgstation.Server.Host.Controllers.ApiController on success - Catch database exceptions and convert to 409 responses with the exception's @ref Tgstation.Server.Api.Models.ErrorMessage -- Check @ref Tgstation.Server.Host.Controllers for correct controller and run the action and use it's response. +- Check @ref Tgstation.Server.Host.Controllers for correct controller and run the action and use it's response. - If not properly authenticated beforehand and action has a @ref Tgstation.Server.Host.Controllers.TgsAuthorizeAttribute return 401 - If not properly authorized beforehand according to the parameters of the action's @ref Tgstation.Server.Host.Controllers.TgsAuthorizeAttribute (if present) return 403 - If requested action does not exist return 404 @@ -98,9 +98,9 @@ The compilation process is a distinct series of steps: 14. Symlink all `GameStaticFiles` to both the A and B directories 15. Commit the @ref Tgstation.Server.Host.Models.CompileJob to the database -If any of the above steps fail, the target directory is deleted and the deployment is considered a bust. If all went well, after the @ref Tgstation.Server.Host.Models.Job completes the new CompileJob is loaded into the instance's @ref Tgstation.Server.Host.Components.Deployment.IDmbFactory . +If any of the above steps fail, the target directory is deleted and the deployment is considered a bust. If all went well, after the @ref Tgstation.Server.Host.Models.Job completes the new CompileJob is loaded into the instance's @ref Tgstation.Server.Host.Components.Deployment.IDmbFactory . -The DmbFactory is where the @ref arch_watchdog gets the @ref Tgstation.Server.Host.Components.Deployment.IDmbProvider instances to run. Each CompileJob loaded into it is given a lock count. The latest CompileJob holds 1 lock and every DreamDaemon instance running that CompileJob holds another. Loading a new CompileJob releases the initial lock, and when all other locks are released the CompileJob's directory is deleted. Any directories in the `Game` folder not in use are also deleted when the Instance starts. +The DmbFactory is where the @ref arch_watchdog gets the @ref Tgstation.Server.Host.Components.Deployment.IDmbProvider instances to run. Each CompileJob loaded into it is given a lock count. The latest CompileJob holds 1 lock and every DreamDaemon instance running that CompileJob holds another. Loading a new CompileJob releases the initial lock, and when all other locks are released the CompileJob's directory is deleted. Any directories in the `Game` folder not in use are also deleted when the Instance starts. @section arch_chat Chat Bot System @@ -118,7 +118,7 @@ The @ref Tgstation.Server.Host.Components.StaticFiles.IConfiguration object is a @section arch_watchdog Watchdog -This is the core of tgstation-server, the component that starts, monitors, and updates DreamDaemon. +This is the core of tgstation-server, the component that starts, monitors, and updates DreamDaemon. At it's core, the watchdog operates using a hot/cold server setup. At any given moment there are two DreamDaemon instances running, only one of which players can see. If anything bad happens to that server, it is killed and the inactive server has its port changed to catch all the connections. If any changes need to be made to the configuration (port, security, compile job), the inactive server is killed and immediately relaunched with the new configuration. Whenever the active server reboots, the two servers change ports so as to minimize load times. @@ -128,9 +128,9 @@ That's a high level view of things, now let's get to the nitty gritty. @subsection arch_wd_launch Launch -First the most recent @ref Tgstation.Server.Host.Components.Deployment.IDmbProvider is retrieved from the @ref Tgstation.Server.Host.Components.Deployment.IDmbFactory twice, adding 2 locks. +First the most recent @ref Tgstation.Server.Host.Components.Deployment.IDmbProvider is retrieved from the @ref Tgstation.Server.Host.Components.Deployment.IDmbFactory twice, adding 2 locks. -This is used to launch a @ref Tgstation.Server.Host.Components.Watchdog.ISessionController via the watchdog's @ref Tgstation.Server.Host.Components.Watchdog.ISessionControllerFactory in the `A` directory of dmb providers @ref Tgstation.Server.Host.Models.CompileJob . This will be designated the `Alpha` server. +This is used to launch a @ref Tgstation.Server.Host.Components.Watchdog.ISessionController via the watchdog's @ref Tgstation.Server.Host.Components.Watchdog.ISessionControllerFactory in the `A` directory of dmb providers @ref Tgstation.Server.Host.Models.CompileJob . This will be designated the `Alpha` server. Whenever DreamDaemon is launched by any part of the watchdog, we try to elevate its process priority to the equivalent of Windows' `Above Normal` @@ -140,7 +140,7 @@ If the watchdog ever enters a failure state it can't recover from, it kills both @subsection arch_wd_monitor The Monitor -The monitor is responsible for handling every @ref Tgstation.Server.Host.Components.Watchdog.MonitorActivationReason . It sleeps until one of these things happen. If multiple things happen at once, they are processed in their order of declaration. +The monitor is responsible for handling every @ref Tgstation.Server.Host.Components.Watchdog.MonitorActivationReason . It sleeps until one of these things happen. If multiple things happen at once, they are processed in their order of declaration. The monitor maintains a @ref Tgstation.Server.Host.Components.Watchdog.MonitorState which helps it make descisions on how to handle activation reasons. The @ref Tgstation.Server.Host.Components.Watchdog.MonitorState.NextAction determines how multiple simultaneous events are handled in succession. diff --git a/docs/Features.dox b/docs/Features.dox index 02e37f1231..8958e28807 100644 --- a/docs/Features.dox +++ b/docs/Features.dox @@ -3,7 +3,7 @@ @tableofcontents -@section new_features New in V4 +@section new_features New since TGS3 (Rewrite) - Agnostic HTTP API: The replaces the WCF service calls used in TGS3. This helps avoid Windows vendor lock-in and get away from the SOAP API that literally no one understood (not even me). With it, it's much easier to expose TGS to the internet, all you need is a HTTPS reverse proxy in front of it. A rundown of the new API exists here: https://tgstation.github.io/tgstation-server/api.html. - Granular Access Controls: Windows users are no longer (required to be) the basis for authentication to the server. We now have database-backed users as a login option. These use a combined Basic/JWT authentication scheme with industry standard password hashing and salting. Users are fully customizable and can be given granular access to every bit of the server via the new permissions system. From changing the BYOND version, to test merging a PR, to restarting the server, every action may now be granted or revoked on a per user basis. @@ -11,8 +11,8 @@ - Proper Long Running Operation Support: Server actions take a long time, from a git pull to a DreamMaker compile. TGS now internally allows for them to be run in parallel with each other and provides an audit record via the database. This is an improvement over the old system where connections had to be held open for the duration of operations. - Database Backend: TGS requires an SQL database to operate. This allows for much better concurrency and is just overall much cleaner than the old single json file storage blob per instance. - Limitation: There is a one-to-one relationship with a TGS server and a database. **DO NOT SHARE TGS DATABASES**. -- Linux/Docker Support: TGS4 is Linux and docker compatible. (Note this does not mean that rust-g and BSQL work out of the box, they must be compiled using event scripts like PreCompile.sh). - - Limitation: TGS4 has a dependency on the native library libgit2 which is known to cause issues on Linux. The binaries distributed with TGS are kept up to date with the upstream repository, but out of the box Linux support can't be assured in every environment. Docker is guaranteed to always work, however. See the repository for the distributed binary here: https://github.com/libgit2/libgit2sharp.nativebinaries. +- Linux/Docker Support: TGS is Linux and docker compatible. (Note this does not mean that rust-g and BSQL work out of the box, they must be compiled using event scripts like PreCompile.sh). + - Limitation: TGS has a dependency on the native library libgit2 which is known to cause issues on Linux. The binaries distributed with TGS are kept up to date with the upstream repository, but out of the box Linux support can't be assured in every environment. Docker is guaranteed to always work, however. See the repository for the distributed binary here: https://github.com/libgit2/libgit2sharp.nativebinaries. - Limitation: System based logins are not supported on Linux. https://github.com/tgstation/tgstation-server/issues/709 - Incredibly Detailed Logging: Various log levels exist now (Trace/Debug/Info/Warning/Error/Critical) and are sanely output to a rolling file on the host. Significant improvement over having to use the Windows event viewer with TGS3. Until such a point where bugs stop copping up I'd recommend Trace logging for the main log level. - Historical Deployment Data: Every time code is compiled the following data is logged and stored. @@ -28,8 +28,9 @@ - Watchdog Heartbeats: An interval in seconds can now be set at which TGS will send /world/Topic() packets to DreamDaemon. If four of these are missed, the server will be rebooted. No more endless @Key Holder pings in discord (and I can finally unmute the /tg/ guild)! This feature can be disabled. - Better DMAPI: No longer requires injecting a .NET runtime .dll into the DreamDaemon process. DD -> TGS communication is now handled securely via BYOND's native /world/Export() API ("But Cyberboss, BYOND only supports GET requests." Who said anything about respecting HTTP standards when dealing with BYOND?). - Safe/Ultrasafe Security Support: Thanks to the new DMAPI, the ultrasafe and safe security levels may be used without running into BYOND's limitations. But no one really cares... -- Self Upgrading: To upgrade TGS3 you needed to download and run the installer. This was pretty seamless, but it's now even better in V4 as the command to upgrade can be given straight to the API. At that point the server will handle downloading the update, detaching running DreamDaemon instances, restarting with the new version, and reattaching to them. Easier than ever patch delivery. -- *Gasp* TESTING: TGS4 currently has over 60% code coverage in automated unit and full stack integration tests. I aim to have that number ever increasing to prevent trivial mistakes. Big improvement over V3 which had... literally none... +- Private/Invisible visibility Support: Stored per instance. +- Self Upgrading: To upgrade TGS3 you needed to download and run the installer. This was pretty seamless, but it's now even better in versions >=4 as the command to upgrade can be given straight to the API. At that point the server will handle downloading the update, detaching running DreamDaemon instances, restarting with the new version, and reattaching to them. Easier than ever patch delivery. +- *Gasp* TESTING: TGS currently has over 60% code coverage in automated unit and full stack integration tests. I aim to have that number ever increasing to prevent trivial mistakes. Big improvement over V3 which had... literally none... Along with these features, nearly every single V3 feature has been included and possibly improved in some fashion. This includes stuff like Windows accounts for logins, and using ACLs for static file handling. The following exceptions exist but are planned for future updates: - Process memory/CPU diagnostic data is not generated: https://github.com/tgstation/tgstation-server/issues/611 diff --git a/src/DMAPI/tgs/README.md b/src/DMAPI/tgs/README.md index 445cee41f5..6319028d81 100644 --- a/src/DMAPI/tgs/README.md +++ b/src/DMAPI/tgs/README.md @@ -7,7 +7,7 @@ This folder should be placed on it's own inside a codebase that wishes to use th - The other versioned folders contain code for the different DMAPI versions. - [v3210](./v3210) contains the final TGS3 API. - [v4](./v4) is the legacy DMAPI 4 (Used in TGS 4.0.X versions). - - [v5](./v5) is the current DMAPI version used by TGS4 >=4.1. + - [v5](./v5) is the current DMAPI version used by TGS >=4.1. - [LICENSE](./LICENSE) is the MIT license for the DMAPI. APIs communicate with TGS in two ways. All versions implement TGS -> DM communication using /world/Topic. DM -> TGS communication, called the bridge method, is different for each version. diff --git a/src/DMAPI/tgs/v5/api.dm b/src/DMAPI/tgs/v5/api.dm index 704ff873c0..572944ed40 100644 --- a/src/DMAPI/tgs/v5/api.dm +++ b/src/DMAPI/tgs/v5/api.dm @@ -269,7 +269,7 @@ if(!result) return - //okay so the standard TGS4 proceedure is: right before rebooting change the port to whatever was sent to us in the above json's data parameter + //okay so the standard TGS proceedure is: right before rebooting change the port to whatever was sent to us in the above json's data parameter var/port = result[DMAPI5_BRIDGE_RESPONSE_NEW_PORT] if(!isnum(port)) diff --git a/src/Tgstation.Server.Api/Models/ErrorCode.cs b/src/Tgstation.Server.Api/Models/ErrorCode.cs index 323d769038..f861b24cbb 100644 --- a/src/Tgstation.Server.Api/Models/ErrorCode.cs +++ b/src/Tgstation.Server.Api/Models/ErrorCode.cs @@ -58,7 +58,7 @@ namespace Tgstation.Server.Api.Models MissingHostWatchdog, /// - /// Attempted to change to a major version TGS4. + /// Attempted to change to a major version less than 4. /// [Description("Cannot downgrade to tgstation-server major version <4!")] CannotChangeServerSuite, diff --git a/src/Tgstation.Server.Host.Service/Program.cs b/src/Tgstation.Server.Host.Service/Program.cs index b21c71b5c5..15a0df1de3 100644 --- a/src/Tgstation.Server.Host.Service/Program.cs +++ b/src/Tgstation.Server.Host.Service/Program.cs @@ -124,8 +124,8 @@ namespace Tgstation.Server.Host.Service processInstaller.Account = ServiceAccount.LocalSystem; installer.Context = new InstallContext("tgs-4-install.log", new string[] { String.Format(CultureInfo.InvariantCulture, "/assemblypath={0}", Assembly.GetEntryAssembly().Location) }); - installer.Description = "/tg/station 13 server v4 running as a windows service"; - installer.DisplayName = "/tg/station server 4"; + installer.Description = "/tg/station 13 server running as a windows service"; + installer.DisplayName = "/tg/station server"; installer.DelayedAutoStart = true; installer.StartType = ServiceStartMode.Automatic; installer.ServicesDependedOn = new string[] { "Tcpip", "Dhcp", "Dnscache" }; diff --git a/src/Tgstation.Server.Host/Controllers/HomeController.cs b/src/Tgstation.Server.Host/Controllers/HomeController.cs index 205c20620d..93cbd26941 100644 --- a/src/Tgstation.Server.Host/Controllers/HomeController.cs +++ b/src/Tgstation.Server.Host/Controllers/HomeController.cs @@ -218,7 +218,7 @@ namespace Tgstation.Server.Host.Controllers { if (ApiHeaders == null) { - Response.Headers.Add(HeaderNames.WWWAuthenticate, new StringValues("basic realm=\"Create TGS4 bearer token\"")); + Response.Headers.Add(HeaderNames.WWWAuthenticate, new StringValues("basic realm=\"Create TGS bearer token\"")); return HeadersIssue(false); } diff --git a/src/Tgstation.Server.Host/Core/Application.cs b/src/Tgstation.Server.Host/Core/Application.cs index 087cb51702..f79766ff24 100644 --- a/src/Tgstation.Server.Host/Core/Application.cs +++ b/src/Tgstation.Server.Host/Core/Application.cs @@ -430,7 +430,7 @@ namespace Tgstation.Server.Host.Core if (generalConfiguration.HostApiDocumentation) { applicationBuilder.UseSwagger(); - applicationBuilder.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "TGS API V4")); + applicationBuilder.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "TGS API")); logger.LogTrace("Swagger API generation enabled"); } diff --git a/src/Tgstation.Server.Host/Database/DatabaseContext.cs b/src/Tgstation.Server.Host/Database/DatabaseContext.cs index 3b9a4194bb..ae46944ec6 100644 --- a/src/Tgstation.Server.Host/Database/DatabaseContext.cs +++ b/src/Tgstation.Server.Host/Database/DatabaseContext.cs @@ -409,7 +409,7 @@ namespace Tgstation.Server.Host.Database if (targetVersion == null) throw new ArgumentNullException(nameof(targetVersion)); if (targetVersion < new Version(4, 0)) - throw new ArgumentOutOfRangeException(nameof(targetVersion), targetVersion, "Not a valid V4 version!"); + throw new ArgumentOutOfRangeException(nameof(targetVersion), targetVersion, "Cannot migrate below version 4.0.0!"); if (currentDatabaseType == DatabaseType.PostgresSql && targetVersion < new Version(4, 3, 0)) throw new NotSupportedException("Cannot migrate below version 4.3.0 with PostgresSql!"); diff --git a/src/Tgstation.Server.Host/Extensions/ServiceCollectionExtensions.cs b/src/Tgstation.Server.Host/Extensions/ServiceCollectionExtensions.cs index bc6ed50c5a..1ae2809a75 100644 --- a/src/Tgstation.Server.Host/Extensions/ServiceCollectionExtensions.cs +++ b/src/Tgstation.Server.Host/Extensions/ServiceCollectionExtensions.cs @@ -104,7 +104,7 @@ namespace Tgstation.Server.Host.Extensions CustomFormatter = new EcsTextFormatter(), AutoRegisterTemplate = true, AutoRegisterTemplateVersion = AutoRegisterTemplateVersion.ESv7, - IndexFormat = "tgs4-logs", + IndexFormat = "tgs-logs", }); } } diff --git a/src/Tgstation.Server.Host/Setup/SetupWizard.cs b/src/Tgstation.Server.Host/Setup/SetupWizard.cs index a697f8801f..0690e02050 100644 --- a/src/Tgstation.Server.Host/Setup/SetupWizard.cs +++ b/src/Tgstation.Server.Host/Setup/SetupWizard.cs @@ -346,7 +346,7 @@ namespace Tgstation.Server.Host.Setup cancellationToken) .ConfigureAwait(false); await console.WriteAsync( - "This means that you may not be able to update to the next minor version of TGS4 without a clean re-installation!", + "This means that you may not be able to update to the next minor version of TGS without a clean re-installation!", true, cancellationToken) .ConfigureAwait(false); 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 e2d527c43c..d0dffe4f4f 100644 --- a/tests/Tgstation.Server.Host.Tests/Components/Chat/Providers/TestDiscordProvider.cs +++ b/tests/Tgstation.Server.Host.Tests/Components/Chat/Providers/TestDiscordProvider.cs @@ -22,7 +22,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers.Tests [TestInitialize] public void Initialize() { - var actualToken = Environment.GetEnvironmentVariable("TGS4_TEST_DISCORD_TOKEN"); + var actualToken = Environment.GetEnvironmentVariable("TGS_TEST_DISCORD_TOKEN"); if (!String.IsNullOrWhiteSpace(actualToken)) testToken1 = new ChatBot { @@ -45,7 +45,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers.Tests public async Task TestConstructionAndDisposal() { if (testToken1 == null) - Assert.Inconclusive("Required environment variable TGS4_TEST_DISCORD_TOKEN isn't set!"); + Assert.Inconclusive("Required environment variable TGS_TEST_DISCORD_TOKEN isn't set!"); Assert.ThrowsException(() => new DiscordProvider(null, null, null, null)); Assert.ThrowsException(() => new DiscordProvider(mockJobManager, null, null, null)); @@ -77,7 +77,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers.Tests public async Task TestConnectAndDisconnect() { if (testToken1 == null) - Assert.Inconclusive("Required environment variable TGS4_TEST_DISCORD_TOKEN isn't set!"); + Assert.Inconclusive("Required environment variable TGS_TEST_DISCORD_TOKEN isn't set!"); var mockLogger = new Mock>(); await using var provider = new DiscordProvider(mockJobManager, Mock.Of(), mockLogger.Object, testToken1); diff --git a/tests/Tgstation.Server.Tests/AdministrationTest.cs b/tests/Tgstation.Server.Tests/AdministrationTest.cs index a3eb17075b..6b9581534a 100644 --- a/tests/Tgstation.Server.Tests/AdministrationTest.cs +++ b/tests/Tgstation.Server.Tests/AdministrationTest.cs @@ -60,9 +60,9 @@ namespace Tgstation.Server.Tests } catch (RateLimitException) { - if (String.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("TGS4_TEST_GITHUB_TOKEN"))) + if (String.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("TGS_TEST_GITHUB_TOKEN"))) { - Assert.Inconclusive("GitHub rate limit hit while testing administration endpoint. Set environment variable TGS4_TEST_GITHUB_TOKEN to fix this!"); + Assert.Inconclusive("GitHub rate limit hit while testing administration endpoint. Set environment variable TGS_TEST_GITHUB_TOKEN to fix this!"); } // CI fails all the time b/c of this, ignore it diff --git a/tests/Tgstation.Server.Tests/Instance/ChatTest.cs b/tests/Tgstation.Server.Tests/Instance/ChatTest.cs index 8c1baf40df..71a0f2ff03 100644 --- a/tests/Tgstation.Server.Tests/Instance/ChatTest.cs +++ b/tests/Tgstation.Server.Tests/Instance/ChatTest.cs @@ -37,9 +37,9 @@ namespace Tgstation.Server.Tests.Instance { var firstBotReq = new ChatBotCreateRequest { - ConnectionString = Environment.GetEnvironmentVariable("TGS4_TEST_IRC_CONNECTION_STRING"), + ConnectionString = Environment.GetEnvironmentVariable("TGS_TEST_IRC_CONNECTION_STRING"), Enabled = false, - Name = "tgs4_integration_test", + Name = "tgs_integration_test", Provider = ChatProvider.Irc, ReconnectionInterval = 1, ChannelLimit = 1 @@ -68,7 +68,7 @@ namespace Tgstation.Server.Tests.Instance Assert.AreEqual(true, updatedBot.Enabled); - var channelId = Environment.GetEnvironmentVariable("TGS4_TEST_IRC_CHANNEL"); ; + var channelId = Environment.GetEnvironmentVariable("TGS_TEST_IRC_CHANNEL"); ; updatedBot = await chatClient.Update(new ChatBotUpdateRequest { @@ -104,7 +104,7 @@ namespace Tgstation.Server.Tests.Instance ConnectionString = new DiscordConnectionStringBuilder { - BotToken = Environment.GetEnvironmentVariable("TGS4_TEST_DISCORD_TOKEN"), + BotToken = Environment.GetEnvironmentVariable("TGS_TEST_DISCORD_TOKEN"), DMOutputDisplay = DiscordDMOutputDisplayType.OnError }.ToString(), Enabled = false, @@ -137,7 +137,7 @@ namespace Tgstation.Server.Tests.Instance Assert.AreEqual(true, updatedBot.Enabled); - var channelId = UInt64.Parse(Environment.GetEnvironmentVariable("TGS4_TEST_DISCORD_CHANNEL")); + var channelId = UInt64.Parse(Environment.GetEnvironmentVariable("TGS_TEST_DISCORD_CHANNEL")); firstBot.Channels = new List { new ChatChannel diff --git a/tests/Tgstation.Server.Tests/Instance/RepositoryTest.cs b/tests/Tgstation.Server.Tests/Instance/RepositoryTest.cs index 0f9ebcadce..ff2e1d8dea 100644 --- a/tests/Tgstation.Server.Tests/Instance/RepositoryTest.cs +++ b/tests/Tgstation.Server.Tests/Instance/RepositoryTest.cs @@ -25,7 +25,7 @@ namespace Tgstation.Server.Tests.Instance public async Task RunPreWatchdog(CancellationToken cancellationToken) { - const string TestRefEnvVar = "TGS4_GITHUB_REF"; + const string TestRefEnvVar = "TGS_GITHUB_REF"; var envVar = Environment.GetEnvironmentVariable(TestRefEnvVar); string workingBranch = null; if (!String.IsNullOrWhiteSpace(envVar)) @@ -108,12 +108,12 @@ namespace Tgstation.Server.Tests.Instance updated = await Checkout(new RepositoryUpdateRequest { CheckoutSha = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" }, true, false, cancellationToken); // Fake ref - updated = await Checkout(new RepositoryUpdateRequest { Reference = "Tgs4IntegrationTestFakeBranchNeverNameABranchThis" }, true, true, cancellationToken); + updated = await Checkout(new RepositoryUpdateRequest { Reference = "TgsIntegrationTestFakeBranchNeverNameABranchThis" }, true, true, cancellationToken); // Back updated = await Checkout(new RepositoryUpdateRequest { Reference = workingBranch }, false, true, cancellationToken); - var testPRString = Environment.GetEnvironmentVariable("TGS4_TEST_PULL_REQUEST_NUMBER"); + var testPRString = Environment.GetEnvironmentVariable("TGS_TEST_PULL_REQUEST_NUMBER"); if (String.IsNullOrWhiteSpace(testPRString)) testPRString = Environment.GetEnvironmentVariable("APPVEYOR_PULL_REQUEST_NUMBER"); if (String.IsNullOrWhiteSpace(testPRString)) diff --git a/tests/Tgstation.Server.Tests/IntegrationTest.cs b/tests/Tgstation.Server.Tests/IntegrationTest.cs index 1aa222d0bf..54c7bee33e 100644 --- a/tests/Tgstation.Server.Tests/IntegrationTest.cs +++ b/tests/Tgstation.Server.Tests/IntegrationTest.cs @@ -96,7 +96,7 @@ namespace Tgstation.Server.Tests } catch (RateLimitException ex) { - if (String.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("TGS4_TEST_GITHUB_TOKEN"))) + if (String.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("TGS_TEST_GITHUB_TOKEN"))) throw; Assert.Inconclusive("GitHub rate limit hit: {0}", ex); @@ -184,7 +184,7 @@ namespace Tgstation.Server.Tests } catch (RateLimitException ex) { - if (String.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("TGS4_TEST_GITHUB_TOKEN"))) + if (String.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("TGS_TEST_GITHUB_TOKEN"))) throw; Assert.Inconclusive("GitHub rate limit hit: {0}", ex); @@ -393,7 +393,7 @@ namespace Tgstation.Server.Tests } catch (RateLimitException ex) { - if (String.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("TGS4_TEST_GITHUB_TOKEN"))) + if (String.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("TGS_TEST_GITHUB_TOKEN"))) throw; Assert.Inconclusive("GitHub rate limit hit: {0}", ex); @@ -590,7 +590,7 @@ namespace Tgstation.Server.Tests } catch (RateLimitException ex) { - if (String.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("TGS4_TEST_GITHUB_TOKEN"))) + if (String.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("TGS_TEST_GITHUB_TOKEN"))) throw; Assert.Inconclusive("GitHub rate limit hit: {0}", ex); @@ -648,14 +648,14 @@ namespace Tgstation.Server.Tests [TestMethod] public async Task TestDownMigrations() { - var connectionString = Environment.GetEnvironmentVariable("TGS4_TEST_CONNECTION_STRING"); + var connectionString = Environment.GetEnvironmentVariable("TGS_TEST_CONNECTION_STRING"); if (String.IsNullOrEmpty(connectionString)) - Assert.Inconclusive("No connection string configured in env var TGS4_TEST_CONNECTION_STRING!"); + Assert.Inconclusive("No connection string configured in env var TGS_TEST_CONNECTION_STRING!"); - var databaseTypeString = Environment.GetEnvironmentVariable("TGS4_TEST_DATABASE_TYPE"); + var databaseTypeString = Environment.GetEnvironmentVariable("TGS_TEST_DATABASE_TYPE"); if (!Enum.TryParse(databaseTypeString, out var databaseType)) - Assert.Inconclusive("No/invalid database type configured in env var TGS4_TEST_DATABASE_TYPE!"); + Assert.Inconclusive("No/invalid database type configured in env var TGS_TEST_DATABASE_TYPE!"); string migrationName = null; DatabaseContext CreateContext() diff --git a/tests/Tgstation.Server.Tests/TestingServer.cs b/tests/Tgstation.Server.Tests/TestingServer.cs index 9fdbe31d62..878527bb15 100644 --- a/tests/Tgstation.Server.Tests/TestingServer.cs +++ b/tests/Tgstation.Server.Tests/TestingServer.cs @@ -35,10 +35,10 @@ namespace Tgstation.Server.Tests public TestingServer(SwarmConfiguration swarmConfiguration, bool enableOAuth, ushort port = 5010) { - Directory = Environment.GetEnvironmentVariable("TGS4_TEST_TEMP_DIRECTORY"); + Directory = Environment.GetEnvironmentVariable("TGS_TEST_TEMP_DIRECTORY"); if (String.IsNullOrWhiteSpace(Directory)) { - Directory = Path.Combine(Path.GetTempPath(), "TGS4_INTEGRATION_TEST"); + Directory = Path.Combine(Path.GetTempPath(), "TGS_INTEGRATION_TEST"); if (System.IO.Directory.Exists(Directory) && swarmConfiguration == null) try { @@ -55,16 +55,16 @@ namespace Tgstation.Server.Tests //so we need a db //we have to rely on env vars - DatabaseType = Environment.GetEnvironmentVariable("TGS4_TEST_DATABASE_TYPE"); - var connectionString = Environment.GetEnvironmentVariable("TGS4_TEST_CONNECTION_STRING"); - var gitHubAccessToken = Environment.GetEnvironmentVariable("TGS4_TEST_GITHUB_TOKEN"); - var dumpOpenAPISpecPathEnvVar = Environment.GetEnvironmentVariable("TGS4_TEST_DUMP_API_SPEC"); + DatabaseType = Environment.GetEnvironmentVariable("TGS_TEST_DATABASE_TYPE"); + var connectionString = Environment.GetEnvironmentVariable("TGS_TEST_CONNECTION_STRING"); + var gitHubAccessToken = Environment.GetEnvironmentVariable("TGS_TEST_GITHUB_TOKEN"); + var dumpOpenAPISpecPathEnvVar = Environment.GetEnvironmentVariable("TGS_TEST_DUMP_API_SPEC"); if (String.IsNullOrEmpty(DatabaseType)) - Assert.Inconclusive("No database type configured in env var TGS4_TEST_DATABASE_TYPE!"); + Assert.Inconclusive("No database type configured in env var TGS_TEST_DATABASE_TYPE!"); if (String.IsNullOrEmpty(connectionString)) - Assert.Inconclusive("No connection string configured in env var TGS4_TEST_CONNECTION_STRING!"); + Assert.Inconclusive("No connection string configured in env var TGS_TEST_CONNECTION_STRING!"); if (String.IsNullOrEmpty(gitHubAccessToken)) Console.WriteLine("WARNING: No GitHub access token configured, test may fail due to rate limits!"); diff --git a/tools/ReleaseNotes/Program.cs b/tools/ReleaseNotes/Program.cs index d431b2568e..7654e0bd5c 100644 --- a/tools/ReleaseNotes/Program.cs +++ b/tools/ReleaseNotes/Program.cs @@ -34,7 +34,7 @@ namespace ReleaseNotes var doNotCloseMilestone = args.Length > 1 && args[1].ToUpperInvariant() == "--NO-CLOSE"; - const string ReleaseNotesEnvVar = "TGS4_RELEASE_NOTES_TOKEN"; + const string ReleaseNotesEnvVar = "TGS_RELEASE_NOTES_TOKEN"; var githubToken = Environment.GetEnvironmentVariable(ReleaseNotesEnvVar); if (String.IsNullOrWhiteSpace(githubToken) && !doNotCloseMilestone) { From 83568dd18b8e2538d60246c4414e69547a478ef7 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Wed, 8 Sep 2021 18:07:38 -0400 Subject: [PATCH 5/7] Version bump to 4.15.0 --- build/Version.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/Version.props b/build/Version.props index 82a4caba04..9716246d2b 100644 --- a/build/Version.props +++ b/build/Version.props @@ -3,7 +3,7 @@ - 4.14.2 + 4.15.0 4.0.0 9.2.0 9.2.0 From 5251ccc297a393b0a633bb3c6c199219fcb294ac Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Sun, 12 Sep 2021 15:35:07 -0400 Subject: [PATCH 6/7] C# 7 compatibility --- src/Tgstation.Server.Host/Components/Deployment/DreamMaker.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Tgstation.Server.Host/Components/Deployment/DreamMaker.cs b/src/Tgstation.Server.Host/Components/Deployment/DreamMaker.cs index 408d27f61e..790dee9cc9 100644 --- a/src/Tgstation.Server.Host/Components/Deployment/DreamMaker.cs +++ b/src/Tgstation.Server.Host/Components/Deployment/DreamMaker.cs @@ -707,7 +707,7 @@ namespace Tgstation.Server.Host.Components.Deployment /// A representing the running operation. async Task ProgressTask(JobProgressReporter progressReporter, TimeSpan? estimatedDuration, CancellationToken cancellationToken) { - progressReporter(currentStage, estimatedDuration.HasValue ? 0 : null); + progressReporter(currentStage, estimatedDuration.HasValue ? (int?)0 : null); var sleepInterval = estimatedDuration.HasValue ? estimatedDuration.Value / 100 : TimeSpan.FromMilliseconds(250); logger.LogDebug("Compile is expected to take: {0}", estimatedDuration); @@ -716,7 +716,7 @@ namespace Tgstation.Server.Host.Components.Deployment for (var iteration = 0; iteration < (estimatedDuration.HasValue ? 99 : Int32.MaxValue); ++iteration) { await Task.Delay(sleepInterval, cancellationToken).ConfigureAwait(false); - progressReporter(currentStage, estimatedDuration.HasValue ? iteration + 1 : null); + progressReporter(currentStage, estimatedDuration.HasValue ? (int?)(iteration + 1) : null); } } catch (OperationCanceledException) From a6deecdceddd3d0bf399cd0cc20bf9d110b2fc1f Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Mon, 13 Sep 2021 11:31:35 -0400 Subject: [PATCH 7/7] Clone/restore webpanel during ResolveAssemblyReferences - This lets it happen while VS is loading the project --- src/Tgstation.Server.Host/Tgstation.Server.Host.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj b/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj index 3424ea6dbc..fe8a7385ed 100644 --- a/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj +++ b/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj @@ -1,4 +1,4 @@ - + @@ -26,7 +26,7 @@ ..\.. - +