diff --git a/README.md b/README.md index 47bcb00689..29eb729a7f 100644 --- a/README.md +++ b/README.md @@ -315,7 +315,7 @@ This folder can contain anything. But, when certain events occur in the instance #### GameStaticFiles -Any files and folders contained in this root level of this folder will be symbolically linked to all deployments at the time they are created. This allows persistent game data (BYOND `.sav`s or code configuration files for example) to persist across all deployments. +Any files and folders contained in this root level of this folder will be symbolically linked to all deployments at the time they are created. This allows persistent game data (BYOND `.sav`s or code configuration files for example) to persist across all deployments. This folder contains a .tgsignore file which can be used to prevent symlinks from being generated by entering the names of files and folders (1 per line) ### Updating diff --git a/src/Tgstation.Server.Host/Components/Byond/WindowsByondInstaller.cs b/src/Tgstation.Server.Host/Components/Byond/WindowsByondInstaller.cs index 8687d24615..c3f051fa60 100644 --- a/src/Tgstation.Server.Host/Components/Byond/WindowsByondInstaller.cs +++ b/src/Tgstation.Server.Host/Components/Byond/WindowsByondInstaller.cs @@ -124,7 +124,7 @@ namespace Tgstation.Server.Host.Components.Byond //after this version lummox made DD depend of directx lol //but then he became amazing and not only fixed it but also gave us 30s compiles \[T]/ - if (version.Major == 512 && version.Minor >= 1427 && version.Major < 1452 && !installedDirectX) + if (version.Major == 512 && version.Minor >= 1427 && version.Minor < 1452 && !installedDirectX) using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false)) //check again because race conditions if (!installedDirectX) diff --git a/src/Tgstation.Server.Host/Components/StaticFiles/Configuration.cs b/src/Tgstation.Server.Host/Components/StaticFiles/Configuration.cs index de3f3877c8..52bcd6b398 100644 --- a/src/Tgstation.Server.Host/Components/StaticFiles/Configuration.cs +++ b/src/Tgstation.Server.Host/Components/StaticFiles/Configuration.cs @@ -6,6 +6,7 @@ using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Security.Cryptography; +using System.Text; using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api.Models; @@ -22,6 +23,11 @@ namespace Tgstation.Server.Host.Components.StaticFiles const string EventScriptsSubdirectory = "EventScripts"; const string GameStaticFilesSubdirectory = "GameStaticFiles"; + /// + /// Name of the ignore file in + /// + const string StaticIgnoreFile = ".tgsignore"; + const string CodeModificationsHeadFile = "HeadInclude.dm"; const string CodeModificationsTailFile = "TailInclude.dm"; @@ -93,12 +99,29 @@ namespace Tgstation.Server.Host.Components.StaticFiles /// public void Dispose() => semaphore.Dispose(); + /// + /// Get the proper path to + /// + /// The relative path to + string StaticIgnorePath() => ioManager.ConcatPath(GameStaticFilesSubdirectory, StaticIgnoreFile); + /// /// Ensures standard configuration directories exist /// /// The for the operation /// A representing the running operation - Task EnsureDirectories(CancellationToken cancellationToken) => Task.WhenAll(ioManager.CreateDirectory(CodeModificationsSubdirectory, cancellationToken), ioManager.CreateDirectory(EventScriptsSubdirectory, cancellationToken), ioManager.CreateDirectory(GameStaticFilesSubdirectory, cancellationToken)); + async Task EnsureDirectories(CancellationToken cancellationToken) + { + async Task ValidateStaticFolder() + { + await ioManager.CreateDirectory(GameStaticFilesSubdirectory, cancellationToken).ConfigureAwait(false); + var staticIgnorePath = StaticIgnorePath(); + if(!await ioManager.FileExists(staticIgnorePath, cancellationToken).ConfigureAwait(false)) + await ioManager.WriteAllBytes(staticIgnorePath, Array.Empty(), cancellationToken).ConfigureAwait(false); + } + + await Task.WhenAll(ioManager.CreateDirectory(CodeModificationsSubdirectory, cancellationToken), ioManager.CreateDirectory(EventScriptsSubdirectory, cancellationToken), ValidateStaticFolder()).ConfigureAwait(false); + } /// public async Task CopyDMFilesTo(string dmeFile, string destination, CancellationToken cancellationToken) @@ -254,6 +277,26 @@ namespace Tgstation.Server.Host.Components.StaticFiles /// public async Task SymlinkStaticFilesTo(string destination, CancellationToken cancellationToken) { + async Task> GetIgnoreFiles() + { + var ignoreFileBytes = await ioManager.ReadAllBytes(StaticIgnorePath(), cancellationToken).ConfigureAwait(false); + var ignoreFileText = Encoding.UTF8.GetString(ignoreFileBytes); + + //we don't want to lose trailing whitespace on linux + + var results = new List { StaticIgnoreFile }; + using (var reader = new StringReader(ignoreFileText)) { + cancellationToken.ThrowIfCancellationRequested(); + var line = await reader.ReadLineAsync().ConfigureAwait(false); + if (!String.IsNullOrEmpty(line)) + results.Add(line); + } + + return results; + }; + + IReadOnlyList ignoreFiles; + async Task SymlinkBase(bool files) { Task> task; @@ -265,7 +308,22 @@ namespace Tgstation.Server.Host.Components.StaticFiles await Task.WhenAll(task.Result.Select(async x => { - var destPath = ioManager.ConcatPath(destination, ioManager.GetFileName(x)); + var fileName = ioManager.GetFileName(x); + + bool ignored; + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + //need to normalize + ignored = ignoreFiles.Any(y => fileName.ToUpperInvariant() == y.ToUpperInvariant()); + else + ignored = ignoreFiles.Any(y => fileName == y); + + if (ignored) + { + logger.LogTrace("Ignoring static file {0}...", fileName); + return; + } + + var destPath = ioManager.ConcatPath(destination, fileName); logger.LogTrace("Symlinking {0} to {1}...", x, destPath); var fileExistsTask = ioManager.FileExists(destPath, cancellationToken); if (await ioManager.DirectoryExists(destPath, cancellationToken).ConfigureAwait(false)) @@ -280,6 +338,7 @@ namespace Tgstation.Server.Host.Components.StaticFiles using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false)) { await EnsureDirectories(cancellationToken).ConfigureAwait(false); + ignoreFiles = await GetIgnoreFiles().ConfigureAwait(false); await Task.WhenAll(SymlinkBase(true), SymlinkBase(false)).ConfigureAwait(false); } }