From 710319fcedd63cf25176ec5fecb67629616d3401 Mon Sep 17 00:00:00 2001 From: SkyratBot <59378654+SkyratBot@users.noreply.github.com> Date: Mon, 27 Feb 2023 01:06:30 +0100 Subject: [PATCH] [MIRROR] Update some TGS stuff [MDB IGNORE] (#19485) * Update some TGS stuff * You're welcome Cyber --------- Co-authored-by: Jordan Dominion Co-authored-by: GoldenAlpharex <58045821+GoldenAlpharex@users.noreply.github.com> --- .gitignore | 10 +- .tgs.yml | 13 + .tgs4.yml | 8 - code/controllers/failsafe.dm | 6 +- code/controllers/master.dm | 2 +- tools/tgs4_scripts/PreCompile.sh | 88 ----- tools/tgs4_scripts/PreSynchronize.sh | 37 -- .../InstallDeps.sh} | 30 +- .../PreCompile.bat | 2 +- tools/tgs_scripts/PreCompile.sh | 38 ++ tools/tgs_scripts/WatchdogLaunch.sh | 6 + tools/tgs_test/Program.cs | 334 ++++++++++++++++++ tools/tgs_test/README.md | 11 + tools/tgs_test/StaticFile.cs | 5 + tools/tgs_test/TgsYml.cs | 15 + tools/tgs_test/Tgstation.TgsTest.csproj | 17 + tools/tgs_test/Tgstation.TgsTest.sln | 25 ++ 17 files changed, 491 insertions(+), 156 deletions(-) create mode 100644 .tgs.yml delete mode 100644 .tgs4.yml delete mode 100755 tools/tgs4_scripts/PreCompile.sh delete mode 100755 tools/tgs4_scripts/PreSynchronize.sh rename tools/{tgs4_scripts/WatchdogLaunch.sh => tgs_scripts/InstallDeps.sh} (50%) mode change 100644 => 100755 rename tools/{tgs4_scripts => tgs_scripts}/PreCompile.bat (82%) create mode 100755 tools/tgs_scripts/PreCompile.sh create mode 100755 tools/tgs_scripts/WatchdogLaunch.sh create mode 100644 tools/tgs_test/Program.cs create mode 100644 tools/tgs_test/README.md create mode 100644 tools/tgs_test/StaticFile.cs create mode 100644 tools/tgs_test/TgsYml.cs create mode 100644 tools/tgs_test/Tgstation.TgsTest.csproj create mode 100644 tools/tgs_test/Tgstation.TgsTest.sln diff --git a/.gitignore b/.gitignore index 40daebeab56..8274552803d 100644 --- a/.gitignore +++ b/.gitignore @@ -203,13 +203,9 @@ Temporary Items !/config/title_screens/images/exclude #Linux docker -/tools/LinuxOneShot/SetupProgram/obj/* -/tools/LinuxOneShot/SetupProgram/bin/* -/tools/LinuxOneShot/SetupProgram/.vs -/tools/LinuxOneShot/Database -/tools/LinuxOneShot/TGS_Config -/tools/LinuxOneShot/TGS_Instances -/tools/LinuxOneShot/TGS_Logs +/tools/tgs_test/.vs/* +/tools/tgs_test/bin/* +/tools/tgs_test/obj/* # Autowiki /tools/autowiki/node_modules diff --git a/.tgs.yml b/.tgs.yml new file mode 100644 index 00000000000..aa8d52acede --- /dev/null +++ b/.tgs.yml @@ -0,0 +1,13 @@ +version: 1 +byond: 514.1588 +static_files: + - name: config + populate: true + - name: data +linux_scripts: + PreCompile.sh: tools/tgs_scripts/PreCompile.sh + WatchdogLaunch.sh: tools/tgs_scripts/WatchdogLaunch.sh + InstallDeps.sh: tools/tgs_scripts/InstallDeps.sh +windows_scripts: + PreCompile.bat: tools/tgs_scripts/PreCompile.bat +security: Trusted diff --git a/.tgs4.yml b/.tgs4.yml deleted file mode 100644 index 932a3a6672f..00000000000 --- a/.tgs4.yml +++ /dev/null @@ -1,8 +0,0 @@ -static_files: - - name: config - populate: true - - name: data -linux_scripts: - PreCompile.sh: tools/tgs4_scripts/PreCompile.sh -windows_scripts: - PreCompile.bat: tools/tgs4_scripts/PreCompile.bat diff --git a/code/controllers/failsafe.dm b/code/controllers/failsafe.dm index 96597548435..bb95adce1bf 100644 --- a/code/controllers/failsafe.dm +++ b/code/controllers/failsafe.dm @@ -138,7 +138,7 @@ GLOBAL_REAL(Failsafe, /datum/controller/failsafe) if (. == 1) //We were able to create a new master master_iteration = 0 SSticker.Recover(); //Recover the ticket system so the Masters runlevel gets set - Master.Initialize(10, FALSE, TRUE) //Need to manually start the MC, normally world.new would do this + Master.Initialize(10, FALSE, FALSE) //Need to manually start the MC, normally world.new would do this to_chat(GLOB.admins, span_adminnotice("Failsafe recovered MC while in emergency state [defcon_pretty()]")) else log_game("FailSafe: Failsafe in emergency state and was unable to recreate MC while in defcon state [defcon_pretty()].") @@ -154,7 +154,7 @@ GLOBAL_REAL(Failsafe, /datum/controller/failsafe) . = Recreate_MC() if (. == 1) //We were able to create a new master SSticker.Recover(); //Recover the ticket system so the Masters runlevel gets set - Master.Initialize(10, FALSE, TRUE) //Need to manually start the MC, normally world.new would do this + Master.Initialize(10, FALSE, FALSE) //Need to manually start the MC, normally world.new would do this to_chat(GLOB.admins, span_adminnotice("MC successfully recreated after recovering all subsystems!")) else message_admins(span_boldannounce("Failed to create new MC!")) @@ -168,7 +168,7 @@ GLOBAL_REAL(Failsafe, /datum/controller/failsafe) . = Recreate_MC() if (. == 1) //We were able to create a new master SSticker.Recover(); //Recover the ticket system so the Masters runlevel gets set - Master.Initialize(10, FALSE, TRUE) //Need to manually start the MC, normally world.new would do this + Master.Initialize(10, FALSE, FALSE) //Need to manually start the MC, normally world.new would do this to_chat(GLOB.admins, span_adminnotice("MC successfully recreated after deleting and recreating all subsystems!")) else message_admins(span_boldannounce("Failed to create new MC!")) diff --git a/code/controllers/master.dm b/code/controllers/master.dm index 5000aa07e30..dfc37bac2b9 100644 --- a/code/controllers/master.dm +++ b/code/controllers/master.dm @@ -205,7 +205,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new StartProcessing(10) else to_chat(world, span_boldannounce("The Master Controller is having some issues, we will need to re-initialize EVERYTHING")) - Initialize(20, TRUE) + Initialize(20, TRUE, FALSE) // Please don't stuff random bullshit here, // Make a subsystem, give it the SS_NO_FIRE flag, and do your work in it's Initialize() diff --git a/tools/tgs4_scripts/PreCompile.sh b/tools/tgs4_scripts/PreCompile.sh deleted file mode 100755 index 69a6f35cf64..00000000000 --- a/tools/tgs4_scripts/PreCompile.sh +++ /dev/null @@ -1,88 +0,0 @@ -#!/bin/bash - -# REPO MAINTAINERS: KEEP CHANGES TO THIS IN SYNC WITH /tools/LinuxOneShot/SetupProgram/PreCompile.sh -# No ~mso -set -e -set -x - -#load dep exports -#need to switch to game dir for Dockerfile weirdness -original_dir=$PWD -cd "$1" -. dependencies.sh -cd "$original_dir" - -#find out what we have (+e is important for this) -set +e -has_git="$(command -v git)" -has_cargo="$(command -v ~/.cargo/bin/cargo)" -has_sudo="$(command -v sudo)" -has_grep="$(command -v grep)" -has_youtubedl="$(command -v youtube-dl)" -has_pip3="$(command -v pip3)" -set -e - -# install cargo if needed -if ! [ -x "$has_cargo" ]; then - echo "Installing rust..." - curl https://sh.rustup.rs -sSf | sh -s -- -y - . ~/.profile -fi - -# apt packages, libssl needed by rust-g but not included in TGS barebones install -if ! ( [ -x "$has_git" ] && [ -x "$has_grep" ] && [ -f "/usr/lib/i386-linux-gnu/libssl.so" ] ); then - echo "Installing apt dependencies..." - if ! [ -x "$has_sudo" ]; then - dpkg --add-architecture i386 - apt-get update - apt-get install -y git libssl-dev:i386 - rm -rf /var/lib/apt/lists/* - else - sudo dpkg --add-architecture i386 - sudo apt-get update - sudo apt-get install -y git libssl-dev:i386 - sudo rm -rf /var/lib/apt/lists/* - fi -fi -dpkg --add-architecture i386 -apt-get update -apt-get install -y lib32z1 pkg-config libssl-dev:i386 libssl-dev libssl1.1:i386 -# update rust-g -if [ ! -d "rust-g" ]; then - echo "Cloning rust-g..." - git clone https://github.com/tgstation/rust-g - cd rust-g - ~/.cargo/bin/rustup target add i686-unknown-linux-gnu -else - echo "Fetching rust-g..." - cd rust-g - git fetch - ~/.cargo/bin/rustup target add i686-unknown-linux-gnu -fi - -echo "Deploying rust-g..." -git checkout "$RUST_G_VERSION" -env PKG_CONFIG_ALLOW_CROSS=1 ~/.cargo/bin/cargo build --release --target=i686-unknown-linux-gnu -mv target/i686-unknown-linux-gnu/release/librust_g.so "$1/librust_g.so" -cd .. - -# install or update youtube-dl when not present, or if it is present with pip3, -# which we assume was used to install it -if ! [ -x "$has_youtubedl" ]; then - echo "Installing youtube-dl with pip3..." - if ! [ -x "$has_sudo" ]; then - apt-get install -y python3 python3-pip - else - sudo apt-get install -y python3 python3-pip - fi - pip3 install youtube-dl -elif [ -x "$has_pip3" ]; then - echo "Ensuring youtube-dl is up-to-date with pip3..." - pip3 install youtube-dl -U -fi - -# compile tgui -echo "Compiling tgui..." -cd "$1" -chmod +x tools/bootstrap/node # Workaround for https://github.com/tgstation/tgstation-server/issues/1167 -env TG_BOOTSTRAP_CACHE="$original_dir" TG_BOOTSTRAP_NODE_LINUX=1 CBT_BUILD_MODE="TGS" tools/bootstrap/node tools/build/build.js diff --git a/tools/tgs4_scripts/PreSynchronize.sh b/tools/tgs4_scripts/PreSynchronize.sh deleted file mode 100755 index 63091bf4b30..00000000000 --- a/tools/tgs4_scripts/PreSynchronize.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/sh - -has_python="$(command -v python3)" -has_git="$(command -v git)" -has_sudo="$(command -v sudo)" -has_pip="$(command -v pip3)" - -set -e - -if ! { [ -x "$has_python" ] && [ -x "$has_pip" ] && [ -x "$has_git" ]; }; then - echo "Installing apt dependencies..." - if ! [ -x "$has_sudo" ]; then - apt update - apt install -y python3 python3-pip git - rm -rf /var/lib/apt/lists/* - else - sudo apt update - sudo apt install -y python3 python3-pip git - sudo rm -rf /var/lib/apt/lists/* - fi -fi - -echo "Installing pip dependencies..." -pip3 install PyYaml beautifulsoup4 - -cd $1 - -echo "Running changelog script..." -python3 tools/ss13_genchangelog.py html/changelogs - -echo "Committing changes..." -git add html - -#we now don't care about failures -set +e -git commit -m "Automatic changelog compile, [ci skip]" -exit 0 diff --git a/tools/tgs4_scripts/WatchdogLaunch.sh b/tools/tgs_scripts/InstallDeps.sh old mode 100644 new mode 100755 similarity index 50% rename from tools/tgs4_scripts/WatchdogLaunch.sh rename to tools/tgs_scripts/InstallDeps.sh index 7e184a6ce86..6069e28ad9c --- a/tools/tgs4_scripts/WatchdogLaunch.sh +++ b/tools/tgs_scripts/InstallDeps.sh @@ -1,19 +1,14 @@ #!/bin/bash -# Special file to ensure all dependencies still exist between server lanuches. -# Mainly for use by people who abuse docker by modifying the container's system. -set -e -set -x - #find out what we have (+e is important for this) set +e has_git="$(command -v git)" has_cargo="$(command -v ~/.cargo/bin/cargo)" has_sudo="$(command -v sudo)" -has_grep="$(command -v grep)" has_youtubedl="$(command -v youtube-dl)" has_pip3="$(command -v pip3)" set -e +set -x # install cargo if needed if ! [ -x "$has_cargo" ]; then @@ -23,19 +18,32 @@ if ! [ -x "$has_cargo" ]; then fi # apt packages, libssl needed by rust-g but not included in TGS barebones install -if ! ( [ -x "$has_git" ] && [ -x "$has_grep" ] && [ -f "/usr/lib/i386-linux-gnu/libssl.so" ] ); then - dpkg --add-architecture i386 - apt-get update - apt-get install -y lib32z1 git pkg-config libssl-dev:i386 libssl-dev libssl1.1:i386 +if ! ( [ -x "$has_git" ] && [ -f "/usr/lib/i386-linux-gnu/libssl.so" ] ); then + echo "Installing apt dependencies..." + if ! [ -x "$has_sudo" ]; then + dpkg --add-architecture i386 + apt-get update + apt-get install -y lib32z1 git pkg-config libssl-dev:i386 libssl-dev zlib1g-dev:i386 + else + sudo dpkg --add-architecture i386 + sudo apt-get update + sudo apt-get install -y lib32z1 git pkg-config libssl-dev:i386 libssl-dev zlib1g-dev:i386 + fi fi -# install youtube-dl when not present +# install or update youtube-dl when not present, or if it is present with pip3, +# which we assume was used to install it if ! [ -x "$has_youtubedl" ]; then echo "Installing youtube-dl with pip3..." if ! [ -x "$has_sudo" ]; then + apt-get update apt-get install -y python3 python3-pip else + sudo apt-get update sudo apt-get install -y python3 python3-pip fi pip3 install youtube-dl +elif [ -x "$has_pip3" ]; then + echo "Ensuring youtube-dl is up-to-date with pip3..." + pip3 install youtube-dl -U fi diff --git a/tools/tgs4_scripts/PreCompile.bat b/tools/tgs_scripts/PreCompile.bat similarity index 82% rename from tools/tgs4_scripts/PreCompile.bat rename to tools/tgs_scripts/PreCompile.bat index fd7f861e69a..ce7572c9ef4 100644 --- a/tools/tgs4_scripts/PreCompile.bat +++ b/tools/tgs_scripts/PreCompile.bat @@ -2,7 +2,7 @@ cd /D "%~dp0" set TG_BOOTSTRAP_CACHE=%cd% IF NOT %1 == "" ( - rem TGS4: we are passed the game directory on the command line + rem TGS4+: we are passed the game directory on the command line cd %1 ) ELSE IF EXIST "..\Game\B\tgstation.dmb" ( rem TGS3: Game/B/tgstation.dmb exists, so build in Game/A diff --git a/tools/tgs_scripts/PreCompile.sh b/tools/tgs_scripts/PreCompile.sh new file mode 100755 index 00000000000..acc8dcfe1cd --- /dev/null +++ b/tools/tgs_scripts/PreCompile.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +./InstallDeps.sh + +set -e +set -x + +#load dep exports +#need to switch to game dir for Dockerfile weirdness +original_dir=$PWD +cd "$1" +. dependencies.sh +cd "$original_dir" + +# update rust-g +if [ ! -d "rust-g" ]; then + echo "Cloning rust-g..." + git clone https://github.com/tgstation/rust-g + cd rust-g + ~/.cargo/bin/rustup target add i686-unknown-linux-gnu +else + echo "Fetching rust-g..." + cd rust-g + git fetch + ~/.cargo/bin/rustup target add i686-unknown-linux-gnu +fi + +echo "Deploying rust-g..." +git checkout "$RUST_G_VERSION" +env PKG_CONFIG_ALLOW_CROSS=1 ~/.cargo/bin/cargo build --release --target=i686-unknown-linux-gnu +mv target/i686-unknown-linux-gnu/release/librust_g.so "$1/librust_g.so" +cd .. + +# compile tgui +echo "Compiling tgui..." +cd "$1" +chmod +x tools/bootstrap/node # Workaround for https://github.com/tgstation/tgstation-server/issues/1167 +env TG_BOOTSTRAP_CACHE="$original_dir" TG_BOOTSTRAP_NODE_LINUX=1 CBT_BUILD_MODE="TGS" tools/bootstrap/node tools/build/build.js diff --git a/tools/tgs_scripts/WatchdogLaunch.sh b/tools/tgs_scripts/WatchdogLaunch.sh new file mode 100755 index 00000000000..cd400cbb969 --- /dev/null +++ b/tools/tgs_scripts/WatchdogLaunch.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +# Special file to ensure all dependencies still exist between server launches. +# Mainly for use by people who abuse docker by modifying the container's system. + +./InstallDeps.sh diff --git a/tools/tgs_test/Program.cs b/tools/tgs_test/Program.cs new file mode 100644 index 00000000000..e111e19cbfb --- /dev/null +++ b/tools/tgs_test/Program.cs @@ -0,0 +1,334 @@ +// Simple app meant to test tgstation's TGS integration given a fresh TGS install with the default account +// +// Args: Repository Owner/Name, TGS instance path, TGS API port, Pushed commit hash (For .tgs.yml access), GitHub Token + +using System.Reflection; +using System.Text; + +using Octokit; +using Tgstation.Server.Api; +using Tgstation.Server.Api.Models.Request; +using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Response; +using Tgstation.Server.Client; +using YamlDotNet.Serialization.NamingConventions; +using YamlDotNet.Serialization; + +Console.WriteLine("Parsing args..."); + +const int ExpectedArgs = 5; +if (args.Length != ExpectedArgs) +{ + Console.WriteLine($"Incorrect number of args: {args.Length}. Expected {ExpectedArgs}"); + return 1; +} + +var repoSlug = args[0]; +var instancePath = args[1]; +var tgsApiPortString = args[2]; +var pushedCommitHash = args[3]; +var gitHubToken = args[4]; + +var repoSlugSplits = repoSlug.Split('/', StringSplitOptions.RemoveEmptyEntries); +if(repoSlugSplits.Length != 2) +{ + Console.WriteLine($"Invalid repo slug: {repoSlug}"); + return 2; +} + +var repoOwner = repoSlugSplits[0]; +var repoName = repoSlugSplits[1]; + +if (!ushort.TryParse(tgsApiPortString, out var tgsApiPort)) +{ + Console.WriteLine($"Invalid port: {tgsApiPortString}"); + return 3; +} + +try +{ + Console.WriteLine($"Retrieving .tgs.yml (@{pushedCommitHash})..."); + var assemblyName = Assembly.GetExecutingAssembly().GetName(); + var gitHubClient = new GitHubClient( + new ProductHeaderValue( + assemblyName.Name, + assemblyName.Version!.Semver().ToString())) + { + Credentials = new Credentials(gitHubToken) + }; + + var tgsYmlContent = await gitHubClient.Repository.Content.GetRawContentByRef(repoOwner, repoName, ".tgs.yml", pushedCommitHash); + var tgsYmlString = Encoding.UTF8.GetString(tgsYmlContent); + + var deserializer = new DeserializerBuilder() + .WithNamingConvention(new UnderscoredNamingConvention()) + .Build(); + + var tgsYml = deserializer.Deserialize(tgsYmlString); + + const int SupportedTgsYmlVersion = 1; + if (tgsYml.Version != SupportedTgsYmlVersion) + { + Console.WriteLine($"Unsupported .tgs.yml version: {tgsYml.Version}. Expected {SupportedTgsYmlVersion}"); + return 4; + } + + var targetByondVersion = Version.Parse(tgsYml.Byond); + + Console.WriteLine($".tgs.yml Security level: {tgsYml.Security}"); + + Console.WriteLine("Downloading and checking BYOND version in dependencies.sh..."); + var dependenciesShContent = await gitHubClient.Repository.Content.GetRawContentByRef(repoOwner, repoName, "dependencies.sh", pushedCommitHash); + var dependenciesSh = Encoding.UTF8.GetString(dependenciesShContent); + var dependenciesShLines = dependenciesSh.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); + + int dependenciesShByondMajor = 0; + int dependenciesShByondMinor = 0; + foreach(var dependenciesShLine in dependenciesShLines) + { + var trimmedLine = dependenciesShLine.Trim(); + var lineSplit = trimmedLine.Split('=', StringSplitOptions.RemoveEmptyEntries); + if (lineSplit.Length != 2) + continue; + + if (lineSplit[0].EndsWith("BYOND_MAJOR")) + dependenciesShByondMajor = Int32.Parse(lineSplit[1]); + else if (lineSplit[0].EndsWith("BYOND_MINOR")) + dependenciesShByondMinor = Int32.Parse(lineSplit[1]); + } + + var dependenciesByondVersion = new Version(dependenciesShByondMajor, dependenciesShByondMinor); + if(dependenciesByondVersion != targetByondVersion) + { + Console.WriteLine($".tgs.yml BYOND version does not match dependencies.sh! Expected {dependenciesByondVersion} got {targetByondVersion}!"); + return 5; + } + + // Connect to TGS + var clientFactory = new ServerClientFactory( + new System.Net.Http.Headers.ProductHeaderValue( + assemblyName.Name!, + assemblyName.Version!.Semver().ToString())); + + var tgsApiUrl = new Uri($"http://127.0.0.1:{tgsApiPort}"); + var giveUpAt = DateTimeOffset.UtcNow.AddMinutes(2); + IServerClient client; + for (var I = 1; ; ++I) + { + try + { + Console.WriteLine($"TGS Connection Attempt {I}..."); + client = await clientFactory.CreateFromLogin( + tgsApiUrl, + 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("Getting TGS information..."); + + var tgsInfo = await client.ServerInformation(default); + + var scriptDictionaryToUse = tgsInfo.WindowsHost ? tgsYml.WindowsScripts : tgsYml.LinuxScripts; + Console.WriteLine($"Downloading {scriptDictionaryToUse.Count} EventScripts..."); + + var scriptDownloadTasks = new Dictionary>(); + foreach (var scriptKvp in scriptDictionaryToUse) + { + scriptDownloadTasks.Add( + scriptKvp.Key, + gitHubClient.Repository.Content.GetRawContentByRef(repoOwner, repoName, scriptKvp.Value, pushedCommitHash)); + } + + await Task.WhenAll(scriptDownloadTasks.Values); + + Console.WriteLine("Setting up TGS instance..."); + + var instance = await client.Instances.CreateOrAttach( + new InstanceCreateRequest + { + ConfigurationType = ConfigurationType.HostWrite, + Name = "tgstation", + Path = instancePath + }, + default); + + instance = await client.Instances.Update( + new InstanceUpdateRequest + { + Id = instance.Id, + Online = true + }, + default); + + var instanceClient = client.Instances.CreateClient(instance); + + Console.WriteLine("Cloning main branch of repo..."); + var repoCloneJob = await instanceClient.Repository.Clone( + new RepositoryCreateRequest + { + Origin = new Uri($"http://github.com/{repoSlug}"), + UpdateSubmodules = true, + AccessUser = "Testing", + AccessToken = gitHubToken + }, + default); + + Console.WriteLine("Installing BYOND..."); + var byondInstallJob = await instanceClient.Byond.SetActiveVersion( + new ByondVersionRequest + { + Version = targetByondVersion + }, + null, + default); + + Console.WriteLine("Updating server/compiler settings..."); + await instanceClient.DreamMaker.Update( + new DreamMakerRequest + { + ApiValidationSecurityLevel = tgsYml.Security + }, + default); + + await instanceClient.DreamDaemon.Update( + new DreamDaemonRequest + { + SecurityLevel = tgsYml.Security, + Visibility = DreamDaemonVisibility.Invisible + }, + default); + + Console.WriteLine("Uploading EventScripts..."); + var configurationUploadTasks = new List>(); + foreach (var scriptDownloadKvp in scriptDownloadTasks) + { + var scriptContent = await scriptDownloadKvp.Value; + + var memoryStream = new MemoryStream(scriptContent); + configurationUploadTasks.Add( + instanceClient.Configuration.Write(new ConfigurationFileRequest + { + Path = $"EventScripts/{scriptDownloadKvp.Key}" + }, + memoryStream, + default)); + } + + await Task.WhenAll(configurationUploadTasks); + + Console.WriteLine("Creating GameStaticFiles structure..."); + var staticFileDownloadTasks = new Dictionary>>(); + foreach (var staticFile in tgsYml.StaticFiles) + { + if (!staticFile.Populate) + { + Console.WriteLine($"Creating empty directory GameStaticFiles/{staticFile.Name}..."); + await instanceClient.Configuration.CreateDirectory(new ConfigurationFileRequest + { + Path = $"GameStaticFiles/{staticFile.Name}" + }, + default); + } + else + { + // not by ref here as we are relying on master being not broken + Console.WriteLine($"Enumerating repo path {staticFile.Name}..."); + var repositoryFilesToUpload = new Queue(await gitHubClient.Repository.Content.GetAllContents(repoOwner, repoName, staticFile.Name)); + while (repositoryFilesToUpload.Count != 0) + { + var repositoryFileToUpload = repositoryFilesToUpload.Dequeue(); + if (repositoryFileToUpload.Type == ContentType.File) + { + // serial because easier to track errors + Console.WriteLine($"Transferring {repositoryFileToUpload.Path}..."); + var fileContent = await gitHubClient.Repository.Content.GetRawContent(repoOwner, repoName, repositoryFileToUpload.Path); + using var memoryStream = new MemoryStream(fileContent); + await instanceClient.Configuration.Write(new ConfigurationFileRequest + { + Path = $"GameStaticFiles/{repositoryFileToUpload.Path}" + }, + memoryStream, + default); + } + else + { + Console.WriteLine($"Enumerating repo path {repositoryFileToUpload.Path}..."); + var additionalFiles = await gitHubClient.Repository.Content.GetAllContents(repoOwner, repoName, repositoryFileToUpload.Path); + foreach (var additionalFile in additionalFiles) + repositoryFilesToUpload.Enqueue(additionalFile); + } + } + } + } + + async Task WaitForJob(JobResponse originalJob, int timeout) + { + Console.WriteLine($"Waiting for job \"{originalJob.Description}\"..."); + var job = originalJob; + var previousProgress = job.Progress; + do + { + if (job.Progress != previousProgress) + Console.WriteLine($"Progress: {previousProgress = job.Progress}"); + + await Task.Delay(TimeSpan.FromSeconds(1)); + job = await instanceClient!.Jobs.GetId(job, default); + --timeout; + } + while (!job.StoppedAt.HasValue && timeout > 0); + + if (!job.StoppedAt.HasValue) + { + await instanceClient!.Jobs.Cancel(job, default); + Console.WriteLine($"Timed out!"); + return false; + } + else if (job.ExceptionDetails != null) + { + Console.WriteLine($"Error: {job.ExceptionDetails}"); + return false; + } + + return true; + } + + if (!await WaitForJob(byondInstallJob.InstallJob!, 120)) + return 6; + + if (!await WaitForJob(repoCloneJob.ActiveJob!, 600)) + return 7; + + Console.WriteLine("Deploying..."); + var deploymentJob = await instanceClient.DreamMaker.Compile(default); + if (!await WaitForJob(deploymentJob, 1800)) + return 8; + + Console.WriteLine("Launching..."); + var launchJob = await instanceClient.DreamDaemon.Start(default); + if (!await WaitForJob(launchJob, 300)) + return 9; + + return 0; +} +catch (Exception ex) +{ + Console.WriteLine(ex); + return 4; +} diff --git a/tools/tgs_test/README.md b/tools/tgs_test/README.md new file mode 100644 index 00000000000..0f2b54458f5 --- /dev/null +++ b/tools/tgs_test/README.md @@ -0,0 +1,11 @@ +# TGS Test Script + +This is a simple app that does a few things + +- Downloads .tgs.yml information from a specific commit of a given repository. +- Checks that the BYOND version in the .tgs.yml file matches the dependencies.sh version. +- Connects to a TGS instance via command line parameters. +- Uses the .tgs.yml information to automatically set up a TGS instance. +- Runs a TGS deploy/launch and validates that they succeeded. + + Look for its invocation in the GitHub workflows diff --git a/tools/tgs_test/StaticFile.cs b/tools/tgs_test/StaticFile.cs new file mode 100644 index 00000000000..135f159ad6e --- /dev/null +++ b/tools/tgs_test/StaticFile.cs @@ -0,0 +1,5 @@ +sealed class StaticFile +{ + public string Name { get; set; } = String.Empty; + public bool Populate { get; set; } +} diff --git a/tools/tgs_test/TgsYml.cs b/tools/tgs_test/TgsYml.cs new file mode 100644 index 00000000000..c738ac4fdef --- /dev/null +++ b/tools/tgs_test/TgsYml.cs @@ -0,0 +1,15 @@ +using Tgstation.Server.Api.Models; + +sealed class TgsYml +{ + public int Version { get; set; } + + public string Byond { get; set; } = String.Empty; + + public List StaticFiles { get; set; } = new List(); + + public Dictionary WindowsScripts { get; set; } = new Dictionary(); + public Dictionary LinuxScripts { get; set; } = new Dictionary(); + + public DreamDaemonSecurity Security { get; set; } +} diff --git a/tools/tgs_test/Tgstation.TgsTest.csproj b/tools/tgs_test/Tgstation.TgsTest.csproj new file mode 100644 index 00000000000..0a641c269ab --- /dev/null +++ b/tools/tgs_test/Tgstation.TgsTest.csproj @@ -0,0 +1,17 @@ + + + + 1.0.0 + Exe + net7.0 + enable + enable + + + + + + + + + diff --git a/tools/tgs_test/Tgstation.TgsTest.sln b/tools/tgs_test/Tgstation.TgsTest.sln new file mode 100644 index 00000000000..37a7a119a88 --- /dev/null +++ b/tools/tgs_test/Tgstation.TgsTest.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.4.33213.308 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tgstation.TgsTest", "Tgstation.TgsTest.csproj", "{3146D745-AAE5-4205-8FF2-0CE471B47B4E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3146D745-AAE5-4205-8FF2-0CE471B47B4E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3146D745-AAE5-4205-8FF2-0CE471B47B4E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3146D745-AAE5-4205-8FF2-0CE471B47B4E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3146D745-AAE5-4205-8FF2-0CE471B47B4E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {001721B4-7740-419D-837E-26CE9DABCAB8} + EndGlobalSection +EndGlobal