From 0fd5110ebe10f6b577282100a8857dcb19bdf734 Mon Sep 17 00:00:00 2001 From: Cyberboss Date: Tue, 23 Oct 2018 15:37:05 -0400 Subject: [PATCH 01/51] Minor code simplification --- .../Components/Watchdog/Watchdog.cs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs b/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs index 0302b24fce..a70fddc11f 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs @@ -453,20 +453,13 @@ namespace Tgstation.Server.Host.Components.Watchdog //servers now swapped - //enable this now - monitorState.ActiveServer.ClosePortOnReboot = true; + //enable this now if inactive server is not still valid + monitorState.ActiveServer.ClosePortOnReboot = restartOnceSwapped; if (!restartOnceSwapped) - { - //inactive server still valid - - //disable this - monitorState.InactiveServer.ClosePortOnReboot = false; - //now try to reopen it on the private port //failing that, just reboot it restartOnceSwapped = !await monitorState.InactiveServer.SetPort(ActiveLaunchParameters.SecondaryPort.Value, cancellationToken).ConfigureAwait(false); - } //break either way because any issues past this point would be solved by the reboot if (restartOnceSwapped) From 7fc615b90cdc3003a552bffacb86f07833f8958e Mon Sep 17 00:00:00 2001 From: Cyberboss Date: Tue, 23 Oct 2018 15:41:00 -0400 Subject: [PATCH 02/51] Add additional session controller logging --- .../Components/Watchdog/SessionController.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Tgstation.Server.Host/Components/Watchdog/SessionController.cs b/src/Tgstation.Server.Host/Components/Watchdog/SessionController.cs index dd5d2b7ebb..445cf4649a 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/SessionController.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/SessionController.cs @@ -220,6 +220,8 @@ namespace Tgstation.Server.Host.Components.Watchdog return result; }; LaunchResult = GetLaunchResult(); + + logger.LogDebug("Created session controller. Primary: {0}, CommsKey: {1}, Port: {2}", IsPrimary, reattachInformation.AccessIdentifier, Port); } /// From 847f21e53150713d4f73ab03bb1c7f97e373e8b8 Mon Sep 17 00:00:00 2001 From: Cyberboss Date: Tue, 23 Oct 2018 15:49:29 -0400 Subject: [PATCH 03/51] Add RawJson field to CommCommand --- .../Components/Interop/CommCommand.cs | 7 ++++++- .../Components/Interop/CommContext.cs | 3 ++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Tgstation.Server.Host/Components/Interop/CommCommand.cs b/src/Tgstation.Server.Host/Components/Interop/CommCommand.cs index cc9ab8571a..7145d35aae 100644 --- a/src/Tgstation.Server.Host/Components/Interop/CommCommand.cs +++ b/src/Tgstation.Server.Host/Components/Interop/CommCommand.cs @@ -8,8 +8,13 @@ namespace Tgstation.Server.Host.Components.Interop sealed class CommCommand { /// - /// The raw JSON decond of the + /// The dictionary of the /// public IReadOnlyDictionary Parameters { get; set; } + + /// + /// The raw JSON of the + /// + public string RawJson { get; set; } } } \ No newline at end of file diff --git a/src/Tgstation.Server.Host/Components/Interop/CommContext.cs b/src/Tgstation.Server.Host/Components/Interop/CommContext.cs index 839086a3bb..1b3c95a6c7 100644 --- a/src/Tgstation.Server.Host/Components/Interop/CommContext.cs +++ b/src/Tgstation.Server.Host/Components/Interop/CommContext.cs @@ -109,7 +109,8 @@ namespace Tgstation.Server.Host.Components.Interop { command = new CommCommand { - Parameters = JsonConvert.DeserializeObject>(file) + Parameters = JsonConvert.DeserializeObject>(file), + RawJson = file }; } catch (JsonSerializationException ex) From bf292ef560fd503dc5d51da1f09e35de333700ff Mon Sep 17 00:00:00 2001 From: Cyberboss Date: Tue, 23 Oct 2018 15:50:01 -0400 Subject: [PATCH 04/51] Fix CommContext not catching the right JsonException --- src/Tgstation.Server.Host/Components/Interop/CommContext.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Tgstation.Server.Host/Components/Interop/CommContext.cs b/src/Tgstation.Server.Host/Components/Interop/CommContext.cs index 1b3c95a6c7..f9c5208d88 100644 --- a/src/Tgstation.Server.Host/Components/Interop/CommContext.cs +++ b/src/Tgstation.Server.Host/Components/Interop/CommContext.cs @@ -113,7 +113,7 @@ namespace Tgstation.Server.Host.Components.Interop RawJson = file }; } - catch (JsonSerializationException ex) + catch (JsonException ex) { //file not fully written yet logger.LogDebug("Suppressing json convert exception for command file write: {0}", ex); From 64d225e98f38060e5c303dda5420387ad7617a92 Mon Sep 17 00:00:00 2001 From: Cyberboss Date: Tue, 23 Oct 2018 16:09:29 -0400 Subject: [PATCH 05/51] Implement tgs_chat_send --- .../Components/Watchdog/SessionController.cs | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/Tgstation.Server.Host/Components/Watchdog/SessionController.cs b/src/Tgstation.Server.Host/Components/Watchdog/SessionController.cs index dd5d2b7ebb..8cd90b5b72 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/SessionController.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/SessionController.cs @@ -1,6 +1,7 @@ using Byond.TopicSender; using Microsoft.Extensions.Logging; using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; using System; using System.Collections.Generic; using System.Globalization; @@ -286,6 +287,25 @@ namespace Tgstation.Server.Host.Components.Watchdog content = new object(); switch (method) { + case Constants.DMCommandChat: + try + { + var message = JsonConvert.DeserializeObject(command.RawJson, new JsonSerializerSettings + { + ContractResolver = new CamelCasePropertyNamesContractResolver() + }); + if (message.ChannelIds == null) + throw new InvalidOperationException("Missing ChannelIds field!"); + if (message.Message == null) + throw new InvalidOperationException("Missing Message field!"); + await chat.SendMessage(message.Message, message.ChannelIds, cancellationToken).ConfigureAwait(false); + } + catch (Exception e) + { + logger.LogDebug("Exception while decoding chat message! Exception: {0}", e); + goto default; + } + break; case Constants.DMCommandServerPrimed: //currently unused, maybe in the future break; From c4ee5dbacb96964411f43fb27b4e2345e3d08abb Mon Sep 17 00:00:00 2001 From: Cyberboss Date: Tue, 23 Oct 2018 16:11:48 -0400 Subject: [PATCH 06/51] Fix V3 revision parsing --- src/DMAPI/tgs/v3210/api.dm | 1 + 1 file changed, 1 insertion(+) diff --git a/src/DMAPI/tgs/v3210/api.dm b/src/DMAPI/tgs/v3210/api.dm index 63bc0beb2b..c536995b04 100644 --- a/src/DMAPI/tgs/v3210/api.dm +++ b/src/DMAPI/tgs/v3210/api.dm @@ -171,6 +171,7 @@ var/datum/tgs_revision_information/ri = new ri.commit = commit ri.origin_commit = originmastercommit + return ri /datum/tgs_api/v3210/EndProcess() sleep(world.tick_lag) //flush the buffers From dfe34a5f526075a797a86d6796ed2601c006dfd2 Mon Sep 17 00:00:00 2001 From: Cyberboss Date: Tue, 23 Oct 2018 16:17:46 -0400 Subject: [PATCH 07/51] Fix tgs_chat_send models --- src/DMAPI/tgs/v4/api.dm | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/DMAPI/tgs/v4/api.dm b/src/DMAPI/tgs/v4/api.dm index 9d74610f9c..dc03791241 100644 --- a/src/DMAPI/tgs/v4/api.dm +++ b/src/DMAPI/tgs/v4/api.dm @@ -269,21 +269,26 @@ for(var/I in channels) var/datum/tgs_chat_channel/channel = I ids += channel.id - message = list("message" = message, "channels" = ids) + message = list("message" = message, "channelIds" = ids) if(intercepted_message_queue) intercepted_message_queue += list(message) else Export(TGS4_COMM_CHAT, message) /datum/tgs_api/v4/ChatTargetedBroadcast(message, admin_only) - message = list("message" = message, "channels" = admin_only ? "admin" : "game") + var/list/channels = list() + for(var/I in ChatChannelInfo()) + var/datum/tgs_chat_channel/channel = I + if (!channel.is_private_channel && ((channel.is_admin_channel && admin_only) || (!channel.is_admin_channel && !admin_only))) + channels += I.id + message = list("message" = message, "channelIds" = channels) if(intercepted_message_queue) intercepted_message_queue += list(message) else Export(TGS4_COMM_CHAT, message) /datum/tgs_api/v4/ChatPrivateMessage(message, datum/tgs_chat_user/user) - message = list("message" = message, "user" = list("id" = user.id, "channel" = user.channel.id)) + message = list("message" = message, "channelIds" = list(user.channel.id)) if(intercepted_message_queue) intercepted_message_queue += list(message) else From ee62423d1ef090c8e3a537004424eb9af228d9cb Mon Sep 17 00:00:00 2001 From: Cyberboss Date: Tue, 23 Oct 2018 16:22:17 -0400 Subject: [PATCH 08/51] Remove unused using --- src/Tgstation.Server.Host/Controllers/InstanceController.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Tgstation.Server.Host/Controllers/InstanceController.cs b/src/Tgstation.Server.Host/Controllers/InstanceController.cs index 5db9cd5897..8a4cb807e1 100644 --- a/src/Tgstation.Server.Host/Controllers/InstanceController.cs +++ b/src/Tgstation.Server.Host/Controllers/InstanceController.cs @@ -9,7 +9,6 @@ using System.Linq; using System.Linq.Expressions; using System.Net; using System.Reflection; -using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api; From 9dea3ba0887017aaf37a5da45f56fdefe6c27379 Mon Sep 17 00:00:00 2001 From: Cyberboss Date: Tue, 23 Oct 2018 16:25:21 -0400 Subject: [PATCH 09/51] Prevent instances from being created in the installation directory --- .../Controllers/InstanceController.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/Tgstation.Server.Host/Controllers/InstanceController.cs b/src/Tgstation.Server.Host/Controllers/InstanceController.cs index 8a4cb807e1..e220a74e39 100644 --- a/src/Tgstation.Server.Host/Controllers/InstanceController.cs +++ b/src/Tgstation.Server.Host/Controllers/InstanceController.cs @@ -120,6 +120,17 @@ namespace Tgstation.Server.Host.Controllers return BadRequest(new ErrorMessage { Message = "path must not be empty!" }); NormalizeModelPath(model, out var rawPath); + + var localPath = ioManager.ResolvePath("."); + NormalizeModelPath(new Api.Models.Instance + { + Path = localPath + }, out var normalizedLocalPath); + + if (rawPath.StartsWith(normalizedLocalPath, StringComparison.Ordinal)) + return Conflict("Instances cannot be created in the installation directory!"); + + var dirExistsTask = ioManager.DirectoryExists(model.Path, cancellationToken); bool attached = false; if (await ioManager.FileExists(model.Path, cancellationToken).ConfigureAwait(false) || await dirExistsTask.ConfigureAwait(false)) From 0c563d014cdf68eaecbcb739a7ccd883bfdfb3a3 Mon Sep 17 00:00:00 2001 From: Cyberboss Date: Tue, 23 Oct 2018 16:29:21 -0400 Subject: [PATCH 10/51] Add a test for creating instances in the installation directory --- tests/Tgstation.Server.Tests/InstanceManagerTest.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/Tgstation.Server.Tests/InstanceManagerTest.cs b/tests/Tgstation.Server.Tests/InstanceManagerTest.cs index 0c9efe68b9..6a09d73d63 100644 --- a/tests/Tgstation.Server.Tests/InstanceManagerTest.cs +++ b/tests/Tgstation.Server.Tests/InstanceManagerTest.cs @@ -51,6 +51,13 @@ namespace Tgstation.Server.Tests }, cancellationToken)).ConfigureAwait(false); await Assert.ThrowsExceptionAsync(() => instanceManagerClient.CreateOrAttach(firstTest, cancellationToken)).ConfigureAwait(false); + //can't create instances in installation directory + await Assert.ThrowsExceptionAsync(() => instanceManagerClient.CreateOrAttach(new Api.Models.Instance + { + Path = "./A/Local/Path", + Name = "NoInstallDirTest" + }, cancellationToken)).ConfigureAwait(false); + //can't move to existent directories await Assert.ThrowsExceptionAsync(() => instanceManagerClient.Update(new Api.Models.Instance { From 673e1db14e54ac268913112fc54bc29071bf6355 Mon Sep 17 00:00:00 2001 From: Cyberboss Date: Tue, 23 Oct 2018 16:31:45 -0400 Subject: [PATCH 11/51] Fix compile error --- src/DMAPI/tgs/v4/api.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DMAPI/tgs/v4/api.dm b/src/DMAPI/tgs/v4/api.dm index dc03791241..d40325cb5a 100644 --- a/src/DMAPI/tgs/v4/api.dm +++ b/src/DMAPI/tgs/v4/api.dm @@ -280,7 +280,7 @@ for(var/I in ChatChannelInfo()) var/datum/tgs_chat_channel/channel = I if (!channel.is_private_channel && ((channel.is_admin_channel && admin_only) || (!channel.is_admin_channel && !admin_only))) - channels += I.id + channels += channel.id message = list("message" = message, "channelIds" = channels) if(intercepted_message_queue) intercepted_message_queue += list(message) From ed197c632baaf03b81121c3aaca65abd429987d2 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Wed, 24 Oct 2018 11:19:23 -0400 Subject: [PATCH 12/51] Add mising call to postWriteHandler --- .../Components/StaticFiles/Configuration.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Tgstation.Server.Host/Components/StaticFiles/Configuration.cs b/src/Tgstation.Server.Host/Components/StaticFiles/Configuration.cs index bb19579a21..5587dc90c3 100644 --- a/src/Tgstation.Server.Host/Components/StaticFiles/Configuration.cs +++ b/src/Tgstation.Server.Host/Components/StaticFiles/Configuration.cs @@ -366,6 +366,7 @@ namespace Tgstation.Server.Host.Components.StaticFiles var success = synchronousIOManager.WriteFileChecked(path, data, ref fileHash, cancellationToken); if (!success) return; + postWriteHandler.HandleWrite(path); result = new ConfigurationFile { Content = data, From 67287458d3b907bf04f3e51d40129b98a4cce309 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Wed, 24 Oct 2018 12:13:56 -0400 Subject: [PATCH 13/51] Don't post write on deletes --- .../Components/StaticFiles/Configuration.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Tgstation.Server.Host/Components/StaticFiles/Configuration.cs b/src/Tgstation.Server.Host/Components/StaticFiles/Configuration.cs index 5587dc90c3..f7a6436e22 100644 --- a/src/Tgstation.Server.Host/Components/StaticFiles/Configuration.cs +++ b/src/Tgstation.Server.Host/Components/StaticFiles/Configuration.cs @@ -366,7 +366,8 @@ namespace Tgstation.Server.Host.Components.StaticFiles var success = synchronousIOManager.WriteFileChecked(path, data, ref fileHash, cancellationToken); if (!success) return; - postWriteHandler.HandleWrite(path); + if (data != null) + postWriteHandler.HandleWrite(path); result = new ConfigurationFile { Content = data, From 5c984d08c20ad4c2f78c8b8ba7c487bb6c0c4be3 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Sat, 27 Oct 2018 14:12:01 -0400 Subject: [PATCH 14/51] Fix spurious travis failures --- build/test_core.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/build/test_core.sh b/build/test_core.sh index b361813ef7..1a8b1108a6 100755 --- a/build/test_core.sh +++ b/build/test_core.sh @@ -8,34 +8,34 @@ mkdir TestResults cd tests/Tgstation.Server.Api.Tests dotnet build -c $CONFIG /p:CopyLocalLockFileAssemblies=true -$HOME/.dotnet/tools/coverlet bin/$CONFIG/netcoreapp2.1/Tgstation.Server.Api.Tests.dll --target "dotnet" --targetargs "test -c $CONFIG --no-build" --format opencover --output "../../TestResults/api.xml" --include "[Tgstation.Server*]*" --exclude "[Tgstation.Server.Api.Tests*]*" +$HOME/.dotnet/tools/coverlet bin/$CONFIG/netcoreapp2.1/Tgstation.Server.Api.Tests.dll --target "bash" --targetargs "-c 'dotnet test -c $CONFIG --no-build && sync'" --format opencover --output "../../TestResults/api.xml" --include "[Tgstation.Server*]*" --exclude "[Tgstation.Server.Api.Tests*]*" cd ../Tgstation.Server.Client.Tests dotnet build -c $CONFIG /p:CopyLocalLockFileAssemblies=true -$HOME/.dotnet/tools/coverlet bin/$CONFIG/netcoreapp2.1/Tgstation.Server.Client.Tests.dll --target "dotnet" --targetargs "test -c $CONFIG --no-build" --format opencover --output "../../TestResults/client.xml" --include "[Tgstation.Server*]*" --exclude "[Tgstation.Server.Client.Tests*]*" +$HOME/.dotnet/tools/coverlet bin/$CONFIG/netcoreapp2.1/Tgstation.Server.Client.Tests.dll --target "bash" --targetargs "-c 'dotnet test -c $CONFIG --no-build && sync'" --format opencover --output "../../TestResults/client.xml" --include "[Tgstation.Server*]*" --exclude "[Tgstation.Server.Client.Tests*]*" cd ../Tgstation.Server.Host.Tests dotnet build -c $CONFIG /p:CopyLocalLockFileAssemblies=true -$HOME/.dotnet/tools/coverlet bin/$CONFIG/netcoreapp2.1/Tgstation.Server.Host.Tests.dll --target "dotnet" --targetargs "test -c $CONFIG --no-build" --format opencover --output "../../TestResults/host.xml" --include "[Tgstation.Server*]*" --exclude "[Tgstation.Server.Host.Tests*]*" --exclude "[Tgstation.Server.Host]Tgstation.Server.Host.Models.Migrations.*" +$HOME/.dotnet/tools/coverlet bin/$CONFIG/netcoreapp2.1/Tgstation.Server.Host.Tests.dll --target "bash" --targetargs "-c 'dotnet test -c $CONFIG --no-build && sync'" --format opencover --output "../../TestResults/host.xml" --include "[Tgstation.Server*]*" --exclude "[Tgstation.Server.Host.Tests*]*" --exclude "[Tgstation.Server.Host]Tgstation.Server.Host.Models.Migrations.*" cd ../Tgstation.Server.Host.Watchdog.Tests dotnet build -c $CONFIG /p:CopyLocalLockFileAssemblies=true -$HOME/.dotnet/tools/coverlet bin/$CONFIG/netcoreapp2.1/Tgstation.Server.Host.Watchdog.Tests.dll --target "dotnet" --targetargs "test -c $CONFIG --no-build" --format opencover --output "../../TestResults/watchdog.xml" --include "[Tgstation.Server*]*" --exclude "[Tgstation.Server.Host.Watchdog.Tests*]*" +$HOME/.dotnet/tools/coverlet bin/$CONFIG/netcoreapp2.1/Tgstation.Server.Host.Watchdog.Tests.dll --target "bash" --targetargs "-c 'dotnet test -c $CONFIG --no-build && sync'" --format opencover --output "../../TestResults/watchdog.xml" --include "[Tgstation.Server*]*" --exclude "[Tgstation.Server.Host.Watchdog.Tests*]*" cd ../Tgstation.Server.Host.Console.Tests dotnet build -c $CONFIG /p:CopyLocalLockFileAssemblies=true -$HOME/.dotnet/tools/coverlet bin/$CONFIG/netcoreapp2.1/Tgstation.Server.Host.Console.Tests.dll --target "dotnet" --targetargs "test -c $CONFIG --no-build" --format opencover --output "../../TestResults/console.xml" --include "[Tgstation.Server*]*" --exclude "[Tgstation.Server.Host.Console.Tests*]*" +$HOME/.dotnet/tools/coverlet bin/$CONFIG/netcoreapp2.1/Tgstation.Server.Host.Console.Tests.dll --target "bash" --targetargs "-c 'dotnet test -c $CONFIG --no-build && sync'" --format opencover --output "../../TestResults/console.xml" --include "[Tgstation.Server*]*" --exclude "[Tgstation.Server.Host.Console.Tests*]*" cd ../Tgstation.Server.Tests export TGS4_TEST_DATABASE_TYPE=MySql export TGS4_TEST_CONNECTION_STRING="server=127.0.0.1;uid=root;pwd=;database=tgs_test" #token set in CI settings dotnet build -c $CONFIG /p:CopyLocalLockFileAssemblies=true -$HOME/.dotnet/tools/coverlet bin/$CONFIG/netcoreapp2.1/Tgstation.Server.Tests.dll --target "dotnet" --targetargs "test -c $CONFIG --no-build" --format opencover --output "../../TestResults/server.xml" --include "[Tgstation.Server*]*" --exclude "[Tgstation.Server.Tests*]*" --exclude "[Tgstation.Server.Host]Tgstation.Server.Host.Models.Migrations.*" +$HOME/.dotnet/tools/coverlet bin/$CONFIG/netcoreapp2.1/Tgstation.Server.Tests.dll --target "bash" --targetargs "-c 'dotnet test -c $CONFIG --no-build && sync'" --format opencover --output "../../TestResults/server.xml" --include "[Tgstation.Server*]*" --exclude "[Tgstation.Server.Tests*]*" --exclude "[Tgstation.Server.Host]Tgstation.Server.Host.Models.Migrations.*" cd ../../TestResults From 0f1fff2915cfbe7a195797600552da9ade9d03d3 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Sat, 27 Oct 2018 14:19:40 -0400 Subject: [PATCH 15/51] Escaped double quotes work better --- build/test_core.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/build/test_core.sh b/build/test_core.sh index 1a8b1108a6..36242dee49 100755 --- a/build/test_core.sh +++ b/build/test_core.sh @@ -8,34 +8,34 @@ mkdir TestResults cd tests/Tgstation.Server.Api.Tests dotnet build -c $CONFIG /p:CopyLocalLockFileAssemblies=true -$HOME/.dotnet/tools/coverlet bin/$CONFIG/netcoreapp2.1/Tgstation.Server.Api.Tests.dll --target "bash" --targetargs "-c 'dotnet test -c $CONFIG --no-build && sync'" --format opencover --output "../../TestResults/api.xml" --include "[Tgstation.Server*]*" --exclude "[Tgstation.Server.Api.Tests*]*" +$HOME/.dotnet/tools/coverlet bin/$CONFIG/netcoreapp2.1/Tgstation.Server.Api.Tests.dll --target "bash" --targetargs "-c \"dotnet test -c $CONFIG --no-build && sync\"" --format opencover --output "../../TestResults/api.xml" --include "[Tgstation.Server*]*" --exclude "[Tgstation.Server.Api.Tests*]*" cd ../Tgstation.Server.Client.Tests dotnet build -c $CONFIG /p:CopyLocalLockFileAssemblies=true -$HOME/.dotnet/tools/coverlet bin/$CONFIG/netcoreapp2.1/Tgstation.Server.Client.Tests.dll --target "bash" --targetargs "-c 'dotnet test -c $CONFIG --no-build && sync'" --format opencover --output "../../TestResults/client.xml" --include "[Tgstation.Server*]*" --exclude "[Tgstation.Server.Client.Tests*]*" +$HOME/.dotnet/tools/coverlet bin/$CONFIG/netcoreapp2.1/Tgstation.Server.Client.Tests.dll --target "bash" --targetargs "-c \"dotnet test -c $CONFIG --no-build && sync\"" --format opencover --output "../../TestResults/client.xml" --include "[Tgstation.Server*]*" --exclude "[Tgstation.Server.Client.Tests*]*" cd ../Tgstation.Server.Host.Tests dotnet build -c $CONFIG /p:CopyLocalLockFileAssemblies=true -$HOME/.dotnet/tools/coverlet bin/$CONFIG/netcoreapp2.1/Tgstation.Server.Host.Tests.dll --target "bash" --targetargs "-c 'dotnet test -c $CONFIG --no-build && sync'" --format opencover --output "../../TestResults/host.xml" --include "[Tgstation.Server*]*" --exclude "[Tgstation.Server.Host.Tests*]*" --exclude "[Tgstation.Server.Host]Tgstation.Server.Host.Models.Migrations.*" +$HOME/.dotnet/tools/coverlet bin/$CONFIG/netcoreapp2.1/Tgstation.Server.Host.Tests.dll --target "bash" --targetargs "-c \"dotnet test -c $CONFIG --no-build && sync\"" --format opencover --output "../../TestResults/host.xml" --include "[Tgstation.Server*]*" --exclude "[Tgstation.Server.Host.Tests*]*" --exclude "[Tgstation.Server.Host]Tgstation.Server.Host.Models.Migrations.*" cd ../Tgstation.Server.Host.Watchdog.Tests dotnet build -c $CONFIG /p:CopyLocalLockFileAssemblies=true -$HOME/.dotnet/tools/coverlet bin/$CONFIG/netcoreapp2.1/Tgstation.Server.Host.Watchdog.Tests.dll --target "bash" --targetargs "-c 'dotnet test -c $CONFIG --no-build && sync'" --format opencover --output "../../TestResults/watchdog.xml" --include "[Tgstation.Server*]*" --exclude "[Tgstation.Server.Host.Watchdog.Tests*]*" +$HOME/.dotnet/tools/coverlet bin/$CONFIG/netcoreapp2.1/Tgstation.Server.Host.Watchdog.Tests.dll --target "bash" --targetargs "-c \"dotnet test -c $CONFIG --no-build && sync\"" --format opencover --output "../../TestResults/watchdog.xml" --include "[Tgstation.Server*]*" --exclude "[Tgstation.Server.Host.Watchdog.Tests*]*" cd ../Tgstation.Server.Host.Console.Tests dotnet build -c $CONFIG /p:CopyLocalLockFileAssemblies=true -$HOME/.dotnet/tools/coverlet bin/$CONFIG/netcoreapp2.1/Tgstation.Server.Host.Console.Tests.dll --target "bash" --targetargs "-c 'dotnet test -c $CONFIG --no-build && sync'" --format opencover --output "../../TestResults/console.xml" --include "[Tgstation.Server*]*" --exclude "[Tgstation.Server.Host.Console.Tests*]*" +$HOME/.dotnet/tools/coverlet bin/$CONFIG/netcoreapp2.1/Tgstation.Server.Host.Console.Tests.dll --target "bash" --targetargs "-c \"dotnet test -c $CONFIG --no-build && sync\"" --format opencover --output "../../TestResults/console.xml" --include "[Tgstation.Server*]*" --exclude "[Tgstation.Server.Host.Console.Tests*]*" cd ../Tgstation.Server.Tests export TGS4_TEST_DATABASE_TYPE=MySql export TGS4_TEST_CONNECTION_STRING="server=127.0.0.1;uid=root;pwd=;database=tgs_test" #token set in CI settings dotnet build -c $CONFIG /p:CopyLocalLockFileAssemblies=true -$HOME/.dotnet/tools/coverlet bin/$CONFIG/netcoreapp2.1/Tgstation.Server.Tests.dll --target "bash" --targetargs "-c 'dotnet test -c $CONFIG --no-build && sync'" --format opencover --output "../../TestResults/server.xml" --include "[Tgstation.Server*]*" --exclude "[Tgstation.Server.Tests*]*" --exclude "[Tgstation.Server.Host]Tgstation.Server.Host.Models.Migrations.*" +$HOME/.dotnet/tools/coverlet bin/$CONFIG/netcoreapp2.1/Tgstation.Server.Tests.dll --target "bash" --targetargs "-c \"dotnet test -c $CONFIG --no-build && sync\"" --format opencover --output "../../TestResults/server.xml" --include "[Tgstation.Server*]*" --exclude "[Tgstation.Server.Tests*]*" --exclude "[Tgstation.Server.Host]Tgstation.Server.Host.Models.Migrations.*" cd ../../TestResults From c2f44c3a7bef5aa069d59a8dd20e911239a26ee9 Mon Sep 17 00:00:00 2001 From: Cyberboss Date: Mon, 29 Oct 2018 09:42:00 -0400 Subject: [PATCH 16/51] Version bump to 4.0.1.3 --- .../Tgstation.Server.Host.Console.csproj | 2 +- src/Tgstation.Server.Host.Service/Properties/AssemblyInfo.cs | 4 ++-- .../Tgstation.Server.Host.Watchdog.csproj | 2 +- src/Tgstation.Server.Host/Tgstation.Server.Host.csproj | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Tgstation.Server.Host.Console/Tgstation.Server.Host.Console.csproj b/src/Tgstation.Server.Host.Console/Tgstation.Server.Host.Console.csproj index fb8e8bcfa7..bb659a9233 100644 --- a/src/Tgstation.Server.Host.Console/Tgstation.Server.Host.Console.csproj +++ b/src/Tgstation.Server.Host.Console/Tgstation.Server.Host.Console.csproj @@ -4,7 +4,7 @@ Exe netcoreapp2.1 Full - 4.0.1.2 + 4.0.1.3 diff --git a/src/Tgstation.Server.Host.Service/Properties/AssemblyInfo.cs b/src/Tgstation.Server.Host.Service/Properties/AssemblyInfo.cs index f53415defe..88bf8ee48f 100644 --- a/src/Tgstation.Server.Host.Service/Properties/AssemblyInfo.cs +++ b/src/Tgstation.Server.Host.Service/Properties/AssemblyInfo.cs @@ -7,5 +7,5 @@ using System.Runtime.InteropServices; [assembly: Guid("29927416-3b78-49a7-a560-5ccaa638b6b4")] [assembly: InternalsVisibleTo("Tgstation.Server.Host.Service.Tests")] -[assembly: AssemblyVersion("4.0.1.2")] -[assembly: AssemblyFileVersion("4.0.1.2")] +[assembly: AssemblyVersion("4.0.1.3")] +[assembly: AssemblyFileVersion("4.0.1.3")] diff --git a/src/Tgstation.Server.Host.Watchdog/Tgstation.Server.Host.Watchdog.csproj b/src/Tgstation.Server.Host.Watchdog/Tgstation.Server.Host.Watchdog.csproj index c5a1cb5ab7..2d3d915541 100644 --- a/src/Tgstation.Server.Host.Watchdog/Tgstation.Server.Host.Watchdog.csproj +++ b/src/Tgstation.Server.Host.Watchdog/Tgstation.Server.Host.Watchdog.csproj @@ -4,7 +4,7 @@ netstandard2.0 Full false - 4.0.1.2 + 4.0.1.3 diff --git a/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj b/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj index 6a1c515126..7427093f60 100644 --- a/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj +++ b/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj @@ -3,7 +3,7 @@ netcoreapp2.1 Full - 4.0.1.2 + 4.0.1.3 From e1472c1b299cf8ec1aa1ec45e61b38ea45878ca1 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Mon, 29 Oct 2018 12:01:04 -0400 Subject: [PATCH 17/51] Revert "Fix spurious travis failures" --- build/test_core.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/build/test_core.sh b/build/test_core.sh index 36242dee49..b361813ef7 100755 --- a/build/test_core.sh +++ b/build/test_core.sh @@ -8,34 +8,34 @@ mkdir TestResults cd tests/Tgstation.Server.Api.Tests dotnet build -c $CONFIG /p:CopyLocalLockFileAssemblies=true -$HOME/.dotnet/tools/coverlet bin/$CONFIG/netcoreapp2.1/Tgstation.Server.Api.Tests.dll --target "bash" --targetargs "-c \"dotnet test -c $CONFIG --no-build && sync\"" --format opencover --output "../../TestResults/api.xml" --include "[Tgstation.Server*]*" --exclude "[Tgstation.Server.Api.Tests*]*" +$HOME/.dotnet/tools/coverlet bin/$CONFIG/netcoreapp2.1/Tgstation.Server.Api.Tests.dll --target "dotnet" --targetargs "test -c $CONFIG --no-build" --format opencover --output "../../TestResults/api.xml" --include "[Tgstation.Server*]*" --exclude "[Tgstation.Server.Api.Tests*]*" cd ../Tgstation.Server.Client.Tests dotnet build -c $CONFIG /p:CopyLocalLockFileAssemblies=true -$HOME/.dotnet/tools/coverlet bin/$CONFIG/netcoreapp2.1/Tgstation.Server.Client.Tests.dll --target "bash" --targetargs "-c \"dotnet test -c $CONFIG --no-build && sync\"" --format opencover --output "../../TestResults/client.xml" --include "[Tgstation.Server*]*" --exclude "[Tgstation.Server.Client.Tests*]*" +$HOME/.dotnet/tools/coverlet bin/$CONFIG/netcoreapp2.1/Tgstation.Server.Client.Tests.dll --target "dotnet" --targetargs "test -c $CONFIG --no-build" --format opencover --output "../../TestResults/client.xml" --include "[Tgstation.Server*]*" --exclude "[Tgstation.Server.Client.Tests*]*" cd ../Tgstation.Server.Host.Tests dotnet build -c $CONFIG /p:CopyLocalLockFileAssemblies=true -$HOME/.dotnet/tools/coverlet bin/$CONFIG/netcoreapp2.1/Tgstation.Server.Host.Tests.dll --target "bash" --targetargs "-c \"dotnet test -c $CONFIG --no-build && sync\"" --format opencover --output "../../TestResults/host.xml" --include "[Tgstation.Server*]*" --exclude "[Tgstation.Server.Host.Tests*]*" --exclude "[Tgstation.Server.Host]Tgstation.Server.Host.Models.Migrations.*" +$HOME/.dotnet/tools/coverlet bin/$CONFIG/netcoreapp2.1/Tgstation.Server.Host.Tests.dll --target "dotnet" --targetargs "test -c $CONFIG --no-build" --format opencover --output "../../TestResults/host.xml" --include "[Tgstation.Server*]*" --exclude "[Tgstation.Server.Host.Tests*]*" --exclude "[Tgstation.Server.Host]Tgstation.Server.Host.Models.Migrations.*" cd ../Tgstation.Server.Host.Watchdog.Tests dotnet build -c $CONFIG /p:CopyLocalLockFileAssemblies=true -$HOME/.dotnet/tools/coverlet bin/$CONFIG/netcoreapp2.1/Tgstation.Server.Host.Watchdog.Tests.dll --target "bash" --targetargs "-c \"dotnet test -c $CONFIG --no-build && sync\"" --format opencover --output "../../TestResults/watchdog.xml" --include "[Tgstation.Server*]*" --exclude "[Tgstation.Server.Host.Watchdog.Tests*]*" +$HOME/.dotnet/tools/coverlet bin/$CONFIG/netcoreapp2.1/Tgstation.Server.Host.Watchdog.Tests.dll --target "dotnet" --targetargs "test -c $CONFIG --no-build" --format opencover --output "../../TestResults/watchdog.xml" --include "[Tgstation.Server*]*" --exclude "[Tgstation.Server.Host.Watchdog.Tests*]*" cd ../Tgstation.Server.Host.Console.Tests dotnet build -c $CONFIG /p:CopyLocalLockFileAssemblies=true -$HOME/.dotnet/tools/coverlet bin/$CONFIG/netcoreapp2.1/Tgstation.Server.Host.Console.Tests.dll --target "bash" --targetargs "-c \"dotnet test -c $CONFIG --no-build && sync\"" --format opencover --output "../../TestResults/console.xml" --include "[Tgstation.Server*]*" --exclude "[Tgstation.Server.Host.Console.Tests*]*" +$HOME/.dotnet/tools/coverlet bin/$CONFIG/netcoreapp2.1/Tgstation.Server.Host.Console.Tests.dll --target "dotnet" --targetargs "test -c $CONFIG --no-build" --format opencover --output "../../TestResults/console.xml" --include "[Tgstation.Server*]*" --exclude "[Tgstation.Server.Host.Console.Tests*]*" cd ../Tgstation.Server.Tests export TGS4_TEST_DATABASE_TYPE=MySql export TGS4_TEST_CONNECTION_STRING="server=127.0.0.1;uid=root;pwd=;database=tgs_test" #token set in CI settings dotnet build -c $CONFIG /p:CopyLocalLockFileAssemblies=true -$HOME/.dotnet/tools/coverlet bin/$CONFIG/netcoreapp2.1/Tgstation.Server.Tests.dll --target "bash" --targetargs "-c \"dotnet test -c $CONFIG --no-build && sync\"" --format opencover --output "../../TestResults/server.xml" --include "[Tgstation.Server*]*" --exclude "[Tgstation.Server.Tests*]*" --exclude "[Tgstation.Server.Host]Tgstation.Server.Host.Models.Migrations.*" +$HOME/.dotnet/tools/coverlet bin/$CONFIG/netcoreapp2.1/Tgstation.Server.Tests.dll --target "dotnet" --targetargs "test -c $CONFIG --no-build" --format opencover --output "../../TestResults/server.xml" --include "[Tgstation.Server*]*" --exclude "[Tgstation.Server.Tests*]*" --exclude "[Tgstation.Server.Host]Tgstation.Server.Host.Models.Migrations.*" cd ../../TestResults From 250ddaf8448cf3fc5205f48e09d8e377645c7b75 Mon Sep 17 00:00:00 2001 From: Cyberboss Date: Thu, 1 Nov 2018 12:17:28 -0400 Subject: [PATCH 18/51] Prevent integration tests from running while live unit testing --- tests/Tgstation.Server.Tests/IntegrationTest.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/Tgstation.Server.Tests/IntegrationTest.cs b/tests/Tgstation.Server.Tests/IntegrationTest.cs index bc80848dd4..a77962acd1 100644 --- a/tests/Tgstation.Server.Tests/IntegrationTest.cs +++ b/tests/Tgstation.Server.Tests/IntegrationTest.cs @@ -2,6 +2,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using System.Diagnostics; using System.IO; +using System.Linq; using System.Net.Http.Headers; using System.Reflection; using System.Threading; @@ -17,8 +18,9 @@ namespace Tgstation.Server.Tests public sealed class IntegrationTest { readonly IServerClientFactory clientFactory = new ServerClientFactory(new ProductHeaderValue(Assembly.GetExecutingAssembly().GetName().Name, Assembly.GetExecutingAssembly().GetName().Version.ToString())); - + [TestMethod] + [TestCategory("SkipWhenLiveUnitTesting")] public async Task TestUpdate() { var updatePathRoot = Path.GetTempFileName(); @@ -93,6 +95,7 @@ namespace Tgstation.Server.Tests } [TestMethod] + [TestCategory("SkipWhenLiveUnitTesting")] public async Task TestStandardOperation() { var server = new TestingServer(null); From 821ef25b8dba74971949e4009c12064a4763c960 Mon Sep 17 00:00:00 2001 From: Cyberboss Date: Thu, 1 Nov 2018 12:22:25 -0400 Subject: [PATCH 19/51] Prevent symlink tests from failing on windows when not running as an administrator --- .../IO/TestSymlinkFactory.cs | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/tests/Tgstation.Server.Host.Tests/IO/TestSymlinkFactory.cs b/tests/Tgstation.Server.Host.Tests/IO/TestSymlinkFactory.cs index 0b8b6fbfc5..668bf7dd90 100644 --- a/tests/Tgstation.Server.Host.Tests/IO/TestSymlinkFactory.cs +++ b/tests/Tgstation.Server.Host.Tests/IO/TestSymlinkFactory.cs @@ -2,6 +2,7 @@ using System; using System.IO; using System.Runtime.InteropServices; +using System.Security.Principal; using System.Threading.Tasks; namespace Tgstation.Server.Host.IO.Tests @@ -20,9 +21,20 @@ namespace Tgstation.Server.Host.IO.Tests symlinkFactory = new PosixSymlinkFactory(); } + public static bool HasPermissionToMakeSymlinks() + { + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + return true; + var identity = WindowsIdentity.GetCurrent(); + var principal = new WindowsPrincipal(identity); + return principal.IsInRole(WindowsBuiltInRole.Administrator); + } + [TestMethod] public async Task TestFileWorks() { + if (!HasPermissionToMakeSymlinks()) + Assert.Inconclusive(); const string Text = "Hello world"; string f2 = null; var f1 = Path.GetTempFileName(); @@ -50,6 +62,8 @@ namespace Tgstation.Server.Host.IO.Tests [TestMethod] public async Task TestDirectoryWorks() { + if (!HasPermissionToMakeSymlinks()) + Assert.Inconclusive(); const string FileName = "TestFile.txt"; const string Text = "Hello world"; string f2 = null; @@ -86,17 +100,14 @@ namespace Tgstation.Server.Host.IO.Tests [TestMethod] public async Task TestFailsProperly() { - const string BadPath = "/../../?>O(UF+}P{{??>/////"; + const string BadPath = "/../../?>!@#$%^&*()O(UF+}P{{??>/////"; try { await symlinkFactory.CreateSymbolicLink(BadPath, BadPath, default).ConfigureAwait(false); + Assert.Fail("No exception thrown!"); } - catch - { - return; - } - Assert.Fail("No exception thrown!"); + catch { } } } } From 4f0d8162cf8c4c68e9ced3f5cdfaf35e74b48396 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Fri, 2 Nov 2018 11:15:09 -0400 Subject: [PATCH 20/51] Update packages to stable --- .../Tgstation.Server.Host.csproj | 17 ++++++++++------- .../Tgstation.Server.Api.Tests.csproj | 2 +- .../Tgstation.Server.Client.Tests.csproj | 2 +- .../Tgstation.Server.Host.Console.Tests.csproj | 2 +- .../Tgstation.Server.Host.Tests.csproj | 2 +- .../Tgstation.Server.Host.Watchdog.Tests.csproj | 2 +- .../Tgstation.Server.Tests.csproj | 2 +- 7 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj b/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj index 7427093f60..12ea42b0ff 100644 --- a/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj +++ b/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj @@ -26,18 +26,21 @@ - + - + all runtime; build; native; contentfiles; analyzers - - - - + + + + + all + runtime; build; native; contentfiles; analyzers + @@ -46,7 +49,7 @@ - + diff --git a/tests/Tgstation.Server.Api.Tests/Tgstation.Server.Api.Tests.csproj b/tests/Tgstation.Server.Api.Tests/Tgstation.Server.Api.Tests.csproj index c395583e37..1ec7f54612 100644 --- a/tests/Tgstation.Server.Api.Tests/Tgstation.Server.Api.Tests.csproj +++ b/tests/Tgstation.Server.Api.Tests/Tgstation.Server.Api.Tests.csproj @@ -8,7 +8,7 @@ - + diff --git a/tests/Tgstation.Server.Client.Tests/Tgstation.Server.Client.Tests.csproj b/tests/Tgstation.Server.Client.Tests/Tgstation.Server.Client.Tests.csproj index 6eaed1daed..4041253f61 100644 --- a/tests/Tgstation.Server.Client.Tests/Tgstation.Server.Client.Tests.csproj +++ b/tests/Tgstation.Server.Client.Tests/Tgstation.Server.Client.Tests.csproj @@ -8,7 +8,7 @@ - + diff --git a/tests/Tgstation.Server.Host.Console.Tests/Tgstation.Server.Host.Console.Tests.csproj b/tests/Tgstation.Server.Host.Console.Tests/Tgstation.Server.Host.Console.Tests.csproj index 1648915ec8..7e17c460e2 100644 --- a/tests/Tgstation.Server.Host.Console.Tests/Tgstation.Server.Host.Console.Tests.csproj +++ b/tests/Tgstation.Server.Host.Console.Tests/Tgstation.Server.Host.Console.Tests.csproj @@ -8,7 +8,7 @@ - + diff --git a/tests/Tgstation.Server.Host.Tests/Tgstation.Server.Host.Tests.csproj b/tests/Tgstation.Server.Host.Tests/Tgstation.Server.Host.Tests.csproj index 3226a9a749..c075c20e1c 100644 --- a/tests/Tgstation.Server.Host.Tests/Tgstation.Server.Host.Tests.csproj +++ b/tests/Tgstation.Server.Host.Tests/Tgstation.Server.Host.Tests.csproj @@ -8,7 +8,7 @@ - + diff --git a/tests/Tgstation.Server.Host.Watchdog.Tests/Tgstation.Server.Host.Watchdog.Tests.csproj b/tests/Tgstation.Server.Host.Watchdog.Tests/Tgstation.Server.Host.Watchdog.Tests.csproj index d2102ff8cf..5f4e69a72a 100644 --- a/tests/Tgstation.Server.Host.Watchdog.Tests/Tgstation.Server.Host.Watchdog.Tests.csproj +++ b/tests/Tgstation.Server.Host.Watchdog.Tests/Tgstation.Server.Host.Watchdog.Tests.csproj @@ -18,7 +18,7 @@ - + diff --git a/tests/Tgstation.Server.Tests/Tgstation.Server.Tests.csproj b/tests/Tgstation.Server.Tests/Tgstation.Server.Tests.csproj index 044446ae80..6927548dae 100644 --- a/tests/Tgstation.Server.Tests/Tgstation.Server.Tests.csproj +++ b/tests/Tgstation.Server.Tests/Tgstation.Server.Tests.csproj @@ -15,7 +15,7 @@ - + From 2b50639e6ddffd04cb279d08bc1be07daeb0f12a Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Fri, 2 Nov 2018 11:23:49 -0400 Subject: [PATCH 21/51] Revert LibGit2Sharp to stable --- src/Tgstation.Server.Host/Tgstation.Server.Host.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj b/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj index 12ea42b0ff..f8ac70e9cd 100644 --- a/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj +++ b/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj @@ -25,7 +25,7 @@ - + From 30bb7a8f158da4ca9386db15470a40cce8691352 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Fri, 2 Nov 2018 11:25:01 -0400 Subject: [PATCH 22/51] Update Discord.Net to the 2.0.0-beta --- src/Tgstation.Server.Host/Tgstation.Server.Host.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj b/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj index f8ac70e9cd..edb20c1711 100644 --- a/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj +++ b/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj @@ -24,7 +24,7 @@ - + From 235c2bf1fec624bd0024c69836bd65abb615d24c Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Fri, 2 Nov 2018 11:27:27 -0400 Subject: [PATCH 23/51] Add a documentation comment --- .../Components/Chat/Providers/DiscordProvider.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Tgstation.Server.Host/Components/Chat/Providers/DiscordProvider.cs b/src/Tgstation.Server.Host/Components/Chat/Providers/DiscordProvider.cs index 70808ab9cc..c1946c2a36 100644 --- a/src/Tgstation.Server.Host/Components/Chat/Providers/DiscordProvider.cs +++ b/src/Tgstation.Server.Host/Components/Chat/Providers/DiscordProvider.cs @@ -49,6 +49,11 @@ namespace Tgstation.Server.Host.Components.Chat.Providers /// readonly List mappedChannels; + /// + /// Normalize a discord mention string + /// + /// The mention provided by the Discord library + /// The normalized mention static string NormalizeMention(string fromDiscord) => fromDiscord.Replace("!", "", StringComparison.Ordinal); /// From d25ae1657c01953c9492674ed5be1c3e8c62c50e Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Fri, 2 Nov 2018 11:27:54 -0400 Subject: [PATCH 24/51] Add a missing inheritdoc --- .../Components/Chat/Providers/DiscordProvider.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Tgstation.Server.Host/Components/Chat/Providers/DiscordProvider.cs b/src/Tgstation.Server.Host/Components/Chat/Providers/DiscordProvider.cs index c1946c2a36..711454887c 100644 --- a/src/Tgstation.Server.Host/Components/Chat/Providers/DiscordProvider.cs +++ b/src/Tgstation.Server.Host/Components/Chat/Providers/DiscordProvider.cs @@ -146,6 +146,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers return true; } + /// public override async Task Disconnect(CancellationToken cancellationToken) { if (!Connected) From c7b8c1b45d7b963e78dfa0d3df9efb695b04f732 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Fri, 2 Nov 2018 12:55:59 -0400 Subject: [PATCH 25/51] Add discord tests --- .../Chat/Providers/TestDiscordProvider.cs | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 tests/Tgstation.Server.Host.Tests/Components/Chat/Providers/TestDiscordProvider.cs diff --git a/tests/Tgstation.Server.Host.Tests/Components/Chat/Providers/TestDiscordProvider.cs b/tests/Tgstation.Server.Host.Tests/Components/Chat/Providers/TestDiscordProvider.cs new file mode 100644 index 0000000000..e422dd239b --- /dev/null +++ b/tests/Tgstation.Server.Host.Tests/Components/Chat/Providers/TestDiscordProvider.cs @@ -0,0 +1,86 @@ +using Microsoft.Extensions.Logging; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace Tgstation.Server.Host.Components.Chat.Providers.Tests +{ + [TestClass] + public sealed class TestDiscordProvider + { + string testToken1; + + [TestInitialize] + public void Initialize() + { + testToken1 = Environment.GetEnvironmentVariable("TGS4_TEST_DISCORD_TOKEN_1"); + } + + [TestMethod] + public void TestConstructionAndDisposal() + { + Assert.ThrowsException(() => new DiscordProvider(null, null)); + var mockLogger = new Mock>(); + Assert.ThrowsException(() => new DiscordProvider(mockLogger.Object, null)); + var mockToken = "asdf"; + new DiscordProvider(mockLogger.Object, mockToken).Dispose(); + } + + [TestMethod] + public async Task TestConnectWithFakeTokenFails() + { + var mockLogger = new Mock>(); + using (var provider = new DiscordProvider(mockLogger.Object, "asdf")) + { + Assert.IsFalse(await provider.Connect(default).ConfigureAwait(false)); + Assert.IsFalse(provider.Connected); + } + } + + [Ignore("Broken due to dependency issues after first call to .Connect()")] + [TestMethod] + public async Task TestConnectAndDisconnect() + { + if (testToken1 == null) + Assert.Inconclusive("Required environment variable TGS4_TEST_DISCORD_TOKEN_1 isn't set!"); + + + var mockLogger = new Mock>(); + using (var provider = new DiscordProvider(mockLogger.Object, testToken1)) + { + Assert.IsFalse(provider.Connected); + await provider.Disconnect(default).ConfigureAwait(false); + Assert.IsFalse(provider.Connected); + Assert.IsTrue(await provider.Connect(default).ConfigureAwait(false)); + Assert.IsTrue(provider.Connected); + Assert.IsTrue(await provider.Connect(default).ConfigureAwait(false)); + Assert.IsTrue(provider.Connected); + + await provider.Disconnect(default).ConfigureAwait(false); + Assert.IsFalse(provider.Connected); + await provider.Disconnect(default).ConfigureAwait(false); + Assert.IsFalse(provider.Connected); + + //now try it with cancellationTokens + using (var cts = new CancellationTokenSource()) + { + cts.Cancel(); + var cancellationToken = cts.Token; + await Assert.ThrowsExceptionAsync(() => provider.Connect(cancellationToken)).ConfigureAwait(false); + Assert.IsFalse(provider.Connected); + Assert.IsTrue(await provider.Connect(default).ConfigureAwait(false)); + Assert.IsTrue(provider.Connected); + await Assert.ThrowsExceptionAsync(() => provider.Disconnect(cancellationToken)).ConfigureAwait(false); + Assert.IsTrue(provider.Connected); + await provider.Disconnect(default).ConfigureAwait(false); + Assert.IsFalse(provider.Connected); + } + + } + } + } +} From 04e8f866d6500ea7cee91b2ac97ec7e101318d32 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Fri, 2 Nov 2018 12:56:08 -0400 Subject: [PATCH 26/51] Improve usage of Assert.Inconclusive --- .../IO/TestSymlinkFactory.cs | 4 ++-- .../Tgstation.Server.Tests/AdministrationTest.cs | 15 +++++++++++++-- tests/Tgstation.Server.Tests/TestingServer.cs | 4 ++-- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/tests/Tgstation.Server.Host.Tests/IO/TestSymlinkFactory.cs b/tests/Tgstation.Server.Host.Tests/IO/TestSymlinkFactory.cs index 668bf7dd90..db3954cdf1 100644 --- a/tests/Tgstation.Server.Host.Tests/IO/TestSymlinkFactory.cs +++ b/tests/Tgstation.Server.Host.Tests/IO/TestSymlinkFactory.cs @@ -34,7 +34,7 @@ namespace Tgstation.Server.Host.IO.Tests public async Task TestFileWorks() { if (!HasPermissionToMakeSymlinks()) - Assert.Inconclusive(); + Assert.Inconclusive("Current user does not have permission to create symlinks!"); const string Text = "Hello world"; string f2 = null; var f1 = Path.GetTempFileName(); @@ -63,7 +63,7 @@ namespace Tgstation.Server.Host.IO.Tests public async Task TestDirectoryWorks() { if (!HasPermissionToMakeSymlinks()) - Assert.Inconclusive(); + Assert.Inconclusive("Current user does not have permission to create symlinks!"); const string FileName = "TestFile.txt"; const string Text = "Hello world"; string f2 = null; diff --git a/tests/Tgstation.Server.Tests/AdministrationTest.cs b/tests/Tgstation.Server.Tests/AdministrationTest.cs index 9bad0c0168..1e0583424e 100644 --- a/tests/Tgstation.Server.Tests/AdministrationTest.cs +++ b/tests/Tgstation.Server.Tests/AdministrationTest.cs @@ -3,6 +3,7 @@ using System; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; +using Tgstation.Server.Api.Models; using Tgstation.Server.Client; namespace Tgstation.Server.Tests @@ -23,10 +24,20 @@ namespace Tgstation.Server.Tests async Task TestRead(CancellationToken cancellationToken) { - var model = await client.Read(cancellationToken).ConfigureAwait(false); + Administration model; + try + { + model = await client.Read(cancellationToken).ConfigureAwait(false); + } + catch (RateLimitException) + { + Assert.Inconclusive("GitHub rate limit hit while testing administration endpoint. Set environment variable TGS4_TEST_GITHUB_TOKEN to fix this!"); + return; //c# needs the equivalent of [noreturn] + } Assert.AreEqual(RuntimeInformation.IsOSPlatform(OSPlatform.Windows), model.WindowsHost); - //uhh not much else to do + //we've released a few 4.x versions now, check the release checker is at least somewhat functional + Assert.AreEqual(4, model.LatestVersion.Major); } } } diff --git a/tests/Tgstation.Server.Tests/TestingServer.cs b/tests/Tgstation.Server.Tests/TestingServer.cs index a76ff15d76..f75fbbea6e 100644 --- a/tests/Tgstation.Server.Tests/TestingServer.cs +++ b/tests/Tgstation.Server.Tests/TestingServer.cs @@ -33,10 +33,10 @@ namespace Tgstation.Server.Tests var gitHubAccessToken = Environment.GetEnvironmentVariable("TGS4_TEST_GITHUB_TOKEN"); if (String.IsNullOrEmpty(databaseType)) - Assert.Fail("No database type configured in env var TGS4_TEST_DATABASE_TYPE!"); + Assert.Inconclusive("No database type configured in env var TGS4_TEST_DATABASE_TYPE!"); if (String.IsNullOrEmpty(connectionString)) - Assert.Fail("No connection string configured in env var TGS4_TEST_CONNECTION_STRING!"); + Assert.Inconclusive("No connection string configured in env var TGS4_TEST_CONNECTION_STRING!"); if (String.IsNullOrEmpty(gitHubAccessToken)) Console.WriteLine("WARNING: No GitHub access token configured, test may fail due to rate limits!"); From bca6d95cc44189cc5cffc22f6c9f57cae53c36db Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Fri, 2 Nov 2018 14:58:51 -0400 Subject: [PATCH 27/51] Give JobHandler a proper constructor --- src/Tgstation.Server.Host/Core/JobHandler.cs | 30 +++++++------------- src/Tgstation.Server.Host/Core/JobManager.cs | 2 +- 2 files changed, 11 insertions(+), 21 deletions(-) diff --git a/src/Tgstation.Server.Host/Core/JobHandler.cs b/src/Tgstation.Server.Host/Core/JobHandler.cs index 72fc7faa40..c57c7bdc1f 100644 --- a/src/Tgstation.Server.Host/Core/JobHandler.cs +++ b/src/Tgstation.Server.Host/Core/JobHandler.cs @@ -9,24 +9,25 @@ namespace Tgstation.Server.Host.Core /// sealed class JobHandler : IDisposable { - /// - /// The being run - /// - readonly Task task; /// /// The for /// readonly CancellationTokenSource cancellationTokenSource; + /// + /// The being run + /// + readonly Task task; /// /// Construct a /// - /// The value of - /// The value of - JobHandler(Task task, CancellationTokenSource cancellationTokenSource) + /// A taking a and returning a that the will wrap + public JobHandler(Func job) { - this.task = task; - this.cancellationTokenSource = cancellationTokenSource; + if (job == null) + throw new ArgumentNullException(nameof(job)); + cancellationTokenSource = new CancellationTokenSource(); + task = job(cancellationTokenSource.Token); } /// @@ -54,16 +55,5 @@ namespace Tgstation.Server.Host.Core /// Cancels /// public void Cancel() => cancellationTokenSource.Cancel(); - - /// - /// Create a - /// - /// A taking a and returning a that the will wrap - /// A new - public static JobHandler Create(Func job) - { - var cts = new CancellationTokenSource(); - return new JobHandler(job(cts.Token), cts); - } } } \ No newline at end of file diff --git a/src/Tgstation.Server.Host/Core/JobManager.cs b/src/Tgstation.Server.Host/Core/JobManager.cs index 614661ac0e..b3aeac331d 100644 --- a/src/Tgstation.Server.Host/Core/JobManager.cs +++ b/src/Tgstation.Server.Host/Core/JobManager.cs @@ -168,7 +168,7 @@ namespace Tgstation.Server.Host.Core await databaseContext.Save(cancellationToken).ConfigureAwait(false); logger.LogDebug("Starting job {0}: {1}...", job.Id, job.Description); - var jobHandler = JobHandler.Create(x => RunJob(job, (jobParam, serviceProvider, ct) => + var jobHandler = new JobHandler(x => RunJob(job, (jobParam, serviceProvider, ct) => operation(jobParam, serviceProvider, y => { lock (this) From 43bcdc28702988b505a1b7a2e5bfdf53544fbe9a Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Fri, 2 Nov 2018 14:58:58 -0400 Subject: [PATCH 28/51] Remove unused usings --- .../Components/Chat/Providers/TestDiscordProvider.cs | 2 -- 1 file changed, 2 deletions(-) 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 e422dd239b..15fec66f51 100644 --- a/tests/Tgstation.Server.Host.Tests/Components/Chat/Providers/TestDiscordProvider.cs +++ b/tests/Tgstation.Server.Host.Tests/Components/Chat/Providers/TestDiscordProvider.cs @@ -2,8 +2,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using System; -using System.Collections.Generic; -using System.Text; using System.Threading; using System.Threading.Tasks; From ed03008a72e69f090f7f0030edc74e5952a8aef0 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Fri, 2 Nov 2018 14:59:23 -0400 Subject: [PATCH 29/51] Add TestJobHandler --- .../Core/TestJobHandler.cs | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 tests/Tgstation.Server.Host.Tests/Core/TestJobHandler.cs diff --git a/tests/Tgstation.Server.Host.Tests/Core/TestJobHandler.cs b/tests/Tgstation.Server.Host.Tests/Core/TestJobHandler.cs new file mode 100644 index 0000000000..4aad145fa5 --- /dev/null +++ b/tests/Tgstation.Server.Host.Tests/Core/TestJobHandler.cs @@ -0,0 +1,80 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Tgstation.Server.Host.Core.Tests +{ + [TestClass] + public sealed class TestJobHandler + { + Task currentWaitTask; + bool cancelled; + + async Task TestJob(CancellationToken cancellationToken) + { + await currentWaitTask.ConfigureAwait(false); + cancelled = cancellationToken.IsCancellationRequested; + } + + [TestMethod] + public void TestConstructionAndDisposal() + { + cancelled = false; + Assert.ThrowsException(() => new JobHandler(null)); + + currentWaitTask = Task.CompletedTask; + new JobHandler(TestJob).Dispose(); + Assert.IsFalse(cancelled); + } + + [TestMethod] + public async Task TestWait() + { + cancelled = false; + //test with a cancelled cts + using (var cts = new CancellationTokenSource()) + { + var tcs = new TaskCompletionSource(); + currentWaitTask = tcs.Task; + cts.Cancel(); + using(var handler = new JobHandler(TestJob)) + { + await Assert.ThrowsExceptionAsync(() => handler.Wait(cts.Token)).ConfigureAwait(false); + tcs.SetResult(null); + await handler.Wait(default).ConfigureAwait(false); + } + } + Assert.IsFalse(cancelled); + } + + [TestMethod] + public void TestProgress() + { + currentWaitTask = Task.CompletedTask; + cancelled = false; + using (var handler = new JobHandler(TestJob)) + { + Assert.IsFalse(handler.Progress.HasValue); + handler.Progress = 42; + Assert.AreEqual(42, handler.Progress); + } + Assert.IsFalse(cancelled); + } + + [TestMethod] + public async Task TestCancellation() + { + var tcs = new TaskCompletionSource(); + currentWaitTask = tcs.Task; + cancelled = false; + using(var handler = new JobHandler(TestJob)) + { + handler.Cancel(); + tcs.SetResult(null); + await handler.Wait(default).ConfigureAwait(false); + } + Assert.IsTrue(cancelled); + } + } +} From bf77bc261ff1d7d653447f54f872e80eb78085c7 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Fri, 2 Nov 2018 15:14:08 -0400 Subject: [PATCH 30/51] Add missing Argument null exceptions to post write handlers --- src/Tgstation.Server.Host/IO/PosixPostWriteHandler.cs | 4 ++++ .../IO/WindowsPostWriteHandler.cs | 10 ++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/Tgstation.Server.Host/IO/PosixPostWriteHandler.cs b/src/Tgstation.Server.Host/IO/PosixPostWriteHandler.cs index d656e0fa99..75eaa24bcd 100644 --- a/src/Tgstation.Server.Host/IO/PosixPostWriteHandler.cs +++ b/src/Tgstation.Server.Host/IO/PosixPostWriteHandler.cs @@ -1,5 +1,6 @@ using Mono.Unix; using Mono.Unix.Native; +using System; namespace Tgstation.Server.Host.IO { @@ -11,6 +12,9 @@ namespace Tgstation.Server.Host.IO /// public void HandleWrite(string filePath) { + if (filePath == null) + throw new ArgumentNullException(nameof(filePath)); + //set executable bit every time, don't want people calling me when their uploaded "sl" binary doesn't work if (Syscall.stat(filePath, out var stat) != 0) throw new UnixIOException(Stdlib.GetLastError()); diff --git a/src/Tgstation.Server.Host/IO/WindowsPostWriteHandler.cs b/src/Tgstation.Server.Host/IO/WindowsPostWriteHandler.cs index 6d149644fa..86907933fe 100644 --- a/src/Tgstation.Server.Host/IO/WindowsPostWriteHandler.cs +++ b/src/Tgstation.Server.Host/IO/WindowsPostWriteHandler.cs @@ -1,4 +1,6 @@ -namespace Tgstation.Server.Host.IO +using System; + +namespace Tgstation.Server.Host.IO { /// /// for Windows systems @@ -6,6 +8,10 @@ sealed class WindowsPostWriteHandler : IPostWriteHandler { /// - public void HandleWrite(string filePath) { } + public void HandleWrite(string filePath) + { + if (filePath == null) + throw new ArgumentNullException(nameof(filePath)); + } } } From 66a1cbc12927e06989142f32dd58688e59feaef6 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Fri, 2 Nov 2018 15:16:42 -0400 Subject: [PATCH 31/51] Add TestPostWriteHandler --- .../IO/TestPostWriteHandler.cs | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 tests/Tgstation.Server.Host.Tests/IO/TestPostWriteHandler.cs diff --git a/tests/Tgstation.Server.Host.Tests/IO/TestPostWriteHandler.cs b/tests/Tgstation.Server.Host.Tests/IO/TestPostWriteHandler.cs new file mode 100644 index 0000000000..7dd1ce45f2 --- /dev/null +++ b/tests/Tgstation.Server.Host.Tests/IO/TestPostWriteHandler.cs @@ -0,0 +1,85 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Mono.Unix; +using System; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using System.Text; + +namespace Tgstation.Server.Host.IO.Tests +{ + [TestClass] + public sealed class TestPostWriteHandler + { + [TestMethod] + public void TestThrowsWithNullArg() + { + IPostWriteHandler postWriteHandler; + var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + if (isWindows) + postWriteHandler = new WindowsPostWriteHandler(); + else + postWriteHandler = new PosixPostWriteHandler(); + + Assert.ThrowsException(() => postWriteHandler.HandleWrite(null)); + } + + [TestMethod] + public void TestPostWrite() + { + IPostWriteHandler postWriteHandler; + var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + if (isWindows) + postWriteHandler = new WindowsPostWriteHandler(); + else + postWriteHandler = new PosixPostWriteHandler(); + + //test on a valid file first + var tmpFile = Path.GetTempFileName(); + try + { + postWriteHandler.HandleWrite(tmpFile); + + if (isWindows) + return; //you do nothing + + //ensure it is now executable + File.WriteAllBytes(tmpFile, Encoding.UTF8.GetBytes("#!/bin/sh\n")); + + using (var process = Process.Start(tmpFile)) + { + process.WaitForExit(); + Assert.AreEqual(0, process.ExitCode); + } + + //run it again for the code coverage on that part where no changes are made if it's already executable + postWriteHandler.HandleWrite(tmpFile); + } + finally + { + File.Delete(tmpFile); + } + } + + [TestMethod] + public void TestThrowsOnUnix() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + return; + var postWriteHandler = new PosixPostWriteHandler(); + var tmpFile = Path.GetTempFileName(); + File.Delete(tmpFile); + Assert.ThrowsException(() => postWriteHandler.HandleWrite(tmpFile)); + Directory.CreateDirectory(tmpFile); + try + { + //can't +x a directory ;) (taps head) + Assert.ThrowsException(() => postWriteHandler.HandleWrite(tmpFile)); + } + finally + { + File.Delete(tmpFile); + } + } + } +} From 018f9b4914e6b56236621ed7fbb34c6d03dace4e Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Fri, 2 Nov 2018 15:22:22 -0400 Subject: [PATCH 32/51] Remove stupidity for the PosixByondInstaller.DownloadVersion --- .../Components/Byond/PosixByondInstaller.cs | 21 ++----------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/src/Tgstation.Server.Host/Components/Byond/PosixByondInstaller.cs b/src/Tgstation.Server.Host/Components/Byond/PosixByondInstaller.cs index 5bf642cdcb..c073c6d4f5 100644 --- a/src/Tgstation.Server.Host/Components/Byond/PosixByondInstaller.cs +++ b/src/Tgstation.Server.Host/Components/Byond/PosixByondInstaller.cs @@ -82,26 +82,9 @@ namespace Tgstation.Server.Host.Components.Byond /// public async Task DownloadVersion(Version version, CancellationToken cancellationToken) { - var ourVersion = version; - //lummox is annoying and doesn't like to post linux versions if nothing changed in DreamDaemon/DM - //if this for's exit condition ever triggers, i get to say I told you so - Exception lastException = null; - for (var I = 0; I < 5 && ourVersion.Minor >= 1; ++I, ourVersion = new Version(ourVersion.Major, ourVersion.Minor)) - { - try - { - var url = String.Format(CultureInfo.InvariantCulture, ByondRevisionsURL, ourVersion.Major, ourVersion.Minor); + var url = String.Format(CultureInfo.InvariantCulture, ByondRevisionsURL, version.Major, version.Minor); - return await ioManager.DownloadFile(new Uri(url), cancellationToken).ConfigureAwait(false); - } - catch (WebException e) - { - if (!(e.Status == WebExceptionStatus.ProtocolError && e.Response is HttpWebResponse response && response.StatusCode == HttpStatusCode.NotFound)) - throw; - lastException = e; - } - } - throw lastException; + return await ioManager.DownloadFile(new Uri(url), cancellationToken).ConfigureAwait(false); } /// From 6e4157c58c931288e567e1b24938212180634fe8 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Fri, 2 Nov 2018 15:22:48 -0400 Subject: [PATCH 33/51] Add missing ArgumentNullExceptions to PosixByondInstaller --- .../Components/Byond/PosixByondInstaller.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Tgstation.Server.Host/Components/Byond/PosixByondInstaller.cs b/src/Tgstation.Server.Host/Components/Byond/PosixByondInstaller.cs index c073c6d4f5..3c25ac9175 100644 --- a/src/Tgstation.Server.Host/Components/Byond/PosixByondInstaller.cs +++ b/src/Tgstation.Server.Host/Components/Byond/PosixByondInstaller.cs @@ -82,6 +82,9 @@ namespace Tgstation.Server.Host.Components.Byond /// public async Task DownloadVersion(Version version, CancellationToken cancellationToken) { + if (version == null) + throw new ArgumentNullException(nameof(version)); + var url = String.Format(CultureInfo.InvariantCulture, ByondRevisionsURL, version.Major, version.Minor); return await ioManager.DownloadFile(new Uri(url), cancellationToken).ConfigureAwait(false); @@ -90,6 +93,11 @@ namespace Tgstation.Server.Host.Components.Byond /// public Task InstallByond(string path, Version version, CancellationToken cancellationToken) { + if (path == null) + throw new ArgumentNullException(nameof(path)); + if (version == null) + throw new ArgumentNullException(nameof(version)); + //write the scripts for running the ting //need to add $ORIGIN to LD_LIBRARY_PATH const string StandardScript = "#!/bin/sh\nexport LD_LIBRARY_PATH=\"\\$ORIGIN:$LD_LIBRARY_PATH\"\nBASEDIR=$(dirname \"$0\")\nexec \"$BASEDIR/{0}\" \"$@\"\n"; From 70e6aa0938437101f22fe7bc923ccabf6e191c43 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Fri, 2 Nov 2018 15:23:37 -0400 Subject: [PATCH 34/51] Fix postwriteahandler test --- tests/Tgstation.Server.Host.Tests/IO/TestPostWriteHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Tgstation.Server.Host.Tests/IO/TestPostWriteHandler.cs b/tests/Tgstation.Server.Host.Tests/IO/TestPostWriteHandler.cs index 7dd1ce45f2..bbd12837f8 100644 --- a/tests/Tgstation.Server.Host.Tests/IO/TestPostWriteHandler.cs +++ b/tests/Tgstation.Server.Host.Tests/IO/TestPostWriteHandler.cs @@ -78,7 +78,7 @@ namespace Tgstation.Server.Host.IO.Tests } finally { - File.Delete(tmpFile); + Directory.Delete(tmpFile); } } } From a0a09dd1feb13f3766b882fc2382a8ff7c78d244 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Fri, 2 Nov 2018 15:29:46 -0400 Subject: [PATCH 35/51] Give up on testing the chmod throw --- .../IO/TestPostWriteHandler.cs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/tests/Tgstation.Server.Host.Tests/IO/TestPostWriteHandler.cs b/tests/Tgstation.Server.Host.Tests/IO/TestPostWriteHandler.cs index bbd12837f8..7a7f1b4715 100644 --- a/tests/Tgstation.Server.Host.Tests/IO/TestPostWriteHandler.cs +++ b/tests/Tgstation.Server.Host.Tests/IO/TestPostWriteHandler.cs @@ -70,16 +70,6 @@ namespace Tgstation.Server.Host.IO.Tests var tmpFile = Path.GetTempFileName(); File.Delete(tmpFile); Assert.ThrowsException(() => postWriteHandler.HandleWrite(tmpFile)); - Directory.CreateDirectory(tmpFile); - try - { - //can't +x a directory ;) (taps head) - Assert.ThrowsException(() => postWriteHandler.HandleWrite(tmpFile)); - } - finally - { - Directory.Delete(tmpFile); - } } } } From 07557691c8df293e3428631e85cfe0aa50120e13 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Fri, 2 Nov 2018 15:58:38 -0400 Subject: [PATCH 36/51] Add TestPosixByondInstaller --- .../Byond/TestPosixByondInstaller.cs | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 tests/Tgstation.Server.Host.Tests/Components/Byond/TestPosixByondInstaller.cs diff --git a/tests/Tgstation.Server.Host.Tests/Components/Byond/TestPosixByondInstaller.cs b/tests/Tgstation.Server.Host.Tests/Components/Byond/TestPosixByondInstaller.cs new file mode 100644 index 0000000000..e2fb2cff0f --- /dev/null +++ b/tests/Tgstation.Server.Host.Tests/Components/Byond/TestPosixByondInstaller.cs @@ -0,0 +1,95 @@ +using Microsoft.Extensions.Logging; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using System; +using System.Threading.Tasks; +using Tgstation.Server.Host.IO; + +namespace Tgstation.Server.Host.Components.Byond.Tests +{ + [TestClass] + public sealed class TestPosixByondInstaller + { + [TestMethod] + public void TestConstruction() + { + Assert.ThrowsException(() => new PosixByondInstaller(null, null, null)); + var mockIOManager = new Mock(); + Assert.ThrowsException(() => new PosixByondInstaller(mockIOManager.Object, null, null)); + var mockPostWriteHandler = new Mock(); + Assert.ThrowsException(() => new PosixByondInstaller(mockIOManager.Object, mockPostWriteHandler.Object, null)); + + var mockLogger = new Mock>(); + new PosixByondInstaller(mockIOManager.Object, mockPostWriteHandler.Object, mockLogger.Object); + } + + [TestMethod] + public async Task TestCacheClean() + { + var mockIOManager = new Mock(); + var mockPostWriteHandler = new Mock(); + var mockLogger = new Mock>(); + var installer = new PosixByondInstaller(mockIOManager.Object, mockPostWriteHandler.Object, mockLogger.Object); + + const string ByondCachePath = "~/.byond/cache"; + + mockIOManager.Setup(x => x.DeleteDirectory(ByondCachePath, default)).Returns(Task.CompletedTask).Verifiable(); + + await installer.CleanCache(default); + + mockIOManager.Verify(); + + + mockIOManager.Setup(x => x.DeleteDirectory(ByondCachePath, default)).Throws(new OperationCanceledException()).Verifiable(); + + await Assert.ThrowsExceptionAsync(() => installer.CleanCache(default)).ConfigureAwait(false); + + mockIOManager.Verify(); + + mockIOManager.Setup(x => x.DeleteDirectory(ByondCachePath, default)).Throws(new Exception()).Verifiable(); + + await installer.CleanCache(default).ConfigureAwait(false); + + mockIOManager.Verify(); + } + + + [TestMethod] + public async Task TestDownload() + { + var mockIOManager = new Mock(); + var mockPostWriteHandler = new Mock(); + var mockLogger = new Mock>(); + var installer = new PosixByondInstaller(mockIOManager.Object, mockPostWriteHandler.Object, mockLogger.Object); + + await Assert.ThrowsExceptionAsync(() => installer.DownloadVersion(null, default)).ConfigureAwait(false); + + var ourArray = Array.Empty(); + mockIOManager.Setup(x => x.DownloadFile(It.Is(uri => uri == new Uri("https://secure.byond.com/download/build/511/511.1385_byond_linux.zip")), default)).Returns(Task.FromResult(ourArray)).Verifiable(); + + var result = await installer.DownloadVersion(new Version(511, 1385), default).ConfigureAwait(false); + + Assert.AreSame(ourArray, result); + mockIOManager.Verify(); + } + + [TestMethod] + public async Task TestInstallByond() + { + var mockIOManager = new Mock(); + var mockPostWriteHandler = new Mock(); + var mockLogger = new Mock>(); + var installer = new PosixByondInstaller(mockIOManager.Object, mockPostWriteHandler.Object, mockLogger.Object); + + const string FakePath = "fake"; + await Assert.ThrowsExceptionAsync(() => installer.InstallByond(null, null, default)).ConfigureAwait(false); + await Assert.ThrowsExceptionAsync(() => installer.InstallByond(FakePath, null, default)).ConfigureAwait(false); + + await installer.InstallByond(FakePath, new Version(511, 1385), default).ConfigureAwait(false); + + mockIOManager.Verify(x => x.ConcatPath(It.IsAny(), It.IsNotNull()), Times.Exactly(5)); + mockIOManager.Verify(x => x.WriteAllBytes(It.IsAny(), It.IsNotNull(), default), Times.Exactly(2)); + mockPostWriteHandler.Verify(x => x.HandleWrite(It.IsAny()), Times.Exactly(4)); + } + } +} From d15f3f1caaca91a0d044a82321741380d9259dba Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Sat, 24 Nov 2018 14:09:41 -0500 Subject: [PATCH 37/51] Fix reattach information port change not happening if port change was instant --- .../Components/Watchdog/SessionController.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Tgstation.Server.Host/Components/Watchdog/SessionController.cs b/src/Tgstation.Server.Host/Components/Watchdog/SessionController.cs index 08decc1996..380b18001d 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/SessionController.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/SessionController.cs @@ -481,6 +481,7 @@ namespace Tgstation.Server.Host.Components.Watchdog return false; } + reattachInformation.Port = port; return true; } From cbb1916aeb906c75d47bff2aceee72661937a5c8 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Sat, 24 Nov 2018 14:26:39 -0500 Subject: [PATCH 38/51] Fix custom chat parameters --- src/Tgstation.Server.Host/Components/Interop/ChatCommand.cs | 2 +- src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Tgstation.Server.Host/Components/Interop/ChatCommand.cs b/src/Tgstation.Server.Host/Components/Interop/ChatCommand.cs index 1c49476b35..a8cc05feba 100644 --- a/src/Tgstation.Server.Host/Components/Interop/ChatCommand.cs +++ b/src/Tgstation.Server.Host/Components/Interop/ChatCommand.cs @@ -5,7 +5,7 @@ namespace Tgstation.Server.Host.Components.Interop sealed class ChatCommand { public string Command { get; set; } - public string Parameters { get; set; } + public string Params { get; set; } public User User { get; set; } } } diff --git a/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs b/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs index 0302b24fce..c0dff683b8 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs @@ -1059,7 +1059,7 @@ namespace Tgstation.Server.Host.Components.Watchdog var commandObject = new ChatCommand { Command = commandName, - Parameters = arguments, + Params = arguments, User = sender }; From 5f1b807541736f99c27a3fdfd6cc0e57a9569ecb Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Sat, 24 Nov 2018 14:57:39 -0500 Subject: [PATCH 39/51] Fix issues with chat send commands --- src/Tgstation.Server.Host/Components/Chat/Channel.cs | 4 ++-- src/Tgstation.Server.Host/Components/Chat/Chat.cs | 8 ++++---- .../Components/Chat/Commands/ICommand.cs | 2 +- .../Components/Chat/Providers/DiscordProvider.cs | 6 +++--- .../Components/Chat/Providers/IrcProvider.cs | 6 +++--- .../Components/Interop/CommCommand.cs | 2 +- .../Components/Interop/CommContext.cs | 2 +- .../Components/Watchdog/SessionController.cs | 4 ++-- 8 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/Tgstation.Server.Host/Components/Chat/Channel.cs b/src/Tgstation.Server.Host/Components/Chat/Channel.cs index 131ce67962..fe5d6acb15 100644 --- a/src/Tgstation.Server.Host/Components/Chat/Channel.cs +++ b/src/Tgstation.Server.Host/Components/Chat/Channel.cs @@ -38,12 +38,12 @@ namespace Tgstation.Server.Host.Components.Chat /// /// If this is considered a channel for admin commands /// - public bool IsAdmin { get; set; } + public bool IsAdminChannel { get; set; } /// /// If this is a 1-to-1 chat channel /// - public bool IsPrivate { get; set; } + public bool IsPrivateChannel { get; set; } /// /// For user use diff --git a/src/Tgstation.Server.Host/Components/Chat/Chat.cs b/src/Tgstation.Server.Host/Components/Chat/Chat.cs index b4d68b1139..fc7321af84 100644 --- a/src/Tgstation.Server.Host/Components/Chat/Chat.cs +++ b/src/Tgstation.Server.Host/Components/Chat/Chat.cs @@ -195,7 +195,7 @@ namespace Tgstation.Server.Host.Components.Chat { var providerId = providers.Where(x => x.Value == provider).Select(x => x.Key).First(); var enumerable = mappedChannels.Where(x => x.Value.ProviderId == providerId && x.Value.ProviderChannelId == message.User.Channel.RealId); - if (message.User.Channel.IsPrivate) + if (message.User.Channel.IsPrivateChannel) lock (mappedChannels) { if (!provider.Connected) @@ -223,7 +223,7 @@ namespace Tgstation.Server.Host.Components.Chat var mapping = enumerable.First().Value; message.User.Channel.Id = mapping.Channel.Id; message.User.Channel.Tag = mapping.Channel.Tag; - message.User.Channel.IsAdmin = mapping.Channel.IsAdmin; + message.User.Channel.IsAdminChannel = mapping.Channel.IsAdminChannel; } } @@ -236,7 +236,7 @@ namespace Tgstation.Server.Host.Components.Chat var addressed = address == CommonMention.ToUpperInvariant() || address == provider.BotMention.ToUpperInvariant(); - if (!addressed && !message.User.Channel.IsPrivate) + if (!addressed && !message.User.Channel.IsPrivateChannel) //no mention return; @@ -302,7 +302,7 @@ namespace Tgstation.Server.Host.Components.Chat return; } - if (commandHandler.AdminOnly && !message.User.Channel.IsAdmin) + if (commandHandler.AdminOnly && !message.User.Channel.IsAdminChannel) { await SendMessage("Use this command in an admin channel!", new List { message.User.Channel.RealId }, cancellationToken).ConfigureAwait(false); return; diff --git a/src/Tgstation.Server.Host/Components/Chat/Commands/ICommand.cs b/src/Tgstation.Server.Host/Components/Chat/Commands/ICommand.cs index 8694606731..4ff8799ce7 100644 --- a/src/Tgstation.Server.Host/Components/Chat/Commands/ICommand.cs +++ b/src/Tgstation.Server.Host/Components/Chat/Commands/ICommand.cs @@ -19,7 +19,7 @@ namespace Tgstation.Server.Host.Components.Chat.Commands string HelpText { get; } /// - /// If the command should only be available to s who's has set + /// If the command should only be available to s who's has set /// bool AdminOnly { get; } diff --git a/src/Tgstation.Server.Host/Components/Chat/Providers/DiscordProvider.cs b/src/Tgstation.Server.Host/Components/Chat/Providers/DiscordProvider.cs index 711454887c..50996ed74a 100644 --- a/src/Tgstation.Server.Host/Components/Chat/Providers/DiscordProvider.cs +++ b/src/Tgstation.Server.Host/Components/Chat/Providers/DiscordProvider.cs @@ -97,7 +97,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers Channel = new Channel { RealId = e.Channel.Id, - IsPrivate = pm, + IsPrivateChannel = pm, ConnectionName = pm ? e.Author.Username : (e.Channel as ITextChannel)?.Guild.Name ?? "UNKNOWN", FriendlyName = e.Channel.Name //isAdmin and Tag populated by manager @@ -188,10 +188,10 @@ namespace Tgstation.Server.Host.Components.Chat.Providers return new Channel { RealId = discordChannel.Id, - IsAdmin = channel.IsAdminChannel == true, + IsAdminChannel = channel.IsAdminChannel == true, ConnectionName = discordChannel.Guild.Name, FriendlyName = discordChannel.Name, - IsPrivate = false, + IsPrivateChannel = false, Tag = channel.Tag }; }; diff --git a/src/Tgstation.Server.Host/Components/Chat/Providers/IrcProvider.cs b/src/Tgstation.Server.Host/Components/Chat/Providers/IrcProvider.cs index cd08c1760e..01fa89697e 100644 --- a/src/Tgstation.Server.Host/Components/Chat/Providers/IrcProvider.cs +++ b/src/Tgstation.Server.Host/Components/Chat/Providers/IrcProvider.cs @@ -204,7 +204,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers ConnectionName = address, FriendlyName = isPrivate ? String.Format(CultureInfo.InvariantCulture, "PM: {0}", channelName) : channelName, RealId = channelId, - IsPrivate = isPrivate + IsPrivateChannel = isPrivate //isAdmin and Tag populated by manager }, FriendlyName = username, @@ -393,10 +393,10 @@ namespace Tgstation.Server.Host.Components.Chat.Providers return new Channel { RealId = id.Value, - IsAdmin = x.IsAdminChannel == true, + IsAdminChannel = x.IsAdminChannel == true, ConnectionName = address, FriendlyName = channelIdMap[id.Value], - IsPrivate = false, + IsPrivateChannel = false, Tag = x.Tag }; }).ToList(); diff --git a/src/Tgstation.Server.Host/Components/Interop/CommCommand.cs b/src/Tgstation.Server.Host/Components/Interop/CommCommand.cs index 7145d35aae..31b4715897 100644 --- a/src/Tgstation.Server.Host/Components/Interop/CommCommand.cs +++ b/src/Tgstation.Server.Host/Components/Interop/CommCommand.cs @@ -10,7 +10,7 @@ namespace Tgstation.Server.Host.Components.Interop /// /// The dictionary of the /// - public IReadOnlyDictionary Parameters { get; set; } + public IReadOnlyDictionary Parameters { get; set; } /// /// The raw JSON of the diff --git a/src/Tgstation.Server.Host/Components/Interop/CommContext.cs b/src/Tgstation.Server.Host/Components/Interop/CommContext.cs index f9c5208d88..e910ad7ffa 100644 --- a/src/Tgstation.Server.Host/Components/Interop/CommContext.cs +++ b/src/Tgstation.Server.Host/Components/Interop/CommContext.cs @@ -109,7 +109,7 @@ namespace Tgstation.Server.Host.Components.Interop { command = new CommCommand { - Parameters = JsonConvert.DeserializeObject>(file), + Parameters = JsonConvert.DeserializeObject>(file), RawJson = file }; } diff --git a/src/Tgstation.Server.Host/Components/Watchdog/SessionController.cs b/src/Tgstation.Server.Host/Components/Watchdog/SessionController.cs index 8cd90b5b72..e36ecedd0e 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/SessionController.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/SessionController.cs @@ -316,7 +316,7 @@ namespace Tgstation.Server.Host.Components.Watchdog case Constants.DMCommandNewPort: lock (this) { - if (!query.TryGetValue(Constants.DMParameterData, out var stringPort) || !UInt16.TryParse(stringPort, out var currentPort)) + if (!query.TryGetValue(Constants.DMParameterData, out var stringPortObject) || !UInt16.TryParse(stringPortObject as string, out var currentPort)) { /////UHHHH logger.LogWarning("DreamDaemon sent new port command without providing it's own!"); @@ -354,7 +354,7 @@ namespace Tgstation.Server.Host.Components.Watchdog content = new ErrorMessage { Message = "Invalid API validation request!" }; break; } - if (!query.TryGetValue(Constants.DMParameterData, out var stringMinimumSecurityLevel) || !Enum.TryParse(stringMinimumSecurityLevel, out var minimumSecurityLevel)) + if (!query.TryGetValue(Constants.DMParameterData, out var stringMinimumSecurityLevelObject) || !Enum.TryParse(stringMinimumSecurityLevelObject as string, out var minimumSecurityLevel)) apiValidationStatus = ApiValidationStatus.BadValidationRequest; else switch (minimumSecurityLevel) From 92ffd941581defcb0740db9edeab666eaeef6563 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Sat, 24 Nov 2018 15:47:16 -0500 Subject: [PATCH 40/51] Add support for backwards migrations --- .../Components/InstanceManager.cs | 25 +++++++++- .../Models/DatabaseContext.cs | 50 +++++++++++++++++++ .../Models/IDatabaseContext.cs | 9 ++++ .../Models/MySqlDatabaseContext.cs | 3 ++ .../Models/SqlServerDatabaseContext.cs | 3 ++ 5 files changed, 88 insertions(+), 2 deletions(-) diff --git a/src/Tgstation.Server.Host/Components/InstanceManager.cs b/src/Tgstation.Server.Host/Components/InstanceManager.cs index 88a2286d47..b9b558cba2 100644 --- a/src/Tgstation.Server.Host/Components/InstanceManager.cs +++ b/src/Tgstation.Server.Host/Components/InstanceManager.cs @@ -12,7 +12,7 @@ using Tgstation.Server.Host.IO; namespace Tgstation.Server.Host.Components { /// - sealed class InstanceManager : IInstanceManager, IHostedService, IDisposable + sealed class InstanceManager : IInstanceManager, IRestartHandler, IHostedService, IDisposable { /// /// The for the @@ -49,6 +49,11 @@ namespace Tgstation.Server.Host.Components /// readonly Dictionary instances; + /// + /// Used in to determine if database downgrades must be made + /// + Version downgradeVersion; + /// /// If the has been d /// @@ -62,16 +67,21 @@ namespace Tgstation.Server.Host.Components /// The value of /// The value of /// The value of + /// The used to register the as a /// The value of - public InstanceManager(IInstanceFactory instanceFactory, IIOManager ioManager, IDatabaseContextFactory databaseContextFactory, IApplication application, IJobManager jobManager, ILogger logger) + public InstanceManager(IInstanceFactory instanceFactory, IIOManager ioManager, IDatabaseContextFactory databaseContextFactory, IApplication application, IJobManager jobManager, IServerControl serverControl, ILogger logger) { this.instanceFactory = instanceFactory ?? throw new ArgumentNullException(nameof(instanceFactory)); this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager)); this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory)); this.application = application ?? throw new ArgumentNullException(nameof(application)); this.jobManager = jobManager ?? throw new ArgumentNullException(nameof(jobManager)); + if (serverControl == null) + throw new ArgumentNullException(nameof(serverControl)); this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); + serverControl.RegisterForRestart(this); + instances = new Dictionary(); } @@ -225,6 +235,17 @@ namespace Tgstation.Server.Host.Components await jobManager.StopAsync(cancellationToken).ConfigureAwait(false); await Task.WhenAll(instances.Select(x => x.Value.StopAsync(cancellationToken))).ConfigureAwait(false); await instanceFactory.StopAsync(cancellationToken).ConfigureAwait(false); + + //downgrade the db if necessary + if (downgradeVersion != null) + await databaseContextFactory.UseContext(db => db.SchemaDowngradeForServerVersion(downgradeVersion, cancellationToken)).ConfigureAwait(false); + } + + /// + public Task HandleRestart(Version updateVersion, CancellationToken cancellationToken) + { + downgradeVersion = updateVersion != null && updateVersion < application.Version ? updateVersion : null; + return Task.CompletedTask; } } } diff --git a/src/Tgstation.Server.Host/Models/DatabaseContext.cs b/src/Tgstation.Server.Host/Models/DatabaseContext.cs index 7578c21c8a..e113f1d999 100644 --- a/src/Tgstation.Server.Host/Models/DatabaseContext.cs +++ b/src/Tgstation.Server.Host/Models/DatabaseContext.cs @@ -1,7 +1,11 @@ using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using System; +using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -169,5 +173,51 @@ namespace Tgstation.Server.Host.Models /// public Task Save(CancellationToken cancellationToken) => SaveChangesAsync(cancellationToken); + + /// + /// If the MY_ class of migrations should be used instead of the MS_ class + /// + /// if the MY_ class of migrations should be used instead of the MS_ class, otherwise + protected abstract bool UseMySQLMigrations(); + + /// + public async Task SchemaDowngradeForServerVersion(Version version, CancellationToken cancellationToken) + { + if (version == null) + throw new ArgumentNullException(nameof(version)); + if (version < new Version(4, 0)) + throw new ArgumentOutOfRangeException(nameof(version), version, "Not a valid V4 version!"); + + string targetMigration = null; + + //Update this with new migrations as they are made + //Always use the MS class + + //TODO: Uncomment once #816 is merged + /* + if (version < new Version(4, 0, 2)) + targetMigration = nameof(MSReattachCompileJobRequired); + */ + + if (targetMigration == null) + return; + + if (UseMySQLMigrations()) + targetMigration = String.Format(CultureInfo.InvariantCulture, "MY" + targetMigration.Substring(2)); + + //even though it clearly implements it in the DatabaseFacade definition this won't work without casting (╯ಠ益ಠ)╯︵ ┻━┻ + var dbServiceProvider = ((IInfrastructure)Database).Instance; + var migrator = dbServiceProvider.GetRequiredService(); + + Logger.LogInformation("Migrating down to version {0}. Target: {1}", version, targetMigration); + try + { + await migrator.MigrateAsync(targetMigration, cancellationToken).ConfigureAwait(false); + } + catch (Exception e) + { + Logger.LogCritical("Failed to migrate! Exception: {0}", e); + } + } } } diff --git a/src/Tgstation.Server.Host/Models/IDatabaseContext.cs b/src/Tgstation.Server.Host/Models/IDatabaseContext.cs index 8fdb8aad74..e7c8e9dcd2 100644 --- a/src/Tgstation.Server.Host/Models/IDatabaseContext.cs +++ b/src/Tgstation.Server.Host/Models/IDatabaseContext.cs @@ -1,4 +1,5 @@ using Microsoft.EntityFrameworkCore; +using System; using System.Threading; using System.Threading.Tasks; @@ -87,5 +88,13 @@ namespace Tgstation.Server.Host.Models /// The for the operation /// A representing the running operation Task Initialize(CancellationToken cancellationToken); + + /// + /// Attempt to downgrade the schema to the migration used for a given server + /// + /// The tgstation-server that the schema should downgrade for + /// The for the operation + /// A representing the running operation + Task SchemaDowngradeForServerVersion(Version version, CancellationToken cancellationToken); } } diff --git a/src/Tgstation.Server.Host/Models/MySqlDatabaseContext.cs b/src/Tgstation.Server.Host/Models/MySqlDatabaseContext.cs index 8c03b31ee0..718fc04d98 100644 --- a/src/Tgstation.Server.Host/Models/MySqlDatabaseContext.cs +++ b/src/Tgstation.Server.Host/Models/MySqlDatabaseContext.cs @@ -38,5 +38,8 @@ namespace Tgstation.Server.Host.Models else options.UseMySql(DatabaseConfiguration.ConnectionString); } + + /// + protected override bool UseMySQLMigrations() => true; } } diff --git a/src/Tgstation.Server.Host/Models/SqlServerDatabaseContext.cs b/src/Tgstation.Server.Host/Models/SqlServerDatabaseContext.cs index d3d5aa8260..94240566d3 100644 --- a/src/Tgstation.Server.Host/Models/SqlServerDatabaseContext.cs +++ b/src/Tgstation.Server.Host/Models/SqlServerDatabaseContext.cs @@ -26,5 +26,8 @@ namespace Tgstation.Server.Host.Models base.OnConfiguring(options); options.UseSqlServer(DatabaseConfiguration.ConnectionString); } + + /// + protected override bool UseMySQLMigrations() => false; } } From f8e39cef5c26576076ef00619f253f1b92df659d Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Sat, 24 Nov 2018 15:55:42 -0500 Subject: [PATCH 41/51] Version bump to 4.0.1.4 --- .../Tgstation.Server.Host.Console.csproj | 2 +- src/Tgstation.Server.Host.Service/Properties/AssemblyInfo.cs | 4 ++-- .../Tgstation.Server.Host.Watchdog.csproj | 2 +- src/Tgstation.Server.Host/Tgstation.Server.Host.csproj | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Tgstation.Server.Host.Console/Tgstation.Server.Host.Console.csproj b/src/Tgstation.Server.Host.Console/Tgstation.Server.Host.Console.csproj index bb659a9233..d181d5f605 100644 --- a/src/Tgstation.Server.Host.Console/Tgstation.Server.Host.Console.csproj +++ b/src/Tgstation.Server.Host.Console/Tgstation.Server.Host.Console.csproj @@ -4,7 +4,7 @@ Exe netcoreapp2.1 Full - 4.0.1.3 + 4.0.1.4 diff --git a/src/Tgstation.Server.Host.Service/Properties/AssemblyInfo.cs b/src/Tgstation.Server.Host.Service/Properties/AssemblyInfo.cs index 88bf8ee48f..2b77d018db 100644 --- a/src/Tgstation.Server.Host.Service/Properties/AssemblyInfo.cs +++ b/src/Tgstation.Server.Host.Service/Properties/AssemblyInfo.cs @@ -7,5 +7,5 @@ using System.Runtime.InteropServices; [assembly: Guid("29927416-3b78-49a7-a560-5ccaa638b6b4")] [assembly: InternalsVisibleTo("Tgstation.Server.Host.Service.Tests")] -[assembly: AssemblyVersion("4.0.1.3")] -[assembly: AssemblyFileVersion("4.0.1.3")] +[assembly: AssemblyVersion("4.0.1.4")] +[assembly: AssemblyFileVersion("4.0.1.4")] diff --git a/src/Tgstation.Server.Host.Watchdog/Tgstation.Server.Host.Watchdog.csproj b/src/Tgstation.Server.Host.Watchdog/Tgstation.Server.Host.Watchdog.csproj index 2d3d915541..ec75cc5b0d 100644 --- a/src/Tgstation.Server.Host.Watchdog/Tgstation.Server.Host.Watchdog.csproj +++ b/src/Tgstation.Server.Host.Watchdog/Tgstation.Server.Host.Watchdog.csproj @@ -4,7 +4,7 @@ netstandard2.0 Full false - 4.0.1.3 + 4.0.1.4 diff --git a/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj b/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj index edb20c1711..a52ca184ad 100644 --- a/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj +++ b/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj @@ -3,7 +3,7 @@ netcoreapp2.1 Full - 4.0.1.3 + 4.0.1.4 From 4c0dbf08f7f8f12dbaa939c6a1ec52438e288670 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Mon, 26 Nov 2018 14:24:21 -0500 Subject: [PATCH 42/51] Script for automatic release notes --- .gitignore | 1 + tools/ReleaseNotes/Program.cs | 316 +++++++++++++++++++++++++ tools/ReleaseNotes/README.md | 7 + tools/ReleaseNotes/ReleaseNotes.csproj | 13 + tools/ReleaseNotes/release_notes.md | 17 ++ 5 files changed, 354 insertions(+) create mode 100644 tools/ReleaseNotes/Program.cs create mode 100644 tools/ReleaseNotes/README.md create mode 100644 tools/ReleaseNotes/ReleaseNotes.csproj create mode 100644 tools/ReleaseNotes/release_notes.md diff --git a/.gitignore b/.gitignore index 20bc8fcd5a..8b0b626b5c 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ artifacts/ /tests/DMAPI/travistester.int /tests/DMAPI/travistester.dmb /src/Tgstation.Server.Host/appsettings.Development.json +/tools/ReleaseNotes/release_nodes.md diff --git a/tools/ReleaseNotes/Program.cs b/tools/ReleaseNotes/Program.cs new file mode 100644 index 0000000000..b0219fad6f --- /dev/null +++ b/tools/ReleaseNotes/Program.cs @@ -0,0 +1,316 @@ +using Octokit; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ReleaseNotes +{ + class Program + { + static async Task Main(string[] args) + { + if (args.Length < 1) + { + Console.WriteLine("Missing version argument!"); + return 1; + } + + var versionString = args[0]; + if (!Version.TryParse(versionString, out var version)) + { + Console.WriteLine("Invalid version: " + versionString); + return 2; + } + + var doNotCloseMilestone = args.Length >= 2 && args[1].ToUpperInvariant() == "--NO-CLOSE"; + + const string ReleaseNotesEnvVar = "TGS_RELEASE_NOTES_TOKEN"; + var githubToken = Environment.GetEnvironmentVariable(ReleaseNotesEnvVar); + if (String.IsNullOrWhiteSpace(githubToken)) + { + Console.WriteLine("Missing " + ReleaseNotesEnvVar + " environment variable!"); + return 3; + } + + try + { + var client = new GitHubClient(new ProductHeaderValue("tgs_release_notes")); + client.Credentials = new Credentials(githubToken); + + const string RepoOwner = "tgstation"; + const string RepoName = "tgstation-server"; + + var releasesTask = client.Repository.Release.GetAll(RepoOwner, RepoName); + + Console.WriteLine("Getting pull requests in milestone " + versionString + "..."); + var milestonePRs = await client.Search.SearchIssues(new SearchIssuesRequest + { + Milestone = versionString, + Type = IssueTypeQualifier.PullRequest, + Repos = { { RepoOwner, RepoName } } + }).ConfigureAwait(false); + + if (milestonePRs.IncompleteResults) + { + Console.WriteLine("Incomplete results for milestone PRs query!"); + return 5; + } + Console.WriteLine(milestonePRs.Items.Count + " total pull requests"); + + Task milestoneTask = null; + var milestoneTaskLock = new object(); + var releaseDictionary = new Dictionary>(); + var authorizedUsers = new Dictionary>(); + + async Task GetReleaseNotesFromPR(Issue pullRequest) + { + PullRequest fullPR = null; + async Task GetFullPR() + { + if (fullPR != null) + return; + fullPR = await client.Repository.PullRequest.Get(RepoOwner, RepoName, pullRequest.Number).ConfigureAwait(false); + }; + + async Task GetMilestone() + { + await GetFullPR().ConfigureAwait(false); + if (fullPR.Milestone == null) + return null; + return await client.Issue.Milestone.Get(RepoOwner, RepoName, fullPR.Milestone.Number); + }; + + if (pullRequest.State.Value == ItemState.Closed) + { + //need to check it was merged + lock (milestoneTaskLock) + if (milestoneTask == null) + milestoneTask = GetMilestone(); + if (!fullPR.Merged) + return; + }; + + async Task BuildNotesFromComment(string comment, User user) + { + var commentSplits = comment.Split('\n'); + var notesOpen = false; + var notesClosed = false; + var notes = new List(); + foreach (var line in commentSplits) + { + var trimmedLine = line.Trim(); + if (!notesOpen) + { + notesOpen = trimmedLine.StartsWith(":cl:", StringComparison.Ordinal); + notesClosed = false; + continue; + } + if (trimmedLine.StartsWith("/:cl:", StringComparison.Ordinal)) + { + notesClosed = true; + notesOpen = false; + continue; + } + if (trimmedLine.Length == 0) + continue; + notes.Add(trimmedLine); + } + if (!notesClosed || notes.Count == 0) + return; + + Task authTask; + TaskCompletionSource ourTcs = null; + lock (authorizedUsers) + { + if (!authorizedUsers.TryGetValue(user.Id, out authTask)) + { + ourTcs = new TaskCompletionSource(); + authTask = ourTcs.Task; + authorizedUsers.Add(user.Id, authTask); + } + } + + if (ourTcs != null) + try + { + //check if the user has access + var perm = await client.Repository.Collaborator.ReviewPermission(RepoOwner, RepoName, user.Login).ConfigureAwait(false); + ourTcs.SetResult(perm.Permission == PermissionLevel.Write || perm.Permission == PermissionLevel.Admin); + } + catch + { + ourTcs.SetResult(false); + throw; + } + + var authorized = await authTask.ConfigureAwait(false); + if (!authorized) + return; + + lock (releaseDictionary) + { + foreach (var I in notes) + Console.WriteLine("#" + pullRequest.Number + " - " + I + " (@" + user.Login + ")"); + if (releaseDictionary.TryGetValue(pullRequest.Number, out var currentValues)) + currentValues.AddRange(notes); + else + releaseDictionary.Add(pullRequest.Number, notes); + } + } + + var comments = await client.Issue.Comment.GetAllForIssue(RepoOwner, RepoName, pullRequest.Number).ConfigureAwait(false); + await Task.WhenAll(BuildNotesFromComment(pullRequest.Body, pullRequest.User), Task.WhenAll(comments.Select(x => BuildNotesFromComment(x.Body, x.User)))).ConfigureAwait(false); + + lock (milestoneTaskLock) + if (milestoneTask == null) + milestoneTask = GetMilestone(); + } + + var tasks = new List(); + foreach (var I in milestonePRs.Items) + tasks.Add(GetReleaseNotesFromPR(I)); + + var releases = await releasesTask.ConfigureAwait(false); + + var releasingSuite = version.Major; + + Version highestReleaseVersion = null; + Release highestRelease = null; + foreach (var I in releases) + { + if (!Version.TryParse(I.TagName.Replace("tgstation-server-v", String.Empty), out var currentReleaseVersion)) + { + Console.WriteLine("WARNING: Unable to determine version of release " + I.HtmlUrl); + continue; + } + + if (currentReleaseVersion.Major == releasingSuite && (highestReleaseVersion == null || currentReleaseVersion > highestReleaseVersion)) + { + highestReleaseVersion = currentReleaseVersion; + highestRelease = I; + } + } + + if (highestReleaseVersion == null) + { + Console.WriteLine("Unable to determine highest release version for suite " + releasingSuite + "!"); + return 6; + } + + var oldNotes = highestRelease.Body; + + var splits = new List(oldNotes.Split('\n')); + //trim away all the lines that don't start with # + + string keepThisRelease; + if (version.Revision == 0) + if (version.Build == 0) + keepThisRelease = "# "; + else + keepThisRelease = "## "; + else + keepThisRelease = "### "; + + for (; !splits[0].StartsWith(keepThisRelease, StringComparison.Ordinal); splits.RemoveAt(0)) + if (splits.Count == 1) + { + Console.WriteLine("Error formatting release notes: Can't detemine notes start!"); + return 7; + } + + oldNotes = String.Join('\n', splits); + + string prefix; + switch (releasingSuite) + { + case 3: + prefix = "The /tg/station server suite"; + break; + default: + prefix = "See https://tgstation.github.io/tgstation-server for installation instructions"; + break; + } + + var newNotes = new StringBuilder(prefix); + newNotes.Append(Environment.NewLine); + newNotes.Append(Environment.NewLine); + if (version.Revision == 0) + if (version.Build == 0) + { + newNotes.Append("# [Version "); + newNotes.Append(version.Minor); + } + else + { + newNotes.Append("## [Changelog for "); + newNotes.Append(version.Build); + newNotes.Append(".x"); + } + else + { + newNotes.Append("### [Patch "); + newNotes.Append(version.Revision); + } + newNotes.Append("]("); + var milestone = await milestoneTask.ConfigureAwait(false); + if (milestone == null) + { + Console.WriteLine("Unable to detemine milestone!"); + return 9; + } + newNotes.Append(milestone.HtmlUrl); + newNotes.Append("?closed=1)"); + newNotes.Append(Environment.NewLine); + + await Task.WhenAll(tasks).ConfigureAwait(false); + + if (releaseDictionary.Count == 0) + { + Console.WriteLine("No release notes for this milestone!"); + return 8; + } + + foreach (var I in releaseDictionary) + foreach (var note in I.Value) + { + newNotes.Append(Environment.NewLine); + newNotes.Append("- "); + newNotes.Append(note); + newNotes.Append(" (#"); + newNotes.Append(I.Key); + newNotes.Append(')'); + } + + newNotes.Append(Environment.NewLine); + newNotes.Append(Environment.NewLine); + newNotes.Append(oldNotes); + + Console.WriteLine("Writing out new release notes..."); + var releaseNotes = newNotes.ToString(); + await File.WriteAllTextAsync("release_notes.md", releaseNotes).ConfigureAwait(false); + + if (doNotCloseMilestone) + Console.WriteLine("Not closing milestone due to parameter!"); + else + { + Console.WriteLine("Closing milestone..."); + await client.Issue.Milestone.Update(RepoOwner, RepoName, milestone.Number, new MilestoneUpdate + { + State = ItemState.Closed + }).ConfigureAwait(false); + } + + return 0; + } + catch (Exception e) + { + Console.WriteLine(e); + return 4; + } + } + } +} diff --git a/tools/ReleaseNotes/README.md b/tools/ReleaseNotes/README.md new file mode 100644 index 0000000000..63aa900ef3 --- /dev/null +++ b/tools/ReleaseNotes/README.md @@ -0,0 +1,7 @@ +This is a small tool to generate TGS release notes from PR descriptions + +Requires environment variable `TGS_RELEASE_NOTES_TOKEN` + +Run it with `dotnet run [--no-close (optionally doesn't close the milestone, USE WHILE DEBUGGING)]` + +Will close the release milestone and output `release_notes.md` with the updated release notes diff --git a/tools/ReleaseNotes/ReleaseNotes.csproj b/tools/ReleaseNotes/ReleaseNotes.csproj new file mode 100644 index 0000000000..64c79d0620 --- /dev/null +++ b/tools/ReleaseNotes/ReleaseNotes.csproj @@ -0,0 +1,13 @@ + + + + Exe + netcoreapp2.1 + latest + + + + + + + diff --git a/tools/ReleaseNotes/release_notes.md b/tools/ReleaseNotes/release_notes.md new file mode 100644 index 0000000000..25c9366336 --- /dev/null +++ b/tools/ReleaseNotes/release_notes.md @@ -0,0 +1,17 @@ +See https://tgstation.github.io/tgstation-server for installation instructions + +## [Changelog for 2.x](https://github.com/tgstation/tgstation-server/milestone/13?closed=1) + +- Added `/datum/tgs_version` to the DMAPI. `/world/TgsVersion()`, `/world/TgsMaximumAPIVersion()`, and `/world/TgsMinimumAPIVersion()` now return this datum (#827) + +## [Changelog for 1.X](https://github.com/tgstation/tgstation-server/milestone/11?closed=1) + +- Added the command line setup wizard. No more need to configure `appsettings.Production.json` manually +- You can now ignore files and folder for symlinking in the `GameStaticFiles` directory using the `.tgsignore` file +- If the repository is configured with valid GitHub synchronization credentials, the user account will now post [test merge comments](https://github.com/tgstation/tgstation/pull/40735#issuecomment-427503427) [when deployments involving them happen](https://github.com/tgstation/tgstation/pull/40736#issuecomment-427503428) +- Releases now contain a `.zip` of the latest DMAPI files +- Installing DirectX on Windows is no longer required for BYOND versions >= 512.1452 +- Updating the server should no longer timeout clients +- Various kinks were ironed out in internal systems + +# [Initial Release](https://github.com/tgstation/tgstation-server/milestone/3?closed=1) \ No newline at end of file From 1f5d14d047440b453aaa16875b88d0ed5bc3b4d7 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Mon, 26 Nov 2018 14:24:32 -0500 Subject: [PATCH 43/51] Test on CI --- appveyor.yml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index bb91fa6517..b77db8d363 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -6,6 +6,8 @@ environment: TGS4_TEST_CONNECTION_STRING: Server=(local)\SQL2017;Initial Catalog=TGS_Test;User ID=sa;Password=Password12! TGS4_TEST_GITHUB_TOKEN: secure: lJNGAXwiB5HlWdthz3K4PetqpTG5IEAyRgKaiKxFMQ8HW8CcOjRtB97B05op7BsK + TGS_RELEASE_NOTES_TOKEN: + secure: lJNGAXwiB5HlWdthz3K4PetqpTG5IEAyRgKaiKxFMQ8HW8CcOjRtB97B05op7BsK branches: only: - master @@ -80,14 +82,19 @@ after_test: - ps: $env:TGSVersion = [System.Diagnostics.FileVersionInfo]::GetVersionInfo("$env:APPVEYOR_BUILD_FOLDER/artifacts/ServerHost/Tgstation.Server.Host.dll").FileVersion - ps: if($env:APPVEYOR_REPO_COMMIT_MESSAGE -match "\[TGSDeploy\]"){ if($env:APPVEYOR_REPO_BRANCH -match "master"){ if($env:CONFIGURATION -match "Release"){ $env:TGSDeploy = "Do it." }}} - ps: if($env:APPVEYOR_REPO_COMMIT_MESSAGE -match "\[NugetDeploy\]"){ if($env:APPVEYOR_REPO_BRANCH -match "master"){ if($env:CONFIGURATION -match "Release"){ $env:NugetDeploy = "Do it." }}} + - ps: $env:TGSGoodNotes = (Start-Process -FilePath 'dotnet' -ArgumentList "run -p tools/ReleaseNotes 4.0.2.0 --no-close" -PassThru -Wait).ExitCode + - ps: $env:TGSReleaseNotes = [IO.File]::ReadAllText("tools/ReleaseNotes/release_notes.md") + - ps: Write-Host $env:TGSReleaseNotes + - ps: Write-Host $env:TGSGoodNotes deploy: - provider: GitHub release: "tgstation-server-v$(TGSVersion)" - description: 'The /tg/station server suite' + description: "$(TGSReleaseNotes)" auth_token: secure: lJNGAXwiB5HlWdthz3K4PetqpTG5IEAyRgKaiKxFMQ8HW8CcOjRtB97B05op7BsK artifact: ServerConsole,ServerService,ServerUpdatePackage,DMAPI - draft: true + draft: $(TGSGoodNotes) + prerelease: true on: TGSDeploy: "Do it." - provider: NuGet From ae880389fe4b38087c03f50dd9cae2acfb64d885 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Mon, 26 Nov 2018 14:43:22 -0500 Subject: [PATCH 44/51] Just use a script actually --- appveyor.yml | 4 +--- build/prep_deployment.ps1 | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 3 deletions(-) create mode 100644 build/prep_deployment.ps1 diff --git a/appveyor.yml b/appveyor.yml index b77db8d363..bd91b5dee0 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -79,9 +79,7 @@ after_test: - ps: Move-Item -path artifacts/ServerService/lib/Default/appsettings.json -destination artifacts/ServerService/ - ps: Remove-Item artifacts/ServerHost/appsettings.json #deploy stuff - - ps: $env:TGSVersion = [System.Diagnostics.FileVersionInfo]::GetVersionInfo("$env:APPVEYOR_BUILD_FOLDER/artifacts/ServerHost/Tgstation.Server.Host.dll").FileVersion - - ps: if($env:APPVEYOR_REPO_COMMIT_MESSAGE -match "\[TGSDeploy\]"){ if($env:APPVEYOR_REPO_BRANCH -match "master"){ if($env:CONFIGURATION -match "Release"){ $env:TGSDeploy = "Do it." }}} - - ps: if($env:APPVEYOR_REPO_COMMIT_MESSAGE -match "\[NugetDeploy\]"){ if($env:APPVEYOR_REPO_BRANCH -match "master"){ if($env:CONFIGURATION -match "Release"){ $env:NugetDeploy = "Do it." }}} + - ps: build/prep_deployment.ps1 - ps: $env:TGSGoodNotes = (Start-Process -FilePath 'dotnet' -ArgumentList "run -p tools/ReleaseNotes 4.0.2.0 --no-close" -PassThru -Wait).ExitCode - ps: $env:TGSReleaseNotes = [IO.File]::ReadAllText("tools/ReleaseNotes/release_notes.md") - ps: Write-Host $env:TGSReleaseNotes diff --git a/build/prep_deployment.ps1 b/build/prep_deployment.ps1 new file mode 100644 index 0000000000..7871c6266a --- /dev/null +++ b/build/prep_deployment.ps1 @@ -0,0 +1,19 @@ +$env:TGSVersion = [System.Diagnostics.FileVersionInfo]::GetVersionInfo("$env:APPVEYOR_BUILD_FOLDER/artifacts/ServerHost/Tgstation.Server.Host.dll").FileVersion +if (($env:CONFIGURATION -match "Release") -And ($env:APPVEYOR_REPO_BRANCH -match "master")) { + if ($env:APPVEYOR_REPO_COMMIT_MESSAGE -match "\[TGSDeploy\]") { + $env:TGSDeploy = "Do it." + } + if ($env:APPVEYOR_REPO_COMMIT_MESSAGE -match "\[NugetDeploy\]") { + $env:NugetDeploy = "Do it." + } +} + +dotnet run -p tools/ReleaseNotes $env:TGSVersion --no-close +$env:TGSGoodNotes = $? +$releaseNotesPath = "tools/ReleaseNotes/release_notes.md" +if (Test-Path $path -PathType $releaseNotesPath) { + $env:TGSReleaseNotes = [IO.File]::ReadAllText($releaseNotesPath) +} +else { + $env:TGSReleaseNotes = "Automatic generation failed, please fill manually!" +} From eabdb9ea3af70baab92f007a4588e3c6da5a3952 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Mon, 26 Nov 2018 14:44:07 -0500 Subject: [PATCH 45/51] Remove this stuff --- appveyor.yml | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index bd91b5dee0..f7dece7815 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -40,33 +40,7 @@ build: parallel: false verbosity: minimal publish_nuget: true -test_script: - - OpenCover.Console.exe -returntargetcode -register:user -target:"C:/Program Files/dotnet/dotnet.exe" -targetargs:"test -c %CONFIGURATION% --logger:trx;LogFileName=results.trx /p:DebugType=full tests/Tgstation.Server.Api.Tests/Tgstation.Server.Api.Tests.csproj" -filter:"+[Tgstation.Server*]* -[Tgstation.Server.Api.Tests*]*" -output:".\api_coverage.xml" -oldstyle - - ps: $wc = New-Object 'System.Net.WebClient' - - ps: $wc.UploadFile("https://ci.appveyor.com/api/testresults/mstest/$($env:APPVEYOR_JOB_ID)", (Resolve-Path .\tests\Tgstation.Server.Api.Tests\TestResults\results.trx)) - - OpenCover.Console.exe -returntargetcode -register:user -target:"C:/Program Files/dotnet/dotnet.exe" -targetargs:"test -c %CONFIGURATION% --logger:trx;LogFileName=results.trx /p:DebugType=full tests/Tgstation.Server.Client.Tests/Tgstation.Server.Client.Tests.csproj" -filter:"+[Tgstation.Server*]* -[Tgstation.Server.Client.Tests*]*" -output:".\client_coverage.xml" -oldstyle - - ps: $wc = New-Object 'System.Net.WebClient' - - ps: $wc.UploadFile("https://ci.appveyor.com/api/testresults/mstest/$($env:APPVEYOR_JOB_ID)", (Resolve-Path .\tests\Tgstation.Server.Client.Tests\TestResults\results.trx)) - - OpenCover.Console.exe -returntargetcode -register:user -target:"C:/Program Files/dotnet/dotnet.exe" -targetargs:"test -c %CONFIGURATION% --logger:trx;LogFileName=results.trx /p:DebugType=full tests/Tgstation.Server.Host.Tests/Tgstation.Server.Host.Tests.csproj" -filter:"+[Tgstation.Server*]* -[Tgstation.Server.Host.Tests*]* -[Tgstation.Server.Host]Tgstation.Server.Host.Models.Migrations*" -output:".\host_coverage.xml" -oldstyle - - ps: $wc = New-Object 'System.Net.WebClient' - - ps: $wc.UploadFile("https://ci.appveyor.com/api/testresults/mstest/$($env:APPVEYOR_JOB_ID)", (Resolve-Path .\tests\Tgstation.Server.Host.Tests\TestResults\results.trx)) - - OpenCover.Console.exe -returntargetcode -register:user -target:"C:/Program Files/dotnet/dotnet.exe" -targetargs:"test -c %CONFIGURATION% --logger:trx;LogFileName=results.trx /p:DebugType=full tests/Tgstation.Server.Host.Console.Tests/Tgstation.Server.Host.Console.Tests.csproj" -filter:"+[Tgstation.Server*]* -[Tgstation.Server.Host.Console.Tests*]*" -output:".\console_coverage.xml" -oldstyle - - ps: $wc = New-Object 'System.Net.WebClient' - - ps: $wc.UploadFile("https://ci.appveyor.com/api/testresults/mstest/$($env:APPVEYOR_JOB_ID)", (Resolve-Path .\tests\Tgstation.Server.Host.Console.Tests\TestResults\results.trx)) - - set path=%ProgramFiles(x86)%\Microsoft Visual Studio\2017\TestAgent\Common7\IDE\CommonExtensions\Microsoft\TestWindow;%path% - - copy "%ProgramFiles(x86)%\Microsoft Visual Studio\2017\Community\Common7\IDE\CommonExtensions\Microsoft\TestWindow\Extensions\appveyor.*" "%ProgramFiles(x86)%\Microsoft Visual Studio\2017\TestAgent\Common7\IDE\CommonExtensions\Microsoft\TestWindow\Extensions" /y - - vstest.console /logger:trx;LogFileName=results.trx "tests\Tgstation.Server.Host.Service.Tests\bin\%CONFIGURATION%\Tgstation.Server.Host.Service.Tests.dll" /Enablecodecoverage /inIsolation /Platform:x64 - - ps: $wc = New-Object 'System.Net.WebClient' - - ps: $wc.UploadFile("https://ci.appveyor.com/api/testresults/mstest/$($env:APPVEYOR_JOB_ID)", (Resolve-Path .\TestResults\results.trx)) - - OpenCover.Console.exe -returntargetcode -register:user -target:"C:/Program Files/dotnet/dotnet.exe" -targetargs:"test -c %CONFIGURATION% --logger:trx;LogFileName=results.trx /p:DebugType=full tests/Tgstation.Server.Host.Watchdog.Tests/Tgstation.Server.Host.Watchdog.Tests.csproj" -filter:"+[Tgstation.Server*]* -[Tgstation.Server.Host.Watchdog.Tests*]*" -output:".\watchdog_coverage.xml" -oldstyle - - ps: $wc = New-Object 'System.Net.WebClient' - - ps: $wc.UploadFile("https://ci.appveyor.com/api/testresults/mstest/$($env:APPVEYOR_JOB_ID)", (Resolve-Path .\tests\Tgstation.Server.Host.Watchdog.Tests\TestResults\results.trx)) - - OpenCover.Console.exe -returntargetcode -register:user -target:"C:/Program Files/dotnet/dotnet.exe" -targetargs:"test -c %CONFIGURATION% --logger:trx;LogFileName=results.trx /p:DebugType=full tests/Tgstation.Server.Tests/Tgstation.Server.Tests.csproj" -filter:"+[Tgstation.Server*]* -[Tgstation.Server.Tests*]* -[Tgstation.Server.Host]Tgstation.Server.Host.Models.Migrations..*" -output:".\server_coverage.xml" -oldstyle - - ps: $wc = New-Object 'System.Net.WebClient' - - ps: $wc.UploadFile("https://ci.appveyor.com/api/testresults/mstest/$($env:APPVEYOR_JOB_ID)", (Resolve-Path .\tests\Tgstation.Server.Tests\TestResults\results.trx)) after_test: - - ps: build/UploadCoverage.ps1 - - ps: build/BuildDox.ps1 #host updater - dotnet publish src/Tgstation.Server.Host/Tgstation.Server.Host.csproj -o ../../artifacts/ServerHost -c %CONFIGURATION% #console @@ -80,8 +54,6 @@ after_test: - ps: Remove-Item artifacts/ServerHost/appsettings.json #deploy stuff - ps: build/prep_deployment.ps1 - - ps: $env:TGSGoodNotes = (Start-Process -FilePath 'dotnet' -ArgumentList "run -p tools/ReleaseNotes 4.0.2.0 --no-close" -PassThru -Wait).ExitCode - - ps: $env:TGSReleaseNotes = [IO.File]::ReadAllText("tools/ReleaseNotes/release_notes.md") - ps: Write-Host $env:TGSReleaseNotes - ps: Write-Host $env:TGSGoodNotes deploy: From 0d7e17a489e6498438a1aedeea9f404e0295114b Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Mon, 26 Nov 2018 14:51:15 -0500 Subject: [PATCH 46/51] Fix bug in release notes script --- tools/ReleaseNotes/Program.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/ReleaseNotes/Program.cs b/tools/ReleaseNotes/Program.cs index b0219fad6f..335d752201 100644 --- a/tools/ReleaseNotes/Program.cs +++ b/tools/ReleaseNotes/Program.cs @@ -87,6 +87,7 @@ namespace ReleaseNotes if (pullRequest.State.Value == ItemState.Closed) { //need to check it was merged + await GetFullPR().ConfigureAwait(false); lock (milestoneTaskLock) if (milestoneTask == null) milestoneTask = GetMilestone(); From d389e6250f3e9dc26b76493c552e3a9a9d7adea1 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Mon, 26 Nov 2018 14:53:28 -0500 Subject: [PATCH 47/51] Fix this too --- build/prep_deployment.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/prep_deployment.ps1 b/build/prep_deployment.ps1 index 7871c6266a..7df3c51b60 100644 --- a/build/prep_deployment.ps1 +++ b/build/prep_deployment.ps1 @@ -11,7 +11,7 @@ if (($env:CONFIGURATION -match "Release") -And ($env:APPVEYOR_REPO_BRANCH -match dotnet run -p tools/ReleaseNotes $env:TGSVersion --no-close $env:TGSGoodNotes = $? $releaseNotesPath = "tools/ReleaseNotes/release_notes.md" -if (Test-Path $path -PathType $releaseNotesPath) { +if (Test-Path $releaseNotesPath -PathType Leaf) { $env:TGSReleaseNotes = [IO.File]::ReadAllText($releaseNotesPath) } else { From 8c2bfc425044792f411f44a09e11f5ae76729222 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Mon, 26 Nov 2018 14:55:05 -0500 Subject: [PATCH 48/51] Other cleanups --- build/prep_deployment.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/prep_deployment.ps1 b/build/prep_deployment.ps1 index 7df3c51b60..180319e02a 100644 --- a/build/prep_deployment.ps1 +++ b/build/prep_deployment.ps1 @@ -8,9 +8,9 @@ if (($env:CONFIGURATION -match "Release") -And ($env:APPVEYOR_REPO_BRANCH -match } } -dotnet run -p tools/ReleaseNotes $env:TGSVersion --no-close +dotnet run -p "$env:APPVEYOR_BUILD_FOLDER/tools/ReleaseNotes" $env:TGSVersion --no-close $env:TGSGoodNotes = $? -$releaseNotesPath = "tools/ReleaseNotes/release_notes.md" +$releaseNotesPath = "$env:APPVEYOR_BUILD_FOLDER/tools/ReleaseNotes/release_notes.md" if (Test-Path $releaseNotesPath -PathType Leaf) { $env:TGSReleaseNotes = [IO.File]::ReadAllText($releaseNotesPath) } From 576f1683e39f788cd9fc36d0d3b01c9375528671 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Mon, 26 Nov 2018 15:13:01 -0500 Subject: [PATCH 49/51] I need to TEST --- build/prep_deployment.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/prep_deployment.ps1 b/build/prep_deployment.ps1 index 180319e02a..3f1b84ac60 100644 --- a/build/prep_deployment.ps1 +++ b/build/prep_deployment.ps1 @@ -8,7 +8,7 @@ if (($env:CONFIGURATION -match "Release") -And ($env:APPVEYOR_REPO_BRANCH -match } } -dotnet run -p "$env:APPVEYOR_BUILD_FOLDER/tools/ReleaseNotes" $env:TGSVersion --no-close +dotnet run -p "$env:APPVEYOR_BUILD_FOLDER/tools/ReleaseNotes" 4.0.2.0 --no-close $env:TGSGoodNotes = $? $releaseNotesPath = "$env:APPVEYOR_BUILD_FOLDER/tools/ReleaseNotes/release_notes.md" if (Test-Path $releaseNotesPath -PathType Leaf) { From 93ab39abb41bafaa799ee932546ea7f8853a55d5 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Mon, 26 Nov 2018 15:28:58 -0500 Subject: [PATCH 50/51] Cleanup --- .github/CONTRIBUTING.md | 25 ++ .github/PULL_REQUEST_TEMPLATE.md | 13 + appveyor.yml | 30 +- build/prep_deployment.ps1 | 34 +- tools/ReleaseNotes/Program.cs | 524 +++++++++++++++---------------- 5 files changed, 343 insertions(+), 283 deletions(-) create mode 100644 .github/PULL_REQUEST_TEMPLATE.md diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index e1c6d6cf23..19273f5d99 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -149,7 +149,32 @@ There is no strict process when it comes to merging pull requests. Pull requests * Commits MUST be properly titled and commented as we only use merge commits for the pull request process +## Deployment process + +Every issue/pull request in a release should share a common milestone named with the release version i.e. `4.5.3.5` + +When every issue and PR in the milestone is closed. Create a version bump PR that changes the version numbers. At the time of this writing they exist in the following files + +- `/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj` +- `/src/Tgstation.Server.Host.Console/Tgstation.Server.Host.Console.csproj` +- 2 in `/src/Tgstation.Server.Host.Service/Properties/AssemblyInfo.cs` +- `/src/Tgstation.Server.Host.Watchdog/Tgstation.Server.Host.Watchdog.csproj` + +Merge the pull request with `[TGSDeploy]` somewhere in the commit title. The scripts will handle amalgamating release notes, building, closing the milestone, and publishing the release. This will also trigger update notifications on existing TGS deployments. + +### API/Client Deployment + +The Api/Client project versions must be updated on nuget when changed. The numbers exist in the following files: + +- `/src/Tgstation.Server.Api/Tgstation.Server.Api.csproj` +- `/src/Tgstation.Server.Client/Tgstation.Server.Client.csproj` + +These should not be the same numbers as the main suite. When bumping the API version the client should only receive a minor (3rd number) version bump unless major changes to CLIENT code were made. + +Merge these alongside regular deployments with `[NugetDeploy]` in the commit title (Won't work without an accompanying `[TGSDeploy]`). This will handle the nuget publishing. + ## Banned content + Do not add any of the following in a Pull Request or risk getting the PR closed: * National Socialist Party of Germany content, National Socialist Party of Germany related content, or National Socialist Party of Germany references diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000000..98fe22ab33 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,13 @@ +[Release Notes]: # (Your PR should contain a detailed list of notable changes, titled appropriately. This includes any observable changes to the server or DMAPI. See example below) + +:cl: +Description of your change +Each newline corresponds to a release note in the upcoming sprint +/:cl: + +:cl: +You can also have multiple sets of release notes per pull request +They will be amalgamated together in the end +/:cl: + +[Why]: # (Please add a short description [two lines down] of why you think these changes would benefit the game. If you can't justify it in words, it might not be worth adding.) diff --git a/appveyor.yml b/appveyor.yml index f7dece7815..5b94661a3e 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -40,7 +40,33 @@ build: parallel: false verbosity: minimal publish_nuget: true +test_script: + - OpenCover.Console.exe -returntargetcode -register:user -target:"C:/Program Files/dotnet/dotnet.exe" -targetargs:"test -c %CONFIGURATION% --logger:trx;LogFileName=results.trx /p:DebugType=full tests/Tgstation.Server.Api.Tests/Tgstation.Server.Api.Tests.csproj" -filter:"+[Tgstation.Server*]* -[Tgstation.Server.Api.Tests*]*" -output:".\api_coverage.xml" -oldstyle + - ps: $wc = New-Object 'System.Net.WebClient' + - ps: $wc.UploadFile("https://ci.appveyor.com/api/testresults/mstest/$($env:APPVEYOR_JOB_ID)", (Resolve-Path .\tests\Tgstation.Server.Api.Tests\TestResults\results.trx)) + - OpenCover.Console.exe -returntargetcode -register:user -target:"C:/Program Files/dotnet/dotnet.exe" -targetargs:"test -c %CONFIGURATION% --logger:trx;LogFileName=results.trx /p:DebugType=full tests/Tgstation.Server.Client.Tests/Tgstation.Server.Client.Tests.csproj" -filter:"+[Tgstation.Server*]* -[Tgstation.Server.Client.Tests*]*" -output:".\client_coverage.xml" -oldstyle + - ps: $wc = New-Object 'System.Net.WebClient' + - ps: $wc.UploadFile("https://ci.appveyor.com/api/testresults/mstest/$($env:APPVEYOR_JOB_ID)", (Resolve-Path .\tests\Tgstation.Server.Client.Tests\TestResults\results.trx)) + - OpenCover.Console.exe -returntargetcode -register:user -target:"C:/Program Files/dotnet/dotnet.exe" -targetargs:"test -c %CONFIGURATION% --logger:trx;LogFileName=results.trx /p:DebugType=full tests/Tgstation.Server.Host.Tests/Tgstation.Server.Host.Tests.csproj" -filter:"+[Tgstation.Server*]* -[Tgstation.Server.Host.Tests*]* -[Tgstation.Server.Host]Tgstation.Server.Host.Models.Migrations*" -output:".\host_coverage.xml" -oldstyle + - ps: $wc = New-Object 'System.Net.WebClient' + - ps: $wc.UploadFile("https://ci.appveyor.com/api/testresults/mstest/$($env:APPVEYOR_JOB_ID)", (Resolve-Path .\tests\Tgstation.Server.Host.Tests\TestResults\results.trx)) + - OpenCover.Console.exe -returntargetcode -register:user -target:"C:/Program Files/dotnet/dotnet.exe" -targetargs:"test -c %CONFIGURATION% --logger:trx;LogFileName=results.trx /p:DebugType=full tests/Tgstation.Server.Host.Console.Tests/Tgstation.Server.Host.Console.Tests.csproj" -filter:"+[Tgstation.Server*]* -[Tgstation.Server.Host.Console.Tests*]*" -output:".\console_coverage.xml" -oldstyle + - ps: $wc = New-Object 'System.Net.WebClient' + - ps: $wc.UploadFile("https://ci.appveyor.com/api/testresults/mstest/$($env:APPVEYOR_JOB_ID)", (Resolve-Path .\tests\Tgstation.Server.Host.Console.Tests\TestResults\results.trx)) + - set path=%ProgramFiles(x86)%\Microsoft Visual Studio\2017\TestAgent\Common7\IDE\CommonExtensions\Microsoft\TestWindow;%path% + - copy "%ProgramFiles(x86)%\Microsoft Visual Studio\2017\Community\Common7\IDE\CommonExtensions\Microsoft\TestWindow\Extensions\appveyor.*" "%ProgramFiles(x86)%\Microsoft Visual Studio\2017\TestAgent\Common7\IDE\CommonExtensions\Microsoft\TestWindow\Extensions" /y + - vstest.console /logger:trx;LogFileName=results.trx "tests\Tgstation.Server.Host.Service.Tests\bin\%CONFIGURATION%\Tgstation.Server.Host.Service.Tests.dll" /Enablecodecoverage /inIsolation /Platform:x64 + - ps: $wc = New-Object 'System.Net.WebClient' + - ps: $wc.UploadFile("https://ci.appveyor.com/api/testresults/mstest/$($env:APPVEYOR_JOB_ID)", (Resolve-Path .\TestResults\results.trx)) + - OpenCover.Console.exe -returntargetcode -register:user -target:"C:/Program Files/dotnet/dotnet.exe" -targetargs:"test -c %CONFIGURATION% --logger:trx;LogFileName=results.trx /p:DebugType=full tests/Tgstation.Server.Host.Watchdog.Tests/Tgstation.Server.Host.Watchdog.Tests.csproj" -filter:"+[Tgstation.Server*]* -[Tgstation.Server.Host.Watchdog.Tests*]*" -output:".\watchdog_coverage.xml" -oldstyle + - ps: $wc = New-Object 'System.Net.WebClient' + - ps: $wc.UploadFile("https://ci.appveyor.com/api/testresults/mstest/$($env:APPVEYOR_JOB_ID)", (Resolve-Path .\tests\Tgstation.Server.Host.Watchdog.Tests\TestResults\results.trx)) + - OpenCover.Console.exe -returntargetcode -register:user -target:"C:/Program Files/dotnet/dotnet.exe" -targetargs:"test -c %CONFIGURATION% --logger:trx;LogFileName=results.trx /p:DebugType=full tests/Tgstation.Server.Tests/Tgstation.Server.Tests.csproj" -filter:"+[Tgstation.Server*]* -[Tgstation.Server.Tests*]* -[Tgstation.Server.Host]Tgstation.Server.Host.Models.Migrations..*" -output:".\server_coverage.xml" -oldstyle + - ps: $wc = New-Object 'System.Net.WebClient' + - ps: $wc.UploadFile("https://ci.appveyor.com/api/testresults/mstest/$($env:APPVEYOR_JOB_ID)", (Resolve-Path .\tests\Tgstation.Server.Tests\TestResults\results.trx)) after_test: + - ps: build/UploadCoverage.ps1 + - ps: build/BuildDox.ps1 #host updater - dotnet publish src/Tgstation.Server.Host/Tgstation.Server.Host.csproj -o ../../artifacts/ServerHost -c %CONFIGURATION% #console @@ -54,8 +80,6 @@ after_test: - ps: Remove-Item artifacts/ServerHost/appsettings.json #deploy stuff - ps: build/prep_deployment.ps1 - - ps: Write-Host $env:TGSReleaseNotes - - ps: Write-Host $env:TGSGoodNotes deploy: - provider: GitHub release: "tgstation-server-v$(TGSVersion)" @@ -63,7 +87,7 @@ deploy: auth_token: secure: lJNGAXwiB5HlWdthz3K4PetqpTG5IEAyRgKaiKxFMQ8HW8CcOjRtB97B05op7BsK artifact: ServerConsole,ServerService,ServerUpdatePackage,DMAPI - draft: $(TGSGoodNotes) + draft: $(TGSDraftNotes) prerelease: true on: TGSDeploy: "Do it." diff --git a/build/prep_deployment.ps1 b/build/prep_deployment.ps1 index 3f1b84ac60..a00431bff8 100644 --- a/build/prep_deployment.ps1 +++ b/build/prep_deployment.ps1 @@ -1,19 +1,25 @@ $env:TGSVersion = [System.Diagnostics.FileVersionInfo]::GetVersionInfo("$env:APPVEYOR_BUILD_FOLDER/artifacts/ServerHost/Tgstation.Server.Host.dll").FileVersion -if (($env:CONFIGURATION -match "Release") -And ($env:APPVEYOR_REPO_BRANCH -match "master")) { - if ($env:APPVEYOR_REPO_COMMIT_MESSAGE -match "\[TGSDeploy\]") { - $env:TGSDeploy = "Do it." + +Write-Host "TGS Version: $env:TGSVersion" + +if (($env:CONFIGURATION -match "Release") -And ($env:APPVEYOR_REPO_BRANCH -match "master") -And ($env:APPVEYOR_REPO_COMMIT_MESSAGE -match "\[TGSDeploy\]")) { + Write-Host "Deploying..." + $env:TGSDeploy = "Do it." + + Write-Host "Generating release notes..." + dotnet run -p "$env:APPVEYOR_BUILD_FOLDER/tools/ReleaseNotes" $env:TGSVersion + $env:TGSDraftNotes = !($?) + $releaseNotesPath = "$env:APPVEYOR_BUILD_FOLDER/tools/ReleaseNotes/release_notes.md" + if (Test-Path $releaseNotesPath -PathType Leaf) { + $env:TGSReleaseNotes = [IO.File]::ReadAllText($releaseNotesPath) } + else { + Write-Host "Release note generation failed, release will be created as a draft!" + $env:TGSReleaseNotes = "Automatic generation failed, please fill manually!" + } + if ($env:APPVEYOR_REPO_COMMIT_MESSAGE -match "\[NugetDeploy\]") { $env:NugetDeploy = "Do it." - } -} - -dotnet run -p "$env:APPVEYOR_BUILD_FOLDER/tools/ReleaseNotes" 4.0.2.0 --no-close -$env:TGSGoodNotes = $? -$releaseNotesPath = "$env:APPVEYOR_BUILD_FOLDER/tools/ReleaseNotes/release_notes.md" -if (Test-Path $releaseNotesPath -PathType Leaf) { - $env:TGSReleaseNotes = [IO.File]::ReadAllText($releaseNotesPath) -} -else { - $env:TGSReleaseNotes = "Automatic generation failed, please fill manually!" + Write-Host "Nuget deployment enabled" + } } diff --git a/tools/ReleaseNotes/Program.cs b/tools/ReleaseNotes/Program.cs index 335d752201..edd1960dd5 100644 --- a/tools/ReleaseNotes/Program.cs +++ b/tools/ReleaseNotes/Program.cs @@ -9,309 +9,301 @@ using System.Threading.Tasks; namespace ReleaseNotes { - class Program - { - static async Task Main(string[] args) - { - if (args.Length < 1) - { - Console.WriteLine("Missing version argument!"); - return 1; - } + /// + /// Contains the application entrypoint + /// + static class Program + { + /// + /// The entrypoint for the + /// + static async Task Main(string[] args) + { + if (args.Length < 1) + { + Console.WriteLine("Missing version argument!"); + return 1; + } - var versionString = args[0]; - if (!Version.TryParse(versionString, out var version)) - { - Console.WriteLine("Invalid version: " + versionString); - return 2; - } + var versionString = args[0]; + if (!Version.TryParse(versionString, out var version)) + { + Console.WriteLine("Invalid version: " + versionString); + return 2; + } - var doNotCloseMilestone = args.Length >= 2 && args[1].ToUpperInvariant() == "--NO-CLOSE"; + var doNotCloseMilestone = args.Length >= 2 && args[1].ToUpperInvariant() == "--NO-CLOSE"; - const string ReleaseNotesEnvVar = "TGS_RELEASE_NOTES_TOKEN"; - var githubToken = Environment.GetEnvironmentVariable(ReleaseNotesEnvVar); - if (String.IsNullOrWhiteSpace(githubToken)) - { - Console.WriteLine("Missing " + ReleaseNotesEnvVar + " environment variable!"); - return 3; - } + const string ReleaseNotesEnvVar = "TGS_RELEASE_NOTES_TOKEN"; + var githubToken = Environment.GetEnvironmentVariable(ReleaseNotesEnvVar); + if (String.IsNullOrWhiteSpace(githubToken)) + { + Console.WriteLine("Missing " + ReleaseNotesEnvVar + " environment variable!"); + return 3; + } - try - { - var client = new GitHubClient(new ProductHeaderValue("tgs_release_notes")); - client.Credentials = new Credentials(githubToken); + try + { + var client = new GitHubClient(new ProductHeaderValue("tgs_release_notes")); + client.Credentials = new Credentials(githubToken); - const string RepoOwner = "tgstation"; - const string RepoName = "tgstation-server"; + const string RepoOwner = "tgstation"; + const string RepoName = "tgstation-server"; - var releasesTask = client.Repository.Release.GetAll(RepoOwner, RepoName); + var releasesTask = client.Repository.Release.GetAll(RepoOwner, RepoName); - Console.WriteLine("Getting pull requests in milestone " + versionString + "..."); - var milestonePRs = await client.Search.SearchIssues(new SearchIssuesRequest - { - Milestone = versionString, - Type = IssueTypeQualifier.PullRequest, - Repos = { { RepoOwner, RepoName } } - }).ConfigureAwait(false); + Console.WriteLine("Getting pull requests in milestone " + versionString + "..."); + var milestonePRs = await client.Search.SearchIssues(new SearchIssuesRequest + { + Milestone = versionString, + Type = IssueTypeQualifier.PullRequest, + Repos = { { RepoOwner, RepoName } } + }).ConfigureAwait(false); - if (milestonePRs.IncompleteResults) - { - Console.WriteLine("Incomplete results for milestone PRs query!"); - return 5; - } - Console.WriteLine(milestonePRs.Items.Count + " total pull requests"); + if (milestonePRs.IncompleteResults) + { + Console.WriteLine("Incomplete results for milestone PRs query!"); + return 5; + } + Console.WriteLine(milestonePRs.Items.Count + " total pull requests"); - Task milestoneTask = null; - var milestoneTaskLock = new object(); - var releaseDictionary = new Dictionary>(); - var authorizedUsers = new Dictionary>(); + Task milestoneTask = null; + var milestoneTaskLock = new object(); + var releaseDictionary = new Dictionary>(); + var authorizedUsers = new Dictionary>(); - async Task GetReleaseNotesFromPR(Issue pullRequest) - { - PullRequest fullPR = null; - async Task GetFullPR() - { - if (fullPR != null) - return; - fullPR = await client.Repository.PullRequest.Get(RepoOwner, RepoName, pullRequest.Number).ConfigureAwait(false); - }; + async Task GetReleaseNotesFromPR(Issue pullRequest) + { + //need to check it was merged + var fullPr = await client.Repository.PullRequest.Get(RepoOwner, RepoName, pullRequest.Number).ConfigureAwait(false); - async Task GetMilestone() - { - await GetFullPR().ConfigureAwait(false); - if (fullPR.Milestone == null) - return null; - return await client.Issue.Milestone.Get(RepoOwner, RepoName, fullPR.Milestone.Number); - }; + async Task GetMilestone() + { + if (fullPR.Milestone == null) + return null; + return await client.Issue.Milestone.Get(RepoOwner, RepoName, fullPR.Milestone.Number); + }; - if (pullRequest.State.Value == ItemState.Closed) - { - //need to check it was merged - await GetFullPR().ConfigureAwait(false); - lock (milestoneTaskLock) - if (milestoneTask == null) - milestoneTask = GetMilestone(); - if (!fullPR.Merged) - return; - }; + lock (milestoneTaskLock) + if (milestoneTask == null) + milestoneTask = GetMilestone(); - async Task BuildNotesFromComment(string comment, User user) - { - var commentSplits = comment.Split('\n'); - var notesOpen = false; - var notesClosed = false; - var notes = new List(); - foreach (var line in commentSplits) - { - var trimmedLine = line.Trim(); - if (!notesOpen) - { - notesOpen = trimmedLine.StartsWith(":cl:", StringComparison.Ordinal); - notesClosed = false; - continue; - } - if (trimmedLine.StartsWith("/:cl:", StringComparison.Ordinal)) - { - notesClosed = true; - notesOpen = false; - continue; - } - if (trimmedLine.Length == 0) - continue; - notes.Add(trimmedLine); - } - if (!notesClosed || notes.Count == 0) - return; + if (!fullPR.Merged) + return; - Task authTask; - TaskCompletionSource ourTcs = null; - lock (authorizedUsers) - { - if (!authorizedUsers.TryGetValue(user.Id, out authTask)) - { - ourTcs = new TaskCompletionSource(); - authTask = ourTcs.Task; - authorizedUsers.Add(user.Id, authTask); - } - } + async Task BuildNotesFromComment(string comment, User user) + { + var commentSplits = comment.Split('\n'); + var notesOpen = false; + var notesClosed = false; + var notes = new List(); + foreach (var line in commentSplits) + { + var trimmedLine = line.Trim(); + if (!notesOpen) + { + notesOpen = trimmedLine.StartsWith(":cl:", StringComparison.Ordinal); + notesClosed = false; + continue; + } + if (trimmedLine.StartsWith("/:cl:", StringComparison.Ordinal)) + { + notesClosed = true; + notesOpen = false; + continue; + } + if (trimmedLine.Length == 0) + continue; + notes.Add(trimmedLine); + } + if (!notesClosed || notes.Count == 0) + return; - if (ourTcs != null) - try - { - //check if the user has access - var perm = await client.Repository.Collaborator.ReviewPermission(RepoOwner, RepoName, user.Login).ConfigureAwait(false); - ourTcs.SetResult(perm.Permission == PermissionLevel.Write || perm.Permission == PermissionLevel.Admin); - } - catch - { - ourTcs.SetResult(false); - throw; - } + Task authTask; + TaskCompletionSource ourTcs = null; + lock (authorizedUsers) + { + if (!authorizedUsers.TryGetValue(user.Id, out authTask)) + { + ourTcs = new TaskCompletionSource(); + authTask = ourTcs.Task; + authorizedUsers.Add(user.Id, authTask); + } + } - var authorized = await authTask.ConfigureAwait(false); - if (!authorized) - return; + if (ourTcs != null) + try + { + //check if the user has access + var perm = await client.Repository.Collaborator.ReviewPermission(RepoOwner, RepoName, user.Login).ConfigureAwait(false); + ourTcs.SetResult(perm.Permission == PermissionLevel.Write || perm.Permission == PermissionLevel.Admin); + } + catch + { + ourTcs.SetResult(false); + throw; + } - lock (releaseDictionary) - { - foreach (var I in notes) - Console.WriteLine("#" + pullRequest.Number + " - " + I + " (@" + user.Login + ")"); - if (releaseDictionary.TryGetValue(pullRequest.Number, out var currentValues)) - currentValues.AddRange(notes); - else - releaseDictionary.Add(pullRequest.Number, notes); - } - } + var authorized = await authTask.ConfigureAwait(false); + if (!authorized) + return; - var comments = await client.Issue.Comment.GetAllForIssue(RepoOwner, RepoName, pullRequest.Number).ConfigureAwait(false); - await Task.WhenAll(BuildNotesFromComment(pullRequest.Body, pullRequest.User), Task.WhenAll(comments.Select(x => BuildNotesFromComment(x.Body, x.User)))).ConfigureAwait(false); + lock (releaseDictionary) + { + foreach (var I in notes) + Console.WriteLine("#" + pullRequest.Number + " - " + I + " (@" + user.Login + ")"); + if (releaseDictionary.TryGetValue(pullRequest.Number, out var currentValues)) + currentValues.AddRange(notes); + else + releaseDictionary.Add(pullRequest.Number, notes); + } + } - lock (milestoneTaskLock) - if (milestoneTask == null) - milestoneTask = GetMilestone(); - } + var comments = await client.Issue.Comment.GetAllForIssue(RepoOwner, RepoName, pullRequest.Number).ConfigureAwait(false); + await Task.WhenAll(BuildNotesFromComment(pullRequest.Body, pullRequest.User), Task.WhenAll(comments.Select(x => BuildNotesFromComment(x.Body, x.User)))).ConfigureAwait(false); + } - var tasks = new List(); - foreach (var I in milestonePRs.Items) - tasks.Add(GetReleaseNotesFromPR(I)); + var tasks = new List(); + foreach (var I in milestonePRs.Items) + tasks.Add(GetReleaseNotesFromPR(I)); - var releases = await releasesTask.ConfigureAwait(false); + var releases = await releasesTask.ConfigureAwait(false); - var releasingSuite = version.Major; + var releasingSuite = version.Major; - Version highestReleaseVersion = null; - Release highestRelease = null; - foreach (var I in releases) - { - if (!Version.TryParse(I.TagName.Replace("tgstation-server-v", String.Empty), out var currentReleaseVersion)) - { - Console.WriteLine("WARNING: Unable to determine version of release " + I.HtmlUrl); - continue; - } + Version highestReleaseVersion = null; + Release highestRelease = null; + foreach (var I in releases) + { + if (!Version.TryParse(I.TagName.Replace("tgstation-server-v", String.Empty), out var currentReleaseVersion)) + { + Console.WriteLine("WARNING: Unable to determine version of release " + I.HtmlUrl); + continue; + } - if (currentReleaseVersion.Major == releasingSuite && (highestReleaseVersion == null || currentReleaseVersion > highestReleaseVersion)) - { - highestReleaseVersion = currentReleaseVersion; - highestRelease = I; - } - } + if (currentReleaseVersion.Major == releasingSuite && (highestReleaseVersion == null || currentReleaseVersion > highestReleaseVersion)) + { + highestReleaseVersion = currentReleaseVersion; + highestRelease = I; + } + } - if (highestReleaseVersion == null) - { - Console.WriteLine("Unable to determine highest release version for suite " + releasingSuite + "!"); - return 6; - } + if (highestReleaseVersion == null) + { + Console.WriteLine("Unable to determine highest release version for suite " + releasingSuite + "!"); + return 6; + } - var oldNotes = highestRelease.Body; + var oldNotes = highestRelease.Body; - var splits = new List(oldNotes.Split('\n')); - //trim away all the lines that don't start with # + var splits = new List(oldNotes.Split('\n')); + //trim away all the lines that don't start with # - string keepThisRelease; - if (version.Revision == 0) - if (version.Build == 0) - keepThisRelease = "# "; - else - keepThisRelease = "## "; - else - keepThisRelease = "### "; + string keepThisRelease; + if (version.Revision == 0) + if (version.Build == 0) + keepThisRelease = "# "; + else + keepThisRelease = "## "; + else + keepThisRelease = "### "; - for (; !splits[0].StartsWith(keepThisRelease, StringComparison.Ordinal); splits.RemoveAt(0)) - if (splits.Count == 1) - { - Console.WriteLine("Error formatting release notes: Can't detemine notes start!"); - return 7; - } + for (; !splits[0].StartsWith(keepThisRelease, StringComparison.Ordinal); splits.RemoveAt(0)) + if (splits.Count == 1) + { + Console.WriteLine("Error formatting release notes: Can't detemine notes start!"); + return 7; + } - oldNotes = String.Join('\n', splits); + oldNotes = String.Join('\n', splits); - string prefix; - switch (releasingSuite) - { - case 3: - prefix = "The /tg/station server suite"; - break; - default: - prefix = "See https://tgstation.github.io/tgstation-server for installation instructions"; - break; - } + string prefix; + switch (releasingSuite) + { + case 3: + prefix = "The /tg/station server suite"; + break; + default: + prefix = "See https://tgstation.github.io/tgstation-server for installation instructions"; + break; + } - var newNotes = new StringBuilder(prefix); - newNotes.Append(Environment.NewLine); - newNotes.Append(Environment.NewLine); - if (version.Revision == 0) - if (version.Build == 0) - { - newNotes.Append("# [Version "); - newNotes.Append(version.Minor); - } - else - { - newNotes.Append("## [Changelog for "); - newNotes.Append(version.Build); - newNotes.Append(".x"); - } - else - { - newNotes.Append("### [Patch "); - newNotes.Append(version.Revision); - } - newNotes.Append("]("); - var milestone = await milestoneTask.ConfigureAwait(false); - if (milestone == null) - { - Console.WriteLine("Unable to detemine milestone!"); - return 9; - } - newNotes.Append(milestone.HtmlUrl); - newNotes.Append("?closed=1)"); - newNotes.Append(Environment.NewLine); + var newNotes = new StringBuilder(prefix); + newNotes.Append(Environment.NewLine); + newNotes.Append(Environment.NewLine); + if (version.Revision == 0) + if (version.Build == 0) + { + newNotes.Append("# [Version "); + newNotes.Append(version.Minor); + } + else + { + newNotes.Append("## [Changelog for "); + newNotes.Append(version.Build); + newNotes.Append(".x"); + } + else + { + newNotes.Append("### [Patch "); + newNotes.Append(version.Revision); + } + newNotes.Append("]("); + var milestone = await milestoneTask.ConfigureAwait(false); + if (milestone == null) + { + Console.WriteLine("Unable to detemine milestone!"); + return 9; + } + newNotes.Append(milestone.HtmlUrl); + newNotes.Append("?closed=1)"); + newNotes.Append(Environment.NewLine); - await Task.WhenAll(tasks).ConfigureAwait(false); + await Task.WhenAll(tasks).ConfigureAwait(false); - if (releaseDictionary.Count == 0) - { - Console.WriteLine("No release notes for this milestone!"); - return 8; - } + if (releaseDictionary.Count == 0) + { + Console.WriteLine("No release notes for this milestone!"); + return 8; + } - foreach (var I in releaseDictionary) - foreach (var note in I.Value) - { - newNotes.Append(Environment.NewLine); - newNotes.Append("- "); - newNotes.Append(note); - newNotes.Append(" (#"); - newNotes.Append(I.Key); - newNotes.Append(')'); - } + foreach (var I in releaseDictionary) + foreach (var note in I.Value) + { + newNotes.Append(Environment.NewLine); + newNotes.Append("- "); + newNotes.Append(note); + newNotes.Append(" (#"); + newNotes.Append(I.Key); + newNotes.Append(')'); + } - newNotes.Append(Environment.NewLine); - newNotes.Append(Environment.NewLine); - newNotes.Append(oldNotes); + newNotes.Append(Environment.NewLine); + newNotes.Append(Environment.NewLine); + newNotes.Append(oldNotes); - Console.WriteLine("Writing out new release notes..."); - var releaseNotes = newNotes.ToString(); - await File.WriteAllTextAsync("release_notes.md", releaseNotes).ConfigureAwait(false); + Console.WriteLine("Writing out new release notes..."); + var releaseNotes = newNotes.ToString(); + await File.WriteAllTextAsync("release_notes.md", releaseNotes).ConfigureAwait(false); - if (doNotCloseMilestone) - Console.WriteLine("Not closing milestone due to parameter!"); - else - { - Console.WriteLine("Closing milestone..."); - await client.Issue.Milestone.Update(RepoOwner, RepoName, milestone.Number, new MilestoneUpdate - { - State = ItemState.Closed - }).ConfigureAwait(false); - } + if (doNotCloseMilestone) + Console.WriteLine("Not closing milestone due to parameter!"); + else + { + Console.WriteLine("Closing milestone..."); + await client.Issue.Milestone.Update(RepoOwner, RepoName, milestone.Number, new MilestoneUpdate + { + State = ItemState.Closed + }).ConfigureAwait(false); + } - return 0; - } - catch (Exception e) - { - Console.WriteLine(e); - return 4; - } - } - } + return 0; + } + catch (Exception e) + { + Console.WriteLine(e); + return 4; + } + } + } } From 40cbdd48502cda81e95df235f276d020d8381a15 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Mon, 26 Nov 2018 15:49:52 -0500 Subject: [PATCH 51/51] Fixup --- .gitignore | 2 +- tgstation-server.sln | 7 +++++++ tools/ReleaseNotes/Program.cs | 12 ++++++------ tools/ReleaseNotes/release_notes.md | 17 ----------------- 4 files changed, 14 insertions(+), 24 deletions(-) delete mode 100644 tools/ReleaseNotes/release_notes.md diff --git a/.gitignore b/.gitignore index 8b0b626b5c..f4f0e4d74f 100644 --- a/.gitignore +++ b/.gitignore @@ -15,4 +15,4 @@ artifacts/ /tests/DMAPI/travistester.int /tests/DMAPI/travistester.dmb /src/Tgstation.Server.Host/appsettings.Development.json -/tools/ReleaseNotes/release_nodes.md +/tools/ReleaseNotes/release_notes.md diff --git a/tgstation-server.sln b/tgstation-server.sln index a7fb21ff65..d3155c0ea4 100644 --- a/tgstation-server.sln +++ b/tgstation-server.sln @@ -114,6 +114,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{E821 EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tgstation.Server.Tests", "tests\Tgstation.Server.Tests\Tgstation.Server.Tests.csproj", "{09056964-1C74-445A-96EC-33F6DFC07916}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ReleaseNotes", "tools\ReleaseNotes\ReleaseNotes.csproj", "{5CB51532-55F0-4255-B6E5-69ED5CCD14CD}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -172,6 +174,10 @@ Global {09056964-1C74-445A-96EC-33F6DFC07916}.Debug|Any CPU.Build.0 = Debug|Any CPU {09056964-1C74-445A-96EC-33F6DFC07916}.Release|Any CPU.ActiveCfg = Release|Any CPU {09056964-1C74-445A-96EC-33F6DFC07916}.Release|Any CPU.Build.0 = Release|Any CPU + {5CB51532-55F0-4255-B6E5-69ED5CCD14CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5CB51532-55F0-4255-B6E5-69ED5CCD14CD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5CB51532-55F0-4255-B6E5-69ED5CCD14CD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5CB51532-55F0-4255-B6E5-69ED5CCD14CD}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -183,6 +189,7 @@ Global {3BB10856-AA01-43D1-AAB0-A013085426D6} = {7E4E7CF6-A48D-410E-8191-C49C6B5AB9CC} {82066812-6C73-4360-943B-B23F2F491261} = {7E4E7CF6-A48D-410E-8191-C49C6B5AB9CC} {057FAC33-CC31-4948-91C6-B0977C335890} = {F7765A4B-021C-454E-ABB1-2AB1B85F91B4} + {5CB51532-55F0-4255-B6E5-69ED5CCD14CD} = {A55C1117-5808-4AB2-BEA6-4D4A3E66A2F2} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {DFD36C95-3E49-41C7-ACDB-86BAF5B18A79} diff --git a/tools/ReleaseNotes/Program.cs b/tools/ReleaseNotes/Program.cs index edd1960dd5..c62af92615 100644 --- a/tools/ReleaseNotes/Program.cs +++ b/tools/ReleaseNotes/Program.cs @@ -75,7 +75,7 @@ namespace ReleaseNotes async Task GetReleaseNotesFromPR(Issue pullRequest) { //need to check it was merged - var fullPr = await client.Repository.PullRequest.Get(RepoOwner, RepoName, pullRequest.Number).ConfigureAwait(false); + var fullPR = await client.Repository.PullRequest.Get(RepoOwner, RepoName, pullRequest.Number).ConfigureAwait(false); async Task GetMilestone() { @@ -151,16 +151,16 @@ namespace ReleaseNotes lock (releaseDictionary) { foreach (var I in notes) - Console.WriteLine("#" + pullRequest.Number + " - " + I + " (@" + user.Login + ")"); - if (releaseDictionary.TryGetValue(pullRequest.Number, out var currentValues)) + Console.WriteLine("#" + fullPR.Number + " - " + I + " (@" + user.Login + ")"); + if (releaseDictionary.TryGetValue(fullPR.Number, out var currentValues)) currentValues.AddRange(notes); else - releaseDictionary.Add(pullRequest.Number, notes); + releaseDictionary.Add(fullPR.Number, notes); } } - var comments = await client.Issue.Comment.GetAllForIssue(RepoOwner, RepoName, pullRequest.Number).ConfigureAwait(false); - await Task.WhenAll(BuildNotesFromComment(pullRequest.Body, pullRequest.User), Task.WhenAll(comments.Select(x => BuildNotesFromComment(x.Body, x.User)))).ConfigureAwait(false); + var comments = await client.Issue.Comment.GetAllForIssue(RepoOwner, RepoName, fullPR.Number).ConfigureAwait(false); + await Task.WhenAll(BuildNotesFromComment(fullPR.Body, fullPR.User), Task.WhenAll(comments.Select(x => BuildNotesFromComment(x.Body, x.User)))).ConfigureAwait(false); } var tasks = new List(); diff --git a/tools/ReleaseNotes/release_notes.md b/tools/ReleaseNotes/release_notes.md deleted file mode 100644 index 25c9366336..0000000000 --- a/tools/ReleaseNotes/release_notes.md +++ /dev/null @@ -1,17 +0,0 @@ -See https://tgstation.github.io/tgstation-server for installation instructions - -## [Changelog for 2.x](https://github.com/tgstation/tgstation-server/milestone/13?closed=1) - -- Added `/datum/tgs_version` to the DMAPI. `/world/TgsVersion()`, `/world/TgsMaximumAPIVersion()`, and `/world/TgsMinimumAPIVersion()` now return this datum (#827) - -## [Changelog for 1.X](https://github.com/tgstation/tgstation-server/milestone/11?closed=1) - -- Added the command line setup wizard. No more need to configure `appsettings.Production.json` manually -- You can now ignore files and folder for symlinking in the `GameStaticFiles` directory using the `.tgsignore` file -- If the repository is configured with valid GitHub synchronization credentials, the user account will now post [test merge comments](https://github.com/tgstation/tgstation/pull/40735#issuecomment-427503427) [when deployments involving them happen](https://github.com/tgstation/tgstation/pull/40736#issuecomment-427503428) -- Releases now contain a `.zip` of the latest DMAPI files -- Installing DirectX on Windows is no longer required for BYOND versions >= 512.1452 -- Updating the server should no longer timeout clients -- Various kinks were ironed out in internal systems - -# [Initial Release](https://github.com/tgstation/tgstation-server/milestone/3?closed=1) \ No newline at end of file