diff --git a/build/Version.props b/build/Version.props
index c94784c13d..1b16b06328 100644
--- a/build/Version.props
+++ b/build/Version.props
@@ -3,7 +3,7 @@
- 5.2.0
+ 5.2.1
4.4.0
9.7.0
10.1.0
diff --git a/src/Tgstation.Server.Host/Components/InstanceFactory.cs b/src/Tgstation.Server.Host/Components/InstanceFactory.cs
index 57a44f29b2..ff4fc85c11 100644
--- a/src/Tgstation.Server.Host/Components/InstanceFactory.cs
+++ b/src/Tgstation.Server.Host/Components/InstanceFactory.cs
@@ -251,6 +251,7 @@ namespace Tgstation.Server.Host.Components
repositoryCommands,
repoIoManager,
eventConsumer,
+ postWriteHandler,
gitRemoteFeaturesFactory,
loggerFactory.CreateLogger(),
loggerFactory.CreateLogger());
diff --git a/src/Tgstation.Server.Host/Components/Repository/Repository.cs b/src/Tgstation.Server.Host/Components/Repository/Repository.cs
index 9990d584ce..9e0bea04f3 100644
--- a/src/Tgstation.Server.Host/Components/Repository/Repository.cs
+++ b/src/Tgstation.Server.Host/Components/Repository/Repository.cs
@@ -91,6 +91,11 @@ namespace Tgstation.Server.Host.Components.Repository
///
readonly ICredentialsProvider credentialsProvider;
+ ///
+ /// The for the .
+ ///
+ readonly IPostWriteHandler postWriteHandler;
+
///
/// The for the .
///
@@ -119,6 +124,7 @@ namespace Tgstation.Server.Host.Components.Repository
/// The value of .
/// The value of .
/// The value of .
+ /// The value of .
/// The to provide the value of .
/// The value of .
/// The value if .
@@ -128,6 +134,7 @@ namespace Tgstation.Server.Host.Components.Repository
IIOManager ioMananger,
IEventConsumer eventConsumer,
ICredentialsProvider credentialsProvider,
+ IPostWriteHandler postWriteHandler,
IGitRemoteFeaturesFactory gitRemoteFeaturesFactory,
ILogger logger,
Action onDispose)
@@ -137,6 +144,7 @@ namespace Tgstation.Server.Host.Components.Repository
this.ioMananger = ioMananger ?? throw new ArgumentNullException(nameof(ioMananger));
this.eventConsumer = eventConsumer ?? throw new ArgumentNullException(nameof(eventConsumer));
this.credentialsProvider = credentialsProvider ?? throw new ArgumentNullException(nameof(credentialsProvider));
+ this.postWriteHandler = postWriteHandler ?? throw new ArgumentNullException(nameof(postWriteHandler));
if (gitRemoteFeaturesFactory == null)
throw new ArgumentNullException(nameof(gitRemoteFeaturesFactory));
@@ -500,7 +508,18 @@ namespace Tgstation.Server.Host.Components.Repository
if (path == null)
throw new ArgumentNullException(nameof(path));
logger.LogTrace("Copying to {0}...", path);
- await ioMananger.CopyDirectory(ioMananger.ResolvePath(), path, new List { ".git" }, cancellationToken);
+ await ioMananger.CopyDirectory(
+ ioMananger.ResolvePath(),
+ path,
+ new List { ".git" },
+ (src, dest) =>
+ {
+ if (postWriteHandler.NeedsPostWrite(src))
+ postWriteHandler.HandleWrite(dest);
+
+ return Task.CompletedTask;
+ },
+ cancellationToken);
}
///
diff --git a/src/Tgstation.Server.Host/Components/Repository/RepositoryManager.cs b/src/Tgstation.Server.Host/Components/Repository/RepositoryManager.cs
index 46e3712f62..ba78fb23c5 100644
--- a/src/Tgstation.Server.Host/Components/Repository/RepositoryManager.cs
+++ b/src/Tgstation.Server.Host/Components/Repository/RepositoryManager.cs
@@ -42,6 +42,11 @@ namespace Tgstation.Server.Host.Components.Repository
///
readonly IEventConsumer eventConsumer;
+ ///
+ /// The for the .
+ ///
+ readonly IPostWriteHandler postWriteHandler;
+
///
/// The for the .
///
@@ -69,6 +74,7 @@ namespace Tgstation.Server.Host.Components.Repository
/// The value of .
/// The value of .
/// The value of .
+ /// The value of .
/// The value of .
/// The value of .
/// The value of .
@@ -77,6 +83,7 @@ namespace Tgstation.Server.Host.Components.Repository
ILibGit2Commands commands,
IIOManager ioManager,
IEventConsumer eventConsumer,
+ IPostWriteHandler postWriteHandler,
IGitRemoteFeaturesFactory gitRemoteFeaturesFactory,
ILogger repositoryLogger,
ILogger logger)
@@ -85,6 +92,7 @@ namespace Tgstation.Server.Host.Components.Repository
this.commands = commands ?? throw new ArgumentNullException(nameof(commands));
this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
this.eventConsumer = eventConsumer ?? throw new ArgumentNullException(nameof(eventConsumer));
+ this.postWriteHandler = postWriteHandler ?? throw new ArgumentNullException(nameof(postWriteHandler));
this.gitRemoteFeaturesFactory = gitRemoteFeaturesFactory ?? throw new ArgumentNullException(nameof(gitRemoteFeaturesFactory));
this.repositoryLogger = repositoryLogger ?? throw new ArgumentNullException(nameof(repositoryLogger));
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
@@ -206,6 +214,7 @@ namespace Tgstation.Server.Host.Components.Repository
ioManager,
eventConsumer,
repositoryFactory,
+ postWriteHandler,
gitRemoteFeaturesFactory,
repositoryLogger,
() =>
diff --git a/src/Tgstation.Server.Host/Components/StaticFiles/Configuration.cs b/src/Tgstation.Server.Host/Components/StaticFiles/Configuration.cs
index 98d63bf5d8..7b84ce8ebf 100644
--- a/src/Tgstation.Server.Host/Components/StaticFiles/Configuration.cs
+++ b/src/Tgstation.Server.Host/Components/StaticFiles/Configuration.cs
@@ -180,7 +180,7 @@ namespace Tgstation.Server.Host.Components.StaticFiles
var dmeExistsTask = ioManager.FileExists(ioManager.ConcatPath(CodeModificationsSubdirectory, dmeFile), cancellationToken);
var headFileExistsTask = ioManager.FileExists(ioManager.ConcatPath(CodeModificationsSubdirectory, CodeModificationsHeadFile), cancellationToken);
var tailFileExistsTask = ioManager.FileExists(ioManager.ConcatPath(CodeModificationsSubdirectory, CodeModificationsTailFile), cancellationToken);
- var copyTask = ioManager.CopyDirectory(CodeModificationsSubdirectory, destination, null, cancellationToken);
+ var copyTask = ioManager.CopyDirectory(CodeModificationsSubdirectory, destination, null, null, cancellationToken);
await Task.WhenAll(dmeExistsTask, headFileExistsTask, tailFileExistsTask, copyTask);
diff --git a/src/Tgstation.Server.Host/IO/DefaultIOManager.cs b/src/Tgstation.Server.Host/IO/DefaultIOManager.cs
index bc46817039..2fd09a6d07 100644
--- a/src/Tgstation.Server.Host/IO/DefaultIOManager.cs
+++ b/src/Tgstation.Server.Host/IO/DefaultIOManager.cs
@@ -88,7 +88,12 @@ namespace Tgstation.Server.Host.IO
}
///
- public async Task CopyDirectory(string src, string dest, IEnumerable ignore, CancellationToken cancellationToken)
+ public async Task CopyDirectory(
+ string src,
+ string dest,
+ IEnumerable ignore,
+ Func postCopyCallback,
+ CancellationToken cancellationToken)
{
if (src == null)
throw new ArgumentNullException(nameof(src));
@@ -97,8 +102,7 @@ namespace Tgstation.Server.Host.IO
src = ResolvePath(src);
dest = ResolvePath(dest);
- foreach (var directoryCopy in CopyDirectoryImpl(src, dest, ignore, cancellationToken))
- await directoryCopy;
+ await Task.WhenAll(CopyDirectoryImpl(src, dest, ignore, postCopyCallback, cancellationToken));
}
///
@@ -368,37 +372,49 @@ namespace Tgstation.Server.Host.IO
/// The source directory path.
/// The destination directory path.
/// Files and folders to ignore at the root level.
+ /// The optional callback called for each source/dest file pair post copy.
/// The for the operation.
/// A of s representing the running operation.
- IEnumerable CopyDirectoryImpl(string src, string dest, IEnumerable ignore, CancellationToken cancellationToken)
+ IEnumerable CopyDirectoryImpl(
+ string src,
+ string dest,
+ IEnumerable ignore,
+ Func postCopyCallback,
+ CancellationToken cancellationToken)
{
var dir = new DirectoryInfo(src);
- var atLeastOneSubDir = false;
foreach (var subDirectory in dir.EnumerateDirectories())
{
if (ignore != null && ignore.Contains(subDirectory.Name))
continue;
- foreach (var copyTask in CopyDirectoryImpl(subDirectory.FullName, Path.Combine(dest, subDirectory.Name), null, cancellationToken))
- {
- atLeastOneSubDir = true;
+ foreach (var copyTask in CopyDirectoryImpl(subDirectory.FullName, Path.Combine(dest, subDirectory.Name), null, postCopyCallback, cancellationToken))
yield return copyTask;
- }
}
async Task CopyThisDirectory()
{
- if (!atLeastOneSubDir)
- await CreateDirectory(dest, cancellationToken); // save on createdir calls
+ await CreateDirectory(dest, cancellationToken);
- var tasks = new List();
+ var fileCopyTasks = new List();
foreach (var fileInfo in dir.EnumerateFiles())
{
if (ignore != null && ignore.Contains(fileInfo.Name))
return;
- tasks.Add(CopyFile(fileInfo.FullName, Path.Combine(dest, fileInfo.Name), cancellationToken));
+
+ var sourceFile = fileInfo.FullName;
+ var destFile = Path.Combine(dest, fileInfo.Name);
+
+ async Task CopyThisFile()
+ {
+ await CopyFile(sourceFile, destFile, cancellationToken);
+ if (postCopyCallback != null)
+ await postCopyCallback(sourceFile, destFile);
+ }
+
+ fileCopyTasks.Add(CopyThisFile());
}
- await Task.WhenAll(tasks);
+ await Task.WhenAll(fileCopyTasks);
}
yield return CopyThisDirectory();
diff --git a/src/Tgstation.Server.Host/IO/IIOManager.cs b/src/Tgstation.Server.Host/IO/IIOManager.cs
index 6f7dd75bbf..2e50ee317d 100644
--- a/src/Tgstation.Server.Host/IO/IIOManager.cs
+++ b/src/Tgstation.Server.Host/IO/IIOManager.cs
@@ -51,9 +51,15 @@ namespace Tgstation.Server.Host.IO
/// The source directory path.
/// The destination directory path.
/// Files and folders to ignore at the root level.
+ /// The optional callback called for each source/dest file pair post copy.
/// The for the operation.
/// A representing the running operation.
- Task CopyDirectory(string src, string dest, IEnumerable ignore, CancellationToken cancellationToken);
+ Task CopyDirectory(
+ string src,
+ string dest,
+ IEnumerable ignore,
+ Func postCopyCallback,
+ CancellationToken cancellationToken);
///
/// Check that the file at exists.
diff --git a/src/Tgstation.Server.Host/IO/IPostWriteHandler.cs b/src/Tgstation.Server.Host/IO/IPostWriteHandler.cs
index e99a2eb1e4..9b21ab9ef8 100644
--- a/src/Tgstation.Server.Host/IO/IPostWriteHandler.cs
+++ b/src/Tgstation.Server.Host/IO/IPostWriteHandler.cs
@@ -5,6 +5,13 @@
///
interface IPostWriteHandler
{
+ ///
+ /// Check if a given will need called on a copy of it.
+ ///
+ /// The path of the source file to check.
+ /// if should be called on copies of , otherwise.
+ public bool NeedsPostWrite(string sourceFilePath);
+
///
/// For handling system specific necessities after a write.
///
diff --git a/src/Tgstation.Server.Host/IO/PosixPostWriteHandler.cs b/src/Tgstation.Server.Host/IO/PosixPostWriteHandler.cs
index b2f9bf9143..a69f77ac74 100644
--- a/src/Tgstation.Server.Host/IO/PosixPostWriteHandler.cs
+++ b/src/Tgstation.Server.Host/IO/PosixPostWriteHandler.cs
@@ -25,6 +25,18 @@ namespace Tgstation.Server.Host.IO
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
+ ///
+ public bool NeedsPostWrite(string sourceFilePath)
+ {
+ if (sourceFilePath == null)
+ throw new ArgumentNullException(nameof(sourceFilePath));
+
+ if (Syscall.stat(sourceFilePath, out var stat) != 0)
+ throw new UnixIOException(Stdlib.GetLastError());
+
+ return stat.st_mode.HasFlag(FilePermissions.S_IXUSR);
+ }
+
///
public void HandleWrite(string filePath)
{
diff --git a/src/Tgstation.Server.Host/IO/WindowsPostWriteHandler.cs b/src/Tgstation.Server.Host/IO/WindowsPostWriteHandler.cs
index bec6986208..76c4b0f9c6 100644
--- a/src/Tgstation.Server.Host/IO/WindowsPostWriteHandler.cs
+++ b/src/Tgstation.Server.Host/IO/WindowsPostWriteHandler.cs
@@ -7,6 +7,15 @@ namespace Tgstation.Server.Host.IO
///
sealed class WindowsPostWriteHandler : IPostWriteHandler
{
+ ///
+ public bool NeedsPostWrite(string sourceFilePath)
+ {
+ if (sourceFilePath == null)
+ throw new ArgumentNullException(nameof(sourceFilePath));
+
+ return false;
+ }
+
///
public void HandleWrite(string filePath)
{
diff --git a/tests/Tgstation.Server.Host.Tests/IO/TestPostWriteHandler.cs b/tests/Tgstation.Server.Host.Tests/IO/TestPostWriteHandler.cs
index 40864bcba2..8c6e1cbaef 100644
--- a/tests/Tgstation.Server.Host.Tests/IO/TestPostWriteHandler.cs
+++ b/tests/Tgstation.Server.Host.Tests/IO/TestPostWriteHandler.cs
@@ -24,6 +24,7 @@ namespace Tgstation.Server.Host.IO.Tests
postWriteHandler = new PosixPostWriteHandler(Mock.Of>());
Assert.ThrowsException(() => postWriteHandler.HandleWrite(null));
+ Assert.ThrowsException(() => postWriteHandler.NeedsPostWrite(null));
}
[TestMethod]
@@ -40,12 +41,14 @@ namespace Tgstation.Server.Host.IO.Tests
var tmpFile = Path.GetTempFileName();
try
{
+ Assert.IsFalse(postWriteHandler.NeedsPostWrite(tmpFile));
postWriteHandler.HandleWrite(tmpFile);
if (isWindows)
return; //you do nothing
//ensure it is now executable
+ Assert.IsTrue(postWriteHandler.NeedsPostWrite(tmpFile));
File.WriteAllBytes(tmpFile, Encoding.UTF8.GetBytes("#!/bin/sh\n"));
using (var process = Process.Start(tmpFile))
diff --git a/tests/Tgstation.Server.Tests/IntegrationTest.cs b/tests/Tgstation.Server.Tests/IntegrationTest.cs
index 7de3b4cdb4..09e7592142 100644
--- a/tests/Tgstation.Server.Tests/IntegrationTest.cs
+++ b/tests/Tgstation.Server.Tests/IntegrationTest.cs
@@ -1073,6 +1073,7 @@ namespace Tgstation.Server.Tests
Mock.Of(),
Mock.Of(),
Mock.Of(),
+ Mock.Of(),
Mock.Of(),
Mock.Of>(),
() => { });