diff --git a/.github/workflows/ci-pipeline.yml b/.github/workflows/ci-pipeline.yml index b61a1e318e0..bdb5180462e 100644 --- a/.github/workflows/ci-pipeline.yml +++ b/.github/workflows/ci-pipeline.yml @@ -1276,7 +1276,7 @@ jobs: path: ./code_coverage/integration_tests/windows_integration_tests_release_system_sqlite - name: Upload Coverage to CodeCov - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@v5 with: directory: ./code_coverage fail_ci_if_error: true @@ -1538,6 +1538,10 @@ jobs: if (Test-Path -Path $log -PathType Leaf) { Get-Content $log } + $potentialMsiLog = [System.IO.Path]::GetFullPath("install_000_tgstation_server.msi.log") + if (Test-Path -Path $potentialMsiLog -PathType Leaf) { + Get-Content $potentialMsiLog + } $installCode = $procMain.ExitCode if($installCode -ne 0) { Write-Host "ERROR INSTALLER EXIT CODE $installCode" diff --git a/build/TestCommon.props b/build/TestCommon.props index 719e316c8ea..02a6453fe86 100644 --- a/build/TestCommon.props +++ b/build/TestCommon.props @@ -18,9 +18,9 @@ - + - + diff --git a/build/Version.props b/build/Version.props index 02bbc0e30a4..8209b0f18a0 100644 --- a/build/Version.props +++ b/build/Version.props @@ -8,14 +8,13 @@ 10.12.0 0.5.0 7.0.0 - 16.3.0 - 19.3.0 + 17.0.0 + 20.0.0 7.3.0 5.10.0 1.6.0 8.0.0 1.2.1 - 2.0.0 netstandard2.0 8 diff --git a/build/package/winget/Tgstation.Server.Host.Service.Wix.Bundle/Bundle.wxs b/build/package/winget/Tgstation.Server.Host.Service.Wix.Bundle/Bundle.wxs index 9caccc81aee..ac3386ff60e 100644 --- a/build/package/winget/Tgstation.Server.Host.Service.Wix.Bundle/Bundle.wxs +++ b/build/package/winget/Tgstation.Server.Host.Service.Wix.Bundle/Bundle.wxs @@ -1,7 +1,7 @@ - + diff --git a/build/package/winget/Tgstation.Server.Host.Service.Wix.Extensions/InstallationExtensions.cs b/build/package/winget/Tgstation.Server.Host.Service.Wix.Extensions/InstallationExtensions.cs index d6a46b77006..ddecf51c9a0 100644 --- a/build/package/winget/Tgstation.Server.Host.Service.Wix.Extensions/InstallationExtensions.cs +++ b/build/package/winget/Tgstation.Server.Host.Service.Wix.Extensions/InstallationExtensions.cs @@ -16,7 +16,7 @@ public static class InstallationExtensions /// /// Package name /// - /// As much as I'd like to use Tgstation.Server.Common.Constants.CanonicalPackageName here, attempting to reference it makes Tgstation.Server.Migrator.Comms fail due to referencing the net2.0 version of that library. EVEN THOUGH IT'S A TRANSITIVE DEPENDENCY OF Tgstation.Server.Client!!!!! If that dead-ass tool has been removed, feel free to do this. + /// As much as I'd like to use Tgstation.Server.Common.Constants.CanonicalPackageName here, attempting to reference it makes VS go crazy with fake errors. const string CanonicalPackageName = "tgstation-server"; /// diff --git a/build/package/winget/Tgstation.Server.Host.Service.Wix/Package.wxs b/build/package/winget/Tgstation.Server.Host.Service.Wix/Package.wxs index 240250b62f4..bde9181f975 100644 --- a/build/package/winget/Tgstation.Server.Host.Service.Wix/Package.wxs +++ b/build/package/winget/Tgstation.Server.Host.Service.Wix/Package.wxs @@ -48,6 +48,10 @@ + + + + diff --git a/build/package/winget/Tgstation.Server.Host.Service.Wix/Tgstation.Server.Host.Service.Wix.wixproj b/build/package/winget/Tgstation.Server.Host.Service.Wix/Tgstation.Server.Host.Service.Wix.wixproj index 9ca13531e21..272694a58e5 100644 --- a/build/package/winget/Tgstation.Server.Host.Service.Wix/Tgstation.Server.Host.Service.Wix.wixproj +++ b/build/package/winget/Tgstation.Server.Host.Service.Wix/Tgstation.Server.Host.Service.Wix.wixproj @@ -18,11 +18,6 @@ true true - - ServiceHostWatchdogComponentGroup - ApplicationDirectory - true - diff --git a/src/Tgstation.Server.Api/Tgstation.Server.Api.csproj b/src/Tgstation.Server.Api/Tgstation.Server.Api.csproj index 73092b2ba6e..a7aafbcc6cf 100644 --- a/src/Tgstation.Server.Api/Tgstation.Server.Api.csproj +++ b/src/Tgstation.Server.Api/Tgstation.Server.Api.csproj @@ -28,9 +28,10 @@ - + + diff --git a/src/Tgstation.Server.Client.GraphQL/Tgstation.Server.Client.GraphQL.csproj b/src/Tgstation.Server.Client.GraphQL/Tgstation.Server.Client.GraphQL.csproj index 780cbbcd7cf..34f1cda13c0 100644 --- a/src/Tgstation.Server.Client.GraphQL/Tgstation.Server.Client.GraphQL.csproj +++ b/src/Tgstation.Server.Client.GraphQL/Tgstation.Server.Client.GraphQL.csproj @@ -9,8 +9,8 @@ - - + + diff --git a/src/Tgstation.Server.Client/Tgstation.Server.Client.csproj b/src/Tgstation.Server.Client/Tgstation.Server.Client.csproj index 5046c091a88..91278a6dcde 100644 --- a/src/Tgstation.Server.Client/Tgstation.Server.Client.csproj +++ b/src/Tgstation.Server.Client/Tgstation.Server.Client.csproj @@ -11,9 +11,9 @@ - + - + diff --git a/src/Tgstation.Server.Common/Tgstation.Server.Common.csproj b/src/Tgstation.Server.Common/Tgstation.Server.Common.csproj index b23195dfca9..4b119caf8b1 100644 --- a/src/Tgstation.Server.Common/Tgstation.Server.Common.csproj +++ b/src/Tgstation.Server.Common/Tgstation.Server.Common.csproj @@ -12,7 +12,7 @@ - + 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 9804e251e3f..57b784056e3 100644 --- a/src/Tgstation.Server.Host.Console/Tgstation.Server.Host.Console.csproj +++ b/src/Tgstation.Server.Host.Console/Tgstation.Server.Host.Console.csproj @@ -13,9 +13,9 @@ - + - + diff --git a/src/Tgstation.Server.Host.Service/Program.cs b/src/Tgstation.Server.Host.Service/Program.cs index 4a9d39fcd21..53623ff2b80 100644 --- a/src/Tgstation.Server.Host.Service/Program.cs +++ b/src/Tgstation.Server.Host.Service/Program.cs @@ -190,10 +190,9 @@ void InvokeSC(string? serviceToUninstall) // Mimicing Tgstation.Server.Host.Service.Wix here, which is the source of truth for this data installer.Context = new InstallContext( Path.Combine(programDataDirectory, $"tgs-install-{Guid.NewGuid()}.log"), - new[] - { + [ $"assemblypath=\"{exePath}\"{(String.IsNullOrWhiteSpace(PassthroughArgs) ? String.Empty : $" -p=\"{PassthroughArgs}\"")}", - }); + ]); installer.Description = $"{Server.Common.Constants.CanonicalPackageName} running as a Windows service."; installer.DisplayName = Server.Common.Constants.CanonicalPackageName; installer.StartType = ServiceStartMode.Automatic; diff --git a/src/Tgstation.Server.Host.Service/Tgstation.Server.Host.Service.csproj b/src/Tgstation.Server.Host.Service/Tgstation.Server.Host.Service.csproj index e1f04e898b2..80a62d106df 100644 --- a/src/Tgstation.Server.Host.Service/Tgstation.Server.Host.Service.csproj +++ b/src/Tgstation.Server.Host.Service/Tgstation.Server.Host.Service.csproj @@ -21,15 +21,21 @@ - + - + - + + + + + + + - + 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 dad6a144544..7b46bb024a6 100644 --- a/src/Tgstation.Server.Host.Watchdog/Tgstation.Server.Host.Watchdog.csproj +++ b/src/Tgstation.Server.Host.Watchdog/Tgstation.Server.Host.Watchdog.csproj @@ -10,7 +10,7 @@ - + diff --git a/src/Tgstation.Server.Host/.config/dotnet-tools.json b/src/Tgstation.Server.Host/.config/dotnet-tools.json index 6b7095305f3..02afa3f0723 100644 --- a/src/Tgstation.Server.Host/.config/dotnet-tools.json +++ b/src/Tgstation.Server.Host/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "dotnet-ef": { - "version": "8.0.10", + "version": "8.0.11", "commands": [ "dotnet-ef" ] diff --git a/src/Tgstation.Server.Host/Components/Watchdog/WatchdogBase.cs b/src/Tgstation.Server.Host/Components/Watchdog/WatchdogBase.cs index e0aff03f405..7fa4fc4926d 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/WatchdogBase.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/WatchdogBase.cs @@ -1,11 +1,10 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using System.Threading; using System.Threading.Tasks; -using BetterWin32Errors; - using Microsoft.Extensions.Logging; using Serilog.Context; diff --git a/src/Tgstation.Server.Host/IO/WindowsFilesystemLinkFactory.cs b/src/Tgstation.Server.Host/IO/WindowsFilesystemLinkFactory.cs index 6636838ab42..54ff36b3205 100644 --- a/src/Tgstation.Server.Host/IO/WindowsFilesystemLinkFactory.cs +++ b/src/Tgstation.Server.Host/IO/WindowsFilesystemLinkFactory.cs @@ -1,10 +1,9 @@ using System; +using System.ComponentModel; using System.IO; using System.Threading; using System.Threading.Tasks; -using BetterWin32Errors; - using Tgstation.Server.Host.System; namespace Tgstation.Server.Host.IO diff --git a/src/Tgstation.Server.Host/System/WindowsNetworkPromptReaper.cs b/src/Tgstation.Server.Host/System/WindowsNetworkPromptReaper.cs index 9bbcd497ce0..bd7b1f645ec 100644 --- a/src/Tgstation.Server.Host/System/WindowsNetworkPromptReaper.cs +++ b/src/Tgstation.Server.Host/System/WindowsNetworkPromptReaper.cs @@ -1,13 +1,12 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Tasks; -using BetterWin32Errors; - using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; diff --git a/src/Tgstation.Server.Host/System/WindowsProcessFeatures.cs b/src/Tgstation.Server.Host/System/WindowsProcessFeatures.cs index 6b71b2b2539..21855c0062e 100644 --- a/src/Tgstation.Server.Host/System/WindowsProcessFeatures.cs +++ b/src/Tgstation.Server.Host/System/WindowsProcessFeatures.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Linq; @@ -8,7 +9,6 @@ using System.Threading; using System.Threading.Tasks; -using BetterWin32Errors; using Microsoft.Extensions.Logging; using Tgstation.Server.Api.Models; diff --git a/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj b/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj index 570b627d6e0..084b814d7c0 100644 --- a/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj +++ b/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj @@ -87,48 +87,46 @@ - - - + - + - + - + - + - + - + - + - + - + - + - + runtime; build; native; contentfiles; analyzers; buildtransitive - + - + @@ -136,7 +134,7 @@ - + @@ -144,7 +142,7 @@ - + @@ -152,15 +150,15 @@ - + - + - + - + - + diff --git a/src/Tgstation.Server.Shared/Tgstation.Server.Shared.csproj b/src/Tgstation.Server.Shared/Tgstation.Server.Shared.csproj index 14c6e899813..ed81c623411 100644 --- a/src/Tgstation.Server.Shared/Tgstation.Server.Shared.csproj +++ b/src/Tgstation.Server.Shared/Tgstation.Server.Shared.csproj @@ -10,11 +10,11 @@ - + - + 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 c2c09fe7b87..0e74bf0b36b 100644 --- a/tests/Tgstation.Server.Client.Tests/Tgstation.Server.Client.Tests.csproj +++ b/tests/Tgstation.Server.Client.Tests/Tgstation.Server.Client.Tests.csproj @@ -6,7 +6,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 a0e5d2ba452..69c414d1e2e 100644 --- a/tests/Tgstation.Server.Host.Tests/Tgstation.Server.Host.Tests.csproj +++ b/tests/Tgstation.Server.Host.Tests/Tgstation.Server.Host.Tests.csproj @@ -7,7 +7,7 @@ - + diff --git a/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs b/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs index aff9e458671..df2476444b3 100644 --- a/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs +++ b/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs @@ -586,13 +586,13 @@ async Task DumpTests(bool mini, CancellationToken cancellationToken) // these can also happen if (!(new PlatformIdentifier().IsWindows - && (job.ExceptionDetails.Contains("BetterWin32Errors.Win32Exception: E_ACCESSDENIED: Access is denied.") - || job.ExceptionDetails.Contains("BetterWin32Errors.Win32Exception: E_HANDLE: The handle is invalid.") - || job.ExceptionDetails.Contains("BetterWin32Errors.Win32Exception: 3489660936: Unknown error (0xd0000008)") - || job.ExceptionDetails.Contains("System.InvalidOperationException: No process is associated with this object.") - || job.ExceptionDetails.Contains("BetterWin32Errors.Win32Exception: 2147942424: The program issued a command but the command length is incorrect.") - || job.ExceptionDetails.Contains("BetterWin32Errors.Win32Exception: 2147942699: Only part of a ReadProcessMemory or WriteProcessMemory request was completed.") - || job.ExceptionDetails.Contains("BetterWin32Errors.Win32Exception: 3489660964: Unknown error (0xd0000024)")))) + && (job.ExceptionDetails.Contains("Access is denied.") + || job.ExceptionDetails.Contains("The handle is invalid.") + || job.ExceptionDetails.Contains("Unknown error") + || job.ExceptionDetails.Contains("No process is associated with this object.") + || job.ExceptionDetails.Contains("The program issued a command but the command length is incorrect.") + || job.ExceptionDetails.Contains("Only part of a ReadProcessMemory or WriteProcessMemory request was completed.") + || job.ExceptionDetails.Contains("Unknown error")))) break; var restartJob = await instanceClient.DreamDaemon.Restart(cancellationToken); diff --git a/tests/Tgstation.Server.Tests/Tgstation.Server.Tests.csproj b/tests/Tgstation.Server.Tests/Tgstation.Server.Tests.csproj index e2651d56a29..84fb6104916 100644 --- a/tests/Tgstation.Server.Tests/Tgstation.Server.Tests.csproj +++ b/tests/Tgstation.Server.Tests/Tgstation.Server.Tests.csproj @@ -6,7 +6,7 @@ - + diff --git a/tgstation-server.sln b/tgstation-server.sln index 31d15d01590..8815c70e786 100644 --- a/tgstation-server.sln +++ b/tgstation-server.sln @@ -197,13 +197,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ISSUE_TEMPLATE", "ISSUE_TEM EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tgstation.Server.Host.Tests.Signals", "tests\Tgstation.Server.Host.Tests.Signals\Tgstation.Server.Host.Tests.Signals.csproj", "{5813CC33-B16C-485D-A74D-20204DDF6542}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tgstation.Server.Migrator", "tools\Tgstation.Server.Migrator\Tgstation.Server.Migrator.csproj", "{CE499888-B22B-457C-891E-0EA9DC317228}" - ProjectSection(ProjectDependencies) = postProject - {07ED0FD5-E46B-4841-931D-BA2B673E16B2} = {07ED0FD5-E46B-4841-931D-BA2B673E16B2} - EndProjectSection -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tgstation.Server.Migrator.Comms", "tools\Tgstation.Server.Migrator.Comms\Tgstation.Server.Migrator.Comms.csproj", "{07ED0FD5-E46B-4841-931D-BA2B673E16B2}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tgstation.Server.Host.Common", "src\Tgstation.Server.Host.Common\Tgstation.Server.Host.Common.csproj", "{CF3968A0-EA81-4464-B2D4-C7D40F6B5BCB}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tgstation.Server.Common", "src\Tgstation.Server.Common\Tgstation.Server.Common.csproj", "{70CD9A98-D31A-44A4-81D1-D02764CEEEFD}" @@ -274,6 +267,14 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tgstation.Server.Shared.Tes EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tgstation.Server.Client.GraphQL", "src\Tgstation.Server.Client.GraphQL\Tgstation.Server.Client.GraphQL.csproj", "{8BF95E2D-FD27-470C-82B7-C21AC01BFBD7}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "nix", "nix", "{5130526C-A553-493B-A9B0-3DB452949886}" + ProjectSection(SolutionItems) = preProject + build\package\nix\flake.nix = build\package\nix\flake.nix + build\package\nix\package.nix = build\package\nix\package.nix + build\package\nix\ServerConsole.sha256 = build\package\nix\ServerConsole.sha256 + build\package\nix\tgstation-server.nix = build\package\nix\tgstation-server.nix + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -448,30 +449,6 @@ Global {5813CC33-B16C-485D-A74D-20204DDF6542}.ReleaseNoWindows|Any CPU.Build.0 = Release|Any CPU {5813CC33-B16C-485D-A74D-20204DDF6542}.ReleaseNoWix|Any CPU.ActiveCfg = Release|Any CPU {5813CC33-B16C-485D-A74D-20204DDF6542}.ReleaseNoWix|Any CPU.Build.0 = Release|Any CPU - {CE499888-B22B-457C-891E-0EA9DC317228}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CE499888-B22B-457C-891E-0EA9DC317228}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CE499888-B22B-457C-891E-0EA9DC317228}.DebugNoWindows|Any CPU.ActiveCfg = Debug|Any CPU - {CE499888-B22B-457C-891E-0EA9DC317228}.DebugNoWindows|Any CPU.Build.0 = Debug|Any CPU - {CE499888-B22B-457C-891E-0EA9DC317228}.DebugNoWix|Any CPU.ActiveCfg = Debug|Any CPU - {CE499888-B22B-457C-891E-0EA9DC317228}.DebugNoWix|Any CPU.Build.0 = Debug|Any CPU - {CE499888-B22B-457C-891E-0EA9DC317228}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CE499888-B22B-457C-891E-0EA9DC317228}.Release|Any CPU.Build.0 = Release|Any CPU - {CE499888-B22B-457C-891E-0EA9DC317228}.ReleaseNoWindows|Any CPU.ActiveCfg = Release|Any CPU - {CE499888-B22B-457C-891E-0EA9DC317228}.ReleaseNoWindows|Any CPU.Build.0 = Release|Any CPU - {CE499888-B22B-457C-891E-0EA9DC317228}.ReleaseNoWix|Any CPU.ActiveCfg = Release|Any CPU - {CE499888-B22B-457C-891E-0EA9DC317228}.ReleaseNoWix|Any CPU.Build.0 = Release|Any CPU - {07ED0FD5-E46B-4841-931D-BA2B673E16B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {07ED0FD5-E46B-4841-931D-BA2B673E16B2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {07ED0FD5-E46B-4841-931D-BA2B673E16B2}.DebugNoWindows|Any CPU.ActiveCfg = Debug|Any CPU - {07ED0FD5-E46B-4841-931D-BA2B673E16B2}.DebugNoWindows|Any CPU.Build.0 = Debug|Any CPU - {07ED0FD5-E46B-4841-931D-BA2B673E16B2}.DebugNoWix|Any CPU.ActiveCfg = Debug|Any CPU - {07ED0FD5-E46B-4841-931D-BA2B673E16B2}.DebugNoWix|Any CPU.Build.0 = Debug|Any CPU - {07ED0FD5-E46B-4841-931D-BA2B673E16B2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {07ED0FD5-E46B-4841-931D-BA2B673E16B2}.Release|Any CPU.Build.0 = Release|Any CPU - {07ED0FD5-E46B-4841-931D-BA2B673E16B2}.ReleaseNoWindows|Any CPU.ActiveCfg = Release|Any CPU - {07ED0FD5-E46B-4841-931D-BA2B673E16B2}.ReleaseNoWindows|Any CPU.Build.0 = Release|Any CPU - {07ED0FD5-E46B-4841-931D-BA2B673E16B2}.ReleaseNoWix|Any CPU.ActiveCfg = Release|Any CPU - {07ED0FD5-E46B-4841-931D-BA2B673E16B2}.ReleaseNoWix|Any CPU.Build.0 = Release|Any CPU {CF3968A0-EA81-4464-B2D4-C7D40F6B5BCB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CF3968A0-EA81-4464-B2D4-C7D40F6B5BCB}.Debug|Any CPU.Build.0 = Debug|Any CPU {CF3968A0-EA81-4464-B2D4-C7D40F6B5BCB}.DebugNoWindows|Any CPU.ActiveCfg = Debug|Any CPU @@ -605,8 +582,6 @@ Global {103C61AB-67D6-46FE-AA47-CC633B88EE0F} = {82066812-6C73-4360-943B-B23F2F491261} {CFFD7992-E73A-4D1F-9D7A-C817C07B7BEB} = {E82104F4-F5C4-4786-ACD4-B635166CDB21} {5813CC33-B16C-485D-A74D-20204DDF6542} = {316141B0-CD21-4769-A013-D53DA9B9EC09} - {CE499888-B22B-457C-891E-0EA9DC317228} = {A55C1117-5808-4AB2-BEA6-4D4A3E66A2F2} - {07ED0FD5-E46B-4841-931D-BA2B673E16B2} = {A55C1117-5808-4AB2-BEA6-4D4A3E66A2F2} {2648A85F-61AE-428E-95E1-66D06C7A3768} = {6FF654E6-3E2C-46D4-872D-D528F77D6973} {457A1F89-6201-4430-BCC6-2F4438A54B9E} = {2648A85F-61AE-428E-95E1-66D06C7A3768} {08E7C650-A447-4DE2-974E-ED123B50F8D6} = {457A1F89-6201-4430-BCC6-2F4438A54B9E} @@ -619,6 +594,7 @@ Global {69944039-65C2-40E1-9D86-0608FA0C2D70} = {A55C1117-5808-4AB2-BEA6-4D4A3E66A2F2} {7F7FCFDF-271D-45C2-830C-BCCB19C57077} = {A55C1117-5808-4AB2-BEA6-4D4A3E66A2F2} {EAB84FD0-5514-4254-B188-7D90ACB7284D} = {316141B0-CD21-4769-A013-D53DA9B9EC09} + {5130526C-A553-493B-A9B0-3DB452949886} = {2648A85F-61AE-428E-95E1-66D06C7A3768} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {DFD36C95-3E49-41C7-ACDB-86BAF5B18A79} diff --git a/tools/Tgstation.Server.LogoGenerator/Program.cs b/tools/Tgstation.Server.LogoGenerator/Program.cs index 2ec845f18d5..820084663a1 100644 --- a/tools/Tgstation.Server.LogoGenerator/Program.cs +++ b/tools/Tgstation.Server.LogoGenerator/Program.cs @@ -3,6 +3,7 @@ using System.Drawing; using System.Drawing.Imaging; using System.IO; +using System.Runtime.InteropServices; using System.Text; using Svg; @@ -25,7 +26,45 @@ using var bitmap = svg.Draw(128, 128); using var whiteBgBitmap = whiteBgSvg.Draw(160, 160); -using var icon = Icon.FromHandle(whiteBgBitmap.GetHicon()); +// https://stackoverflow.com/questions/21387391/how-to-convert-an-image-to-an-icon-without-losing-transparency/21389253#21389253 +// slight modifications +static Icon IconFromImage(Image img) +{ + using var ms = new MemoryStream(); + using var bw = new BinaryWriter(ms); + // Header + bw.Write((short)0); // 0 : reserved + bw.Write((short)1); // 2 : 1=ico, 2=cur + bw.Write((short)1); // 4 : number of images + // Image directory + var w = img.Width; + if (w >= 256) throw new InvalidOperationException("Width too big!"); + bw.Write((byte)w); // 0 : width of image + var h = img.Height; + if (h >= 256) throw new InvalidOperationException("Height too big!"); + bw.Write((byte)h); // 1 : height of image + bw.Write((byte)0); // 2 : number of colors in palette + bw.Write((byte)0); // 3 : reserved + bw.Write((short)0); // 4 : number of color planes + bw.Write((short)0); // 6 : bits per pixel + var sizeHere = ms.Position; + bw.Write(0); // 8 : image size + var start = (int)ms.Position + 4; + bw.Write(start); // 12: offset of image data + // Image data + img.Save(ms, ImageFormat.Png); + var imageSize = (int)ms.Position - start; + ms.Seek(sizeHere, SeekOrigin.Begin); + bw.Write(imageSize); + ms.Seek(0, SeekOrigin.Begin); + + // And load it + return new Icon(ms); +} + +using var icon = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) + ? IconFromImage(whiteBgBitmap) + : Icon.FromHandle(whiteBgBitmap.GetHicon()); Directory.CreateDirectory("artifacts"); await using var iconStream = new FileStream("artifacts/tgs.ico", FileMode.Create); diff --git a/tools/Tgstation.Server.Migrator.Comms/Program.cs b/tools/Tgstation.Server.Migrator.Comms/Program.cs deleted file mode 100644 index 3c46d8a1cca..00000000000 --- a/tools/Tgstation.Server.Migrator.Comms/Program.cs +++ /dev/null @@ -1,389 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net.Http.Headers; -using System.Reflection; -using System.Text; -using System.Threading.Tasks; - -using TGS.Interface; - -using Tgstation.Server.Api; -using Tgstation.Server.Api.Models; -using Tgstation.Server.Api.Models.Internal; -using Tgstation.Server.Api.Models.Request; -using Tgstation.Server.Client; -using Tgstation.Server.Common.Extensions; - -static class Program -{ - static async Task Main(string[] args) - { - try - { - var tgs3Client = new Client(); - - switch (args[0]) - { - case "--verify-connection": - var status = tgs3Client.ConnectionStatus(out var error); - if (status != ConnectivityLevel.Administrator) - { - Console.WriteLine($"Connection Error: {error}"); - return 3; - } - return 0; - case "--migrate": - ushort apiPort = ushort.Parse(args[1]); - return await Migrate(tgs3Client, apiPort); - default: - return 2; - } - } - catch (Exception ex) - { - Console.WriteLine(ex); - return 1; - } - } - - static async Task Migrate(IClient tgs3Client, ushort apiPort) - { -#if DEBUG - Console.WriteLine("Test log line..."); - System.Diagnostics.Debugger.Launch(); -#endif - Console.WriteLine("Connecting to TGS3..."); - var status = tgs3Client.ConnectionStatus(out var tgs3Error); - if (status != ConnectivityLevel.Administrator) - { - Console.WriteLine($"Connection Error: {tgs3Client}"); - return 13; - } - - Console.WriteLine("Connected!"); - - Console.WriteLine("Connecting to TGS6..."); - var assemblyName = Assembly.GetExecutingAssembly().GetName(); - var productInfoHeaderValue = - new ProductInfoHeaderValue( - assemblyName.Name!, - assemblyName.Version!.Semver().ToString()); - - var serverUrl = new Uri($"http://localhost:{apiPort}"); - var clientFactory = new RestServerClientFactory(productInfoHeaderValue.Product); - var TGS6Client = await clientFactory.CreateFromLogin( - serverUrl, - DefaultCredentials.AdminUserName, - DefaultCredentials.DefaultAdminUserPassword); - - Console.WriteLine("Connected!"); - - // we do this synchronously and patiently because we aren't chumbii and this is delicate - // We need clear logs - var tgs3Instances = tgs3Client.Server.Instances.ToList(); - foreach (var tgs3Instance in tgs3Instances) - { - var instanceName = tgs3Instance.Metadata.Name; - var instancePath = tgs3Instance.Metadata.Path; - - if (!tgs3Instance.Metadata.Enabled) - { - Console.WriteLine($"Skipping instance {instanceName} at {instancePath}. Disabled."); - continue; - } - - Console.WriteLine($"Migrating instance {instanceName} at {instancePath}"); - - RepositoryUpdateRequest? repositoryUpdateRequest = null; - if (tgs3Instance.Repository.Exists()) - { - Console.WriteLine("Gathering instance repository data..."); - repositoryUpdateRequest = new RepositoryUpdateRequest - { - CommitterEmail = tgs3Instance.Repository.GetCommitterEmail(), - CommitterName = tgs3Instance.Repository.GetCommitterName(), - UpdateSubmodules = true, // default in 3 - Reference = tgs3Instance.Repository.GetBranch(out tgs3Error), - }; - - if (tgs3Error != null) - { - Console.WriteLine($"Error retrieving current branch: {tgs3Error}"); - } - } - else - Console.WriteLine("Instance has no repository, that's fine."); - - Console.WriteLine("Gather DreamDaemon and DreamMaker data..."); - var dreamDaemonRequest = new DreamDaemonRequest - { - AllowWebClient = tgs3Instance.DreamDaemon.Webclient(), - AutoStart = tgs3Instance.DreamDaemon.Autostart(), - Port = tgs3Instance.DreamDaemon.Port(), - SecurityLevel = tgs3Instance.DreamDaemon.SecurityLevel() switch - { - TGS.Interface.DreamDaemonSecurity.Safe => Tgstation.Server.Api.Models.DreamDaemonSecurity.Safe, - TGS.Interface.DreamDaemonSecurity.Ultrasafe => Tgstation.Server.Api.Models.DreamDaemonSecurity.Ultrasafe, - _ => Tgstation.Server.Api.Models.DreamDaemonSecurity.Trusted, - } - }; - - var dreamMakerRequest = new DreamMakerRequest - { - ApiValidationSecurityLevel = dreamDaemonRequest.SecurityLevel, - ApiValidationPort = (ushort)(dreamDaemonRequest.Port + 111) // Best rotation we can do... - }; - - Console.WriteLine("Gathering chat data..."); - var providerInfos = tgs3Instance.Chat.ProviderInfos(); - var chatBotCreateRequests = new List(); - foreach(var providerInfo in providerInfos) - { - if (!providerInfo.Enabled) - continue; - - var createRequest = new ChatBotCreateRequest() - { - Provider = providerInfo.Provider switch - { - TGS.Interface.ChatProvider.Discord => Tgstation.Server.Api.Models.ChatProvider.Discord, - _ => Tgstation.Server.Api.Models.ChatProvider.Irc, - }, - Enabled = true, - ReconnectionInterval = 5, - }; - - var isDiscordProvider = createRequest.Provider == Tgstation.Server.Api.Models.ChatProvider.Discord; - createRequest.Name = isDiscordProvider - ? "Discord Bot" - : "IRC Bot"; - - Console.WriteLine($"Gathering data for {createRequest.Name}..."); - - ChatConnectionStringBuilder csb; - if (createRequest.Provider == Tgstation.Server.Api.Models.ChatProvider.Discord) - { - var discordSetupInfo = new DiscordSetupInfo(providerInfo); - csb = new DiscordConnectionStringBuilder - { - DMOutputDisplay = DiscordDMOutputDisplayType.Always, - BotToken = discordSetupInfo.BotToken - }; - - } - else - { - var ircSetupInfo = new IRCSetupInfo(providerInfo); - csb = new IrcConnectionStringBuilder - { - Address = ircSetupInfo.URL, - Nickname = ircSetupInfo.Nickname, - Port = ircSetupInfo.Port - }; - } - createRequest.ConnectionString = csb.ToString(); - - createRequest.Channels = new List(); - - static string NormalizeChannelId(string channelId) => channelId.ToLowerInvariant().Trim(); - - var distinctChannels = providerInfo.WatchdogChannels - .Union(providerInfo.DevChannels) - .Union(providerInfo.AdminChannels) - .Union(providerInfo.GameChannels) - .Select(NormalizeChannelId) - .Distinct(); - - foreach(var channelIdentifier in distinctChannels) - { - var newChatChannel = new ChatChannel - { - IsWatchdogChannel = providerInfo.WatchdogChannels.Any(x => NormalizeChannelId(x) == channelIdentifier), - IsAdminChannel = providerInfo.AdminChannels.Any(x => NormalizeChannelId(x) == channelIdentifier), - IsUpdatesChannel = providerInfo.DevChannels.Any(x => NormalizeChannelId(x) == channelIdentifier), - // system channels are too new a feature to target - ChannelData = channelIdentifier, - }; - - createRequest.Channels.Add(newChatChannel); - } - - chatBotCreateRequests.Add(createRequest); - } - - Console.WriteLine("Detaching TGS3 instance..."); - tgs3Client.Server.InstanceManager.DetachInstance(instanceName); - - Console.WriteLine("Creating TGS6 attach file..."); - File.WriteAllText(Path.Combine(instancePath, "TGS4_ALLOW_INSTANCE_ATTACH"), String.Empty); - - Console.WriteLine("Checking BYOND install..."); - var byondDirectory = Path.Combine(instancePath, "BYOND"); - var byondVersionFile = Path.Combine(byondDirectory, "byond_version.dat"); - - EngineVersionRequest? byondVersionRequest = null; - if (Directory.Exists(byondDirectory) && File.Exists(byondVersionFile)) - { - var byondVersion = Version.Parse(File.ReadAllText(byondVersionFile).Trim()); - Console.WriteLine($"Found installed BYOND version: {byondVersion.Major}.{byondVersion.Minor}"); - byondVersionRequest = new EngineVersionRequest - { - EngineVersion = new EngineVersion - { - Version = byondVersion - } - }; - } - - var oldStaticDirectory = Path.Combine(instancePath, "Static"); - var newConfigurationDirectory = Path.Combine(instancePath, "Configuration"); - if (Directory.Exists(oldStaticDirectory)) - { - Console.WriteLine("Migrating Static to Configuration/GameStaticFiles"); - var gameStaticFilesDirectory = Path.Combine(newConfigurationDirectory, "GameStaticFiles"); - Directory.CreateDirectory(newConfigurationDirectory); - Directory.Move(oldStaticDirectory, gameStaticFilesDirectory); - - Console.WriteLine("Moving code modifications..."); - var codeModsDirectory = Path.Combine(newConfigurationDirectory, "CodeModifications"); - Directory.CreateDirectory(codeModsDirectory); - - var allDmFiles = Directory.EnumerateFiles(gameStaticFilesDirectory, "*.dm", SearchOption.TopDirectoryOnly).ToList(); - foreach (var dmFile in allDmFiles) - { - File.Move(Path.Combine(gameStaticFilesDirectory, dmFile), Path.Combine(codeModsDirectory, Path.GetFileName(dmFile))); - } - - var allDmeFiles = Directory.EnumerateFiles(gameStaticFilesDirectory, "*.dme", SearchOption.TopDirectoryOnly).ToList(); - if (allDmeFiles.Any()) - { - foreach (var dmeFile in allDmeFiles) - { - File.Move(Path.Combine(gameStaticFilesDirectory, dmeFile), Path.Combine(codeModsDirectory, Path.GetFileName(dmeFile))); - } - } - else if (allDmFiles.Any()) - { - Console.WriteLine("Generating HeadInclude.dm..."); - var headIncludeBuilder = new StringBuilder(); - foreach (var dmFile in allDmFiles.OrderBy(fileName => fileName.ToUpperInvariant())) - { - headIncludeBuilder.Append("#include \""); - headIncludeBuilder.Append(Path.GetFileName(dmFile)); - headIncludeBuilder.Append("\""); - headIncludeBuilder.Append(Environment.NewLine); - } - - File.WriteAllText(Path.Combine(codeModsDirectory, "HeadInclude.dm"), headIncludeBuilder.ToString()); - } - } - - var eventHandlersDirectory = Path.Combine(instancePath, "EventHandlers"); - if (Directory.Exists(eventHandlersDirectory)) - { - Console.WriteLine("Moving event scripts..."); - Directory.CreateDirectory(newConfigurationDirectory); - Directory.Move(eventHandlersDirectory, Path.Combine(newConfigurationDirectory, "EventScripts")); - } - - var diagnosticsDirectory = Path.Combine(instancePath, "Diagnostics"); - var minidumpsDirectory = Path.Combine(diagnosticsDirectory, "Minidumps"); - if (Directory.Exists(minidumpsDirectory)) - { - Console.WriteLine("Renaming Minidumps folder to ProcessDumps..."); - Directory.Move(minidumpsDirectory, Path.Combine(diagnosticsDirectory, "ProcessDumps")); - } - - Console.WriteLine("Deleting BYOND folder..."); - await RecursivelyDeleteDirectory(new DirectoryInfo(byondDirectory)); - Console.WriteLine("Deleting RepoKey folder..."); - await RecursivelyDeleteDirectory(new DirectoryInfo(Path.Combine(instancePath, "RepoKey"))); - Console.WriteLine("Deleting Game folder..."); - await RecursivelyDeleteDirectory(new DirectoryInfo(Path.Combine(instancePath, "Game"))); - Console.WriteLine("Deleting Instance.json..."); - File.Delete(Path.Combine(instancePath, "Instance.json")); - Console.WriteLine("Deleting prtestjob.json..."); - File.Delete(Path.Combine(instancePath, "prtestjob.json")); - Console.WriteLine("Deleting TGS3.json..."); - File.Delete(Path.Combine(instancePath, "TGS3.json")); - Console.WriteLine("Deleting TGDreamDaemonBridge.dll..."); - File.Delete(Path.Combine(instancePath, "TGDreamDaemonBridge.dll")); - - Console.WriteLine("Attaching TGS6 instance..."); - var TGS6Instance = await TGS6Client.Instances.CreateOrAttach(new InstanceCreateRequest - { - ConfigurationType = ConfigurationType.Disallowed, - Name = instanceName, - Path = instancePath, - }, default); - - Console.WriteLine($"Onlining TGS6 instance ID {TGS6Instance.Id}..."); - TGS6Instance = await TGS6Client.Instances.Update(new InstanceUpdateRequest - { - Online = true, - Id = TGS6Instance.Id - }, default); - - var v5InstanceClient = TGS6Client.Instances.CreateClient(TGS6Instance); - - if (byondVersionRequest != null) - { - Console.WriteLine("Triggering BYOND install job..."); - await v5InstanceClient.Engine.SetActiveVersion(byondVersionRequest, null, default); - } - - if (repositoryUpdateRequest != null) - { - Console.WriteLine("Updating repository settings..."); - await v5InstanceClient.Repository.Update(repositoryUpdateRequest, default); - } - - Console.WriteLine("Updating deployment settings..."); - await v5InstanceClient.DreamMaker.Update(dreamMakerRequest, default); - - Console.WriteLine("Updating DreamDaemon settings..."); - await v5InstanceClient.DreamDaemon.Update(dreamDaemonRequest, default); - - foreach(var chatBotCreateRequest in chatBotCreateRequests) - { - Console.WriteLine($"Creating chat bot {chatBotCreateRequest.Name}..."); - await v5InstanceClient.ChatBots.Create(chatBotCreateRequest, default); - } - - Console.WriteLine($"Instance {instanceName} (TGS6 ID: {TGS6Instance.Id}) successfully migrated!"); - } - - Console.WriteLine("All enabled V3 instances migrated into V5 and detached from V3!"); - - return 0; - } - - static async Task RecursivelyDeleteDirectory(DirectoryInfo dir) - { - var tasks = new List(); - - if (!dir.Exists) - return; - - // check if we are a symbolic link - if (!dir.Attributes.HasFlag(FileAttributes.Directory) || dir.Attributes.HasFlag(FileAttributes.ReparsePoint)) - { - dir.Delete(); - return; - } - - foreach (var subDir in dir.EnumerateDirectories()) - tasks.Add(RecursivelyDeleteDirectory(subDir)); - - foreach (var file in dir.EnumerateFiles()) - { - file.Attributes = FileAttributes.Normal; - file.Delete(); - } - - await Task.WhenAll(tasks); - dir.Delete(true); - } -} diff --git a/tools/Tgstation.Server.Migrator.Comms/Tgstation.Server.Migrator.Comms.csproj b/tools/Tgstation.Server.Migrator.Comms/Tgstation.Server.Migrator.Comms.csproj deleted file mode 100644 index e43af6ce0f6..00000000000 --- a/tools/Tgstation.Server.Migrator.Comms/Tgstation.Server.Migrator.Comms.csproj +++ /dev/null @@ -1,24 +0,0 @@ - - - - - Exe - net472 - win-x86 - $(TgsMigratorVersion) - enable - - - - - - - - - - - - - - - diff --git a/tools/Tgstation.Server.Migrator/Program.cs b/tools/Tgstation.Server.Migrator/Program.cs deleted file mode 100644 index 86cabd419e5..00000000000 --- a/tools/Tgstation.Server.Migrator/Program.cs +++ /dev/null @@ -1,544 +0,0 @@ -using System; -using System.Collections.Specialized; -using System.ComponentModel; -using System.Configuration.Install; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.IO; -using System.Linq; -using System.Management; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Reflection; -using System.Security.Principal; -using System.ServiceProcess; -using System.Text.RegularExpressions; -using System.Threading.Tasks; - -using Microsoft.Extensions.Logging; - -using Octokit; - -using Tgstation.Server.Api; -using Tgstation.Server.Client; -using Tgstation.Server.Common; -using Tgstation.Server.Common.Extensions; -using Tgstation.Server.Common.Http; -using Tgstation.Server.Host.IO; -using Tgstation.Server.Host.Setup; -using Tgstation.Server.Migrator.Properties; - -using FileMode = System.IO.FileMode; - -[DoesNotReturn] -static void ExitPause(int exitCode) -{ - Console.WriteLine("Consider saving the console text to report issues. Press any key to exit..."); - Console.ReadKey(); - Environment.Exit(exitCode); -} - -try -{ - var commandLine = Environment.GetCommandLineArgs(); - var commandLineArguments = commandLine.Skip(1); - var skipPreamble = commandLineArguments.Any(x => x.Equals("--skip-preamble", StringComparison.OrdinalIgnoreCase)); - - Console.WriteLine("This is a very straightfoward script to migrate the instances of a TGS3 install into a new TGS6 install"); - - static bool PromptYesOrNo(string question) - { - Console.Write($"{question} (y/n):"); - var character = Console.ReadKey(); - Console.WriteLine(); - return character.KeyChar.ToString().ToUpperInvariant() == "Y"; - } - - // WORKING DIRECTORY CHECK - var currentAssembly = Assembly.GetExecutingAssembly(); - if(Path.GetDirectoryName(Path.GetFullPath(currentAssembly.Location))!.Replace("\\", "/").ToUpperInvariant() - != Path.GetFullPath(Environment.CurrentDirectory).Replace("\\", "/").ToUpperInvariant()) - { - Console.WriteLine("Please keep the working directory equivalent to the program directory for this migration!"); - ExitPause(8); - } - - // PREREQUISITE CHECK - if (!skipPreamble) - { - Console.WriteLine("We need to ensure you're running this program as Administrator because there are several operations we'll do that require it."); - Console.WriteLine("If not, you will be prompted to elevate this process."); - } - - // ADMINISTRATOR CHECK - static bool IsAdministrator() - { - using var identity = WindowsIdentity.GetCurrent(); - var principal = new WindowsPrincipal(identity); - return principal.IsInRole(WindowsBuiltInRole.Administrator); - } - - if (!IsAdministrator()) - { - Console.WriteLine("Not running as admin. Elevating process..."); - var selfExecutable = commandLine.First().Replace(".dll", ".exe"); - var selfArguments = String.Join(" ", commandLineArguments) + " --skip-preamble"; - using var elevatedProcess = new Process(); - elevatedProcess.StartInfo.UseShellExecute = true; - elevatedProcess.StartInfo.FileName = selfExecutable; - elevatedProcess.StartInfo.Arguments = selfArguments; - elevatedProcess.StartInfo.Verb = "runas"; - - elevatedProcess.Start(); - return; - } - - Console.WriteLine("Administrative privileges confirmed."); - - // TGS3 SERVICE CHECK - - const string PathToCommsBinary = -#if DEBUG - "../../../../../Tgstation.Server.Migrator.Comms/bin/Debug/net472/win-x86/" + -#else - "Comms/" + -#endif - "Tgstation.Server.Migrator.Comms.exe"; - - Console.WriteLine($"Checking {PathToCommsBinary} exists..."); - if (!File.Exists(PathToCommsBinary)) - { - Console.WriteLine("Could not find WCF comms binary!"); - ExitPause(7); - } - - static int RunComms(string command) - { - using var commsProcess = new Process(); - commsProcess.StartInfo.FileName = PathToCommsBinary; - commsProcess.StartInfo.Arguments = command; - commsProcess.Start(); - commsProcess.WaitForExit(); - return commsProcess.ExitCode; - } - - Console.WriteLine("Checking for TGS3 service..."); - const string OldServiceName = "TG Station Server"; - const string NewServiceName = Constants.CanonicalPackageName; - - static ServiceController GetTgs3Service(bool checkNewOneIsntInstalled) - { - var allServices = ServiceController.GetServices(); - var tgs3Service = allServices.FirstOrDefault(service => service.ServiceName == OldServiceName); - foreach (var service in allServices) - { - if (service == tgs3Service) - continue; - - using (service) - if (checkNewOneIsntInstalled && (service.ServiceName == NewServiceName || service.ServiceName == "tgstation-server-4")) - { - Console.WriteLine("Detected existing TGS4+ install! Cannot continue. Please uninstall any versions of TGS4+ before continuing."); - ExitPause(10); - } - } - - if (checkNewOneIsntInstalled) - Console.WriteLine("TGS4+ service install not detected."); - - if (tgs3Service == null) - { - Console.WriteLine("TGS3 is not installed on this machine!"); - ExitPause(5); - } - - return tgs3Service; - } - - using var tgs3Service = GetTgs3Service(true); - - if (tgs3Service.Status != ServiceControllerStatus.Running) - { - Console.WriteLine("TGS3 service is installed but not running! Please start the service before continuing."); - ExitPause(9); - } - - // TGS3 CONNECTION CHECK - Console.WriteLine("Checking TGS3 connection..."); - var commsExitCode = RunComms("--verify-connection"); - if(commsExitCode != 0) - { - Console.WriteLine("Could not connect to TGS3 as administrator!"); - ExitPause(6); - } - - // USER INPUT - Console.WriteLine("We've confirmed you have have both TGS3 installed and TGS4+ service UNinstalled on THIS machine."); - Console.WriteLine(); - Console.WriteLine("Please read all of the following CAREFULLY before proceeding:"); - Console.WriteLine("Confirm you want to migrate to the latest version installing the necessary prerequisite .NET version along the way."); - Console.WriteLine("Please note that this is a one way upgrade and will not keep your DreamDaemon servers running throughout it."); - Console.WriteLine("All TGS3 instances will be migrated in place. The following components will be preserved:"); - Console.WriteLine("- Repository (No test merge data or SSH key)"); - Console.WriteLine("- BYOND version (redownloaded from byond.com)"); - Console.WriteLine("- A FEW server configuration settings (Committer info, Autostart, Webclient, Game Port, Security Level)"); - Console.WriteLine("- EventHandlers"); - Console.WriteLine("- Chat Bots, if enabled"); - Console.WriteLine(" - TGS4+ doesn't support individual user/group identification. Admin channels will be used instead"); - Console.WriteLine(" - IRC authentication information cannot be copied and must be manually adjusted"); - Console.WriteLine("- Static Files"); - Console.WriteLine("- Code Modifications"); - Console.WriteLine("Remaining components such as logins, game builds, etc. can be recreated once the migration is complete."); - Console.WriteLine("IMPORTANT NOTES:"); - Console.WriteLine("- INSTANCES CANNOT HAVE GAME PORTS OFFSET 111 UNITS FROM EACH OTHER OR HIGHER THAN 65423! WE AREN'T CORRECTING FOR THIS WHILE MIGRATING!"); - Console.WriteLine("- DISABLED INSTANCES WILL NOT BE MIGRATED! PLEASE ENABLE ALL INSTANCES YOU WISH TO MIGRATE BEFORE CONTINUING!"); - Console.WriteLine("- INSTANCE AUTO UPDATE CAN INTERFERE WITH THE MIGRATION! PLEASE DISABLE IT ON ALL INSTANCES BEING MIGRATED BEFORE CONTINUING!"); - Console.WriteLine("- DO NOT ATTEMPT TO USE TGS3 VIA NORMAL METHODS WHILE THIS MIGRATION IS TAKING PLACE OR YOU COULD CORRUPT YOUR DATA!"); - Console.WriteLine("Side note: You can skip the TGS6 setup wizard step by copying your premade appsettings.Production.yml file next to this .exe NOW."); - if (!PromptYesOrNo("Proceed with upgrade?")) - { - Console.WriteLine("Prerequisite not met."); - ExitPause(0); - } - - string? tgsInstallPath = null; - do - { - Console.WriteLine("Please enter the directory where you would like the TGS binaries installed."); - Console.Write("This may be anywhere but should be empty: "); - tgsInstallPath = Console.ReadLine(); - if (!String.IsNullOrWhiteSpace(tgsInstallPath)) - { - if (!Path.IsPathRooted(tgsInstallPath)) - { - Console.WriteLine("Please do not use a relative path for this. Enter the full path including the drive letter."); - tgsInstallPath = null; - } - else if (Path.GetInvalidPathChars().Any(invalidChar => tgsInstallPath.Contains(invalidChar))) - { - Console.WriteLine("Invalid characters detected!"); - tgsInstallPath = null; - } - } - } - while (String.IsNullOrWhiteSpace(tgsInstallPath)); - - Console.WriteLine("Attempting to create TGS install directory..."); - Directory.CreateDirectory(tgsInstallPath); - - // ASP.NET 8.0 RUNTIME CHECK - Console.WriteLine("Next step, we need to ensure the ASP.NET Core 6 runtime is installed on your machine."); - Console.WriteLine("We're going to download it for you."); - Console.WriteLine("Yes, this program runs .NET 6, but it contains the entire runtime embedded into it. You will need a system-wide install for TGS."); - - var runtimeInstalled = true; // assume for now - using (var dotnetRuntimeCheck = new Process()) - { - dotnetRuntimeCheck.StartInfo.FileName = "C:/Program Files/dotnet/dotnet.exe"; - dotnetRuntimeCheck.StartInfo.Arguments = "--list-runtimes"; - dotnetRuntimeCheck.StartInfo.RedirectStandardOutput = true; - - try - { - dotnetRuntimeCheck.Start(); - - dotnetRuntimeCheck.WaitForExit(); - } - catch (Win32Exception ex) when (ex.NativeErrorCode == 2) - { - Console.WriteLine("Dotnet does not appear to be installed at all."); - runtimeInstalled = false; - } - - if (runtimeInstalled) - { - var versions = await dotnetRuntimeCheck.StandardOutput.ReadToEndAsync(); - var regex = new Regex("Microsoft\\.AspNetCore\\.App 8\\.0\\.[0-9]+"); - - if (!regex.IsMatch(versions)) - runtimeInstalled = false; - } - } - - // ASP.NET 8.0 RUNTIME SETUP - var assemblyName = currentAssembly.GetName(); - var productInfoHeaderValue = - new ProductInfoHeaderValue( - assemblyName.Name!, - assemblyName.Version!.Semver().ToString()); - var httpClientFactory = new HttpClientFactory(productInfoHeaderValue); - if (!runtimeInstalled) - { - // RUNTIME DONWLOAD - Console.WriteLine("The version we are installing is the latest circa 26-09-2022, feel free to update it later if you want but that is not necessary."); - - var x64 = Environment.Is64BitOperatingSystem; - var xSubstitution = x64 ? "64" : "86"; - Console.WriteLine($"Running on an x{xSubstitution} system."); - - var downloadUri = RuntimeDistributableAttribute.Instance.RuntimeDistributableUrl; - - var dotnetDownloadFilePath = $"dotnet-hosting-bundle-installer.exe"; - - Console.WriteLine($"Downloading {downloadUri} to {Path.GetFullPath(dotnetDownloadFilePath)}..."); - - using var httpClient = httpClientFactory.CreateClient(); - using var request = new HttpRequestMessage(HttpMethod.Get, downloadUri); - var webRequestTask = httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, default); - using var response = await webRequestTask; - response.EnsureSuccessStatusCode(); - await using (var responseStream = await response.Content.ReadAsStreamAsync()) - { - await using var fileStream = new FileStream( - dotnetDownloadFilePath, - FileMode.Create, - FileAccess.Write, - FileShare.ReadWrite | FileShare.Delete, - 4096, - FileOptions.Asynchronous | FileOptions.SequentialScan); - await responseStream.CopyToAsync(fileStream); - } - - // RUNTIME INSTALLATION - Console.WriteLine("Runtime downloaded. Running silent installation..."); - bool silentInstallSuccess = true; - using var silentInstallProcess = new Process(); - { - silentInstallProcess.StartInfo.UseShellExecute = false; - silentInstallProcess.StartInfo.FileName = dotnetDownloadFilePath; - silentInstallProcess.StartInfo.Arguments = "/install /quiet /norestart"; - silentInstallProcess.Start(); - silentInstallProcess.WaitForExit(); - - if (silentInstallProcess.ExitCode != 0) - { - Console.WriteLine("Silent installation failed! Please install the runtime interactively."); - Console.WriteLine("Launching install dialog"); - silentInstallSuccess = false; - } - } - - if (!silentInstallSuccess) - { - using var installProcess = new Process(); - installProcess.StartInfo.FileName = dotnetDownloadFilePath; - installProcess.Start(); - installProcess.WaitForExit(); - - if (!PromptYesOrNo("Was the installation successful?")) - { - Console.WriteLine("Cannot continue without ASP.NET 8.0 runtime installed."); - ExitPause(2); - } - } - } - else - { - Console.WriteLine("Runtime detected successfully. Continuing..."); - } - - - // TGS6 ONLINE LOCATING - Console.WriteLine("Now we're going to locate the latest version of the TGS service."); - Console.WriteLine("(This migrator does not support the console runner, but you may switch the installation to it after completion)"); - - Console.WriteLine("Determining latest version of TGS 5.X.X..."); - - var gitHubClient = new GitHubClient(new Octokit.ProductHeaderValue(productInfoHeaderValue.Product!.Name, productInfoHeaderValue.Product.Version)); - - string? gitHubPat = Environment.GetEnvironmentVariable("TGS_MIGRATOR_GITHUB_PAT"); - if (gitHubPat != null) - gitHubClient.Credentials = new Credentials(gitHubPat); - - const int TgstationServerRepoId = 92952846; - - var allReleases = await gitHubClient.Repository.Release.GetAll(TgstationServerRepoId); - - const string VersionFiveTagPrefix = "tgstation-server-v5."; - var allVersionFiveReleases = allReleases - .Where(release => release.TagName.StartsWith(VersionFiveTagPrefix)); - var latestVersionFiveRelease = allVersionFiveReleases - .OrderByDescending(release => Version.Parse(release.TagName[(VersionFiveTagPrefix.Length - 2)..])) - .FirstOrDefault(); - - if (latestVersionFiveRelease == null) - { - Console.WriteLine("Unable to determine latest version 5 release!"); - ExitPause(3); - } - - Console.WriteLine($"Latest V5 version: {latestVersionFiveRelease.TagName}"); - - var serverServiceAsset = latestVersionFiveRelease.Assets.FirstOrDefault(asset => asset.Name == "ServerService.zip"); - if (serverServiceAsset == null) - { - Console.WriteLine("Unable to determine ServerService.zip release asset!"); - ExitPause(4); - } - - // TGS6 SETUP WIZARD - Console.WriteLine("We are now going to run the TGS setup wizard to generate your new server configuration file."); - - var serverFactory = Tgstation.Server.Host.Core.Application.CreateDefaultServerFactory(); - _ = await serverFactory.CreateServer(new[] { $"General:SetupWizardMode={SetupWizardMode.Only}" }, null, default); // This is where the wizard actually runs - - // TGS6 DOWNLOAD AND UNZIP - Console.WriteLine("Downloading TGS6..."); - - using (var loggerFactory = LoggerFactory.Create(builder => { })) - { - BufferedFileStreamProvider tgsFiveZipBuffer; - { - var fileDownloader = new FileDownloader(httpClientFactory, loggerFactory.CreateLogger()); - await using var tgsFiveZipDownload = fileDownloader.DownloadFile(new Uri(serverServiceAsset.BrowserDownloadUrl), null); - tgsFiveZipBuffer = new BufferedFileStreamProvider( - await tgsFiveZipDownload.GetResult(default)); - } - - await using (tgsFiveZipBuffer) - { - Console.WriteLine("Unzipping TGS6..."); - await serverFactory.IOManager.ZipToDirectory( - tgsInstallPath, - await tgsFiveZipBuffer.GetResult(default), - default); - } - } - - // TGS6 CONFIG SETUP - const string ConfigurationFileName = "appsettings.Production.yml"; - Console.WriteLine("Extracting API port from configuration..."); - ushort configuredApiPort; - { - var configFileContents = await File.ReadAllTextAsync(ConfigurationFileName); - var match = Regex.Match(configFileContents, "ApiPort: ([0-9]+)"); - if (!match.Success) - { - Console.WriteLine("Unable to extract ApiPort setting!"); - ExitPause(12); - } - - configuredApiPort = ushort.Parse(match.Groups[1].Value); - } - - Console.WriteLine("Moving configuration file from setup wizard to installation folder..."); - File.Copy(ConfigurationFileName, Path.Combine(tgsInstallPath, ConfigurationFileName)); - - // TGS6 SERVICE SETUP - Console.WriteLine("Installing TGS6 service..."); - using (var processInstaller = new ServiceProcessInstaller()) - using (var installer = new ServiceInstaller()) - { - processInstaller.Account = ServiceAccount.LocalSystem; - - installer.Context = new InstallContext( - "tgs-migrate-install.log", - new string[] - { - $"assemblypath={Path.Combine(tgsInstallPath, "Tgstation.Server.Host.Service.exe")}" - }); - installer.Description = "/tg/station 13 server running as a windows service"; - installer.DisplayName = "tgstation-server"; - installer.StartType = ServiceStartMode.Automatic; - installer.ServicesDependedOn = new string[] { "Tcpip", "Dhcp", "Dnscache" }; - installer.ServiceName = NewServiceName; - installer.Parent = processInstaller; - - var state = new ListDictionary(); - installer.Install(state); - } - - Console.WriteLine("Starting TGS6 service..."); - var allServices = ServiceController.GetServices(); - using (var TGS6Service = allServices.FirstOrDefault(service => service.ServiceName == NewServiceName)) - { - if (TGS6Service == null) - { - Console.WriteLine("Unable to locate newly installed TGS6 service!"); - ExitPause(11); - } - - foreach (var service in allServices) - { - if (service == TGS6Service) - continue; - - service.Dispose(); - } - - TGS6Service.Start(); - TGS6Service.WaitForStatus(ServiceControllerStatus.Running, TimeSpan.FromMinutes(2)); - } - - // TGS6 CLIENT CONNECTION - const int MaxWaitMinutes = 5; - Console.WriteLine($"Connecting to TGS6 (Max {MaxWaitMinutes} minute wait)..."); - var giveUpAt = DateTimeOffset.UtcNow.AddMinutes(MaxWaitMinutes); - - var serverUrl = new Uri($"http://localhost:{configuredApiPort}"); - var clientFactory = new RestServerClientFactory(productInfoHeaderValue.Product); - IRestServerClient TGS6Client; - for (var I = 1; ; ++I) - { - try - { - Console.WriteLine($"Attempt {I}..."); - TGS6Client = await clientFactory.CreateFromLogin( - serverUrl, - DefaultCredentials.AdminUserName, - DefaultCredentials.DefaultAdminUserPassword); - break; - } - catch (HttpRequestException) - { - //migrating, to be expected - if (DateTimeOffset.UtcNow > giveUpAt) - throw; - await Task.Delay(TimeSpan.FromSeconds(1)); - } - catch (ServiceUnavailableException) - { - // migrating, to be expected - if (DateTimeOffset.UtcNow > giveUpAt) - throw; - await Task.Delay(TimeSpan.FromSeconds(1)); - } - } - - Console.WriteLine("Successfully connected to TGS6!"); - - // COMMS MIGRATION - Console.WriteLine("Deferring to Comms binary to migrate instances..."); - - commsExitCode = RunComms($"--migrate {configuredApiPort}"); - if (commsExitCode != 0) - { - Console.WriteLine("Could not connect to TGS3 as administrator!"); - ExitPause(commsExitCode); - } - - // TGS3 SHUTDOWN - Console.WriteLine("Shutting down TGS3 service..."); - tgs3Service.Stop(); - tgs3Service.WaitForStatus(ServiceControllerStatus.Stopped, TimeSpan.FromMinutes(2)); - tgs3Service.Dispose(); - - Console.WriteLine("Disabling TGS3 service..."); - using (var managementObject = new ManagementObject(string.Format("Win32_Service.Name=\"{0}\"", OldServiceName))) - { - managementObject.InvokeMethod("ChangeStartMode", new object[] { "Disabled" }); - } - - if(tgs3Service.StartType != ServiceStartMode.Disabled) - Console.WriteLine("Failed to disable TGS3 service! This isn't critical, however."); - - Console.WriteLine("Migration complete! Please continue uninstall TGS3 using Add/Remove Programs."); - Console.WriteLine("Then configure TGS6 using an interactive client to build and start your server."); - ExitPause(0); -} -catch (Exception ex) -{ - Console.WriteLine("An error occurred in the migration!"); - Console.WriteLine(ex); - ExitPause(1); -} diff --git a/tools/Tgstation.Server.Migrator/Properties/RuntimeDistributableAttribute.cs b/tools/Tgstation.Server.Migrator/Properties/RuntimeDistributableAttribute.cs deleted file mode 100644 index d4434c61bb6..00000000000 --- a/tools/Tgstation.Server.Migrator/Properties/RuntimeDistributableAttribute.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using System.Reflection; - -namespace Tgstation.Server.Migrator.Properties -{ - /// - /// Attribute for bringing in the runtime redistributable download link - /// - [AttributeUsage(AttributeTargets.Assembly)] - sealed class RuntimeDistributableAttribute : Attribute - { - /// - /// Return the 's instance of the . - /// - public static RuntimeDistributableAttribute Instance => Assembly - .GetExecutingAssembly() - .GetCustomAttribute()!; - - /// - /// The of the current runtime distributable. - /// - public Uri RuntimeDistributableUrl { get; } - - /// - /// Initializes a new instance of the class. - /// - /// The value of . - public RuntimeDistributableAttribute( - string runtimeDistributableUrl) - { - RuntimeDistributableUrl = new Uri(runtimeDistributableUrl ?? throw new ArgumentNullException(nameof(runtimeDistributableUrl))); - } - } -} diff --git a/tools/Tgstation.Server.Migrator/Tgstation.Server.Migrator.csproj b/tools/Tgstation.Server.Migrator/Tgstation.Server.Migrator.csproj deleted file mode 100644 index 2511e50c18c..00000000000 --- a/tools/Tgstation.Server.Migrator/Tgstation.Server.Migrator.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - - Exe - $(TgsFrameworkVersion) - win-x86 - true - $(TgsMigratorVersion) - enable - CA1416 - false - - - - - - - - - - - <_Parameter1>$(TgsDotnetRedistUrl) - - - - - - - - - - - - - -