mirror of
https://github.com/tgstation/tgstation-server.git
synced 2026-08-26 06:27:19 +01:00
Merge pull request #1442 from tgstation/FileOutput [APIDeploy][DMDeploy]
Add support for reading DreamDaemon output to file + other stuff
This commit is contained in:
+5
-5
@@ -3,12 +3,12 @@
|
||||
<!-- Integration tests will ensure they match across the board -->
|
||||
<Import Project="ControlPanelVersion.props" />
|
||||
<PropertyGroup>
|
||||
<TgsCoreVersion>5.6.0</TgsCoreVersion>
|
||||
<TgsCoreVersion>5.7.0</TgsCoreVersion>
|
||||
<TgsConfigVersion>4.4.0</TgsConfigVersion>
|
||||
<TgsApiVersion>9.8.1</TgsApiVersion>
|
||||
<TgsApiLibraryVersion>10.2.0</TgsApiLibraryVersion>
|
||||
<TgsClientVersion>11.2.1</TgsClientVersion>
|
||||
<TgsDmapiVersion>6.1.0</TgsDmapiVersion>
|
||||
<TgsApiVersion>9.9.0</TgsApiVersion>
|
||||
<TgsApiLibraryVersion>10.3.0</TgsApiLibraryVersion>
|
||||
<TgsClientVersion>11.3.0</TgsClientVersion>
|
||||
<TgsDmapiVersion>6.2.0</TgsDmapiVersion>
|
||||
<TgsInteropVersion>5.4.0</TgsInteropVersion>
|
||||
<TgsHostWatchdogVersion>1.2.1</TgsHostWatchdogVersion>
|
||||
<TgsContainerScriptVersion>1.2.1</TgsContainerScriptVersion>
|
||||
|
||||
+3
-1
@@ -1,6 +1,6 @@
|
||||
// tgstation-server DMAPI
|
||||
|
||||
#define TGS_DMAPI_VERSION "6.1.0"
|
||||
#define TGS_DMAPI_VERSION "6.2.0"
|
||||
|
||||
// All functions and datums outside this document are subject to change with any version and should not be relied on.
|
||||
|
||||
@@ -258,6 +258,8 @@
|
||||
var/help_text = ""
|
||||
/// If this command should be available to game administrators only
|
||||
var/admin_only = FALSE
|
||||
/// A subtype of [/datum/tgs_chat_command] that is ignored when enumerating available commands. Use this to create shared base /datums for commands.
|
||||
var/ignore_type
|
||||
|
||||
/**
|
||||
* Process command activation. Should return a [/datum/tgs_message_content] to respond to the issuer with.
|
||||
|
||||
@@ -10,9 +10,12 @@
|
||||
var/warned_about_the_dangers_of_robutussin = !warnings_only
|
||||
for(var/I in typesof(/datum/tgs_chat_command) - /datum/tgs_chat_command)
|
||||
if(!warned_about_the_dangers_of_robutussin)
|
||||
TGS_ERROR_LOG("Custom chat commands in [ApiVersion()] lacks the /datum/tgs_chat_user/sender.channel field!")
|
||||
TGS_WARNING_LOG("Custom chat commands in [ApiVersion()] lacks the /datum/tgs_chat_user/sender.channel field!")
|
||||
warned_about_the_dangers_of_robutussin = TRUE
|
||||
var/datum/tgs_chat_command/stc = I
|
||||
if(stc.ignore_type == I)
|
||||
continue
|
||||
|
||||
var/command_name = initial(stc.name)
|
||||
if(!command_name || findtext(command_name, " ") || findtext(command_name, "'") || findtext(command_name, "\""))
|
||||
if(warnings_only && !warned_command_names[command_name])
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
custom_commands = list()
|
||||
for(var/I in typesof(/datum/tgs_chat_command) - /datum/tgs_chat_command)
|
||||
var/datum/tgs_chat_command/stc = new I
|
||||
if(stc.ignore_type == I)
|
||||
continue
|
||||
|
||||
var/command_name = stc.name
|
||||
if(!command_name || findtext(command_name, " ") || findtext(command_name, "'") || findtext(command_name, "\""))
|
||||
TGS_ERROR_LOG("Custom command [command_name] ([I]) can't be used as it is empty or contains illegal characters!")
|
||||
|
||||
@@ -3,14 +3,17 @@
|
||||
custom_commands = list()
|
||||
for(var/I in typesof(/datum/tgs_chat_command) - /datum/tgs_chat_command)
|
||||
var/datum/tgs_chat_command/stc = new I
|
||||
if(stc.ignore_type == I)
|
||||
continue
|
||||
|
||||
var/command_name = stc.name
|
||||
if(!command_name || findtext(command_name, " ") || findtext(command_name, "'") || findtext(command_name, "\""))
|
||||
TGS_WARNING_LOG("Custom command [command_name] ([I]) can't be used as it is empty or contains illegal characters!")
|
||||
TGS_ERROR_LOG("Custom command [command_name] ([I]) can't be used as it is empty or contains illegal characters!")
|
||||
continue
|
||||
|
||||
if(results[command_name])
|
||||
var/datum/other = custom_commands[command_name]
|
||||
TGS_WARNING_LOG("Custom commands [other.type] and [I] have the same name (\"[command_name]\"), only [other.type] will be available!")
|
||||
TGS_ERROR_LOG("Custom commands [other.type] and [I] have the same name (\"[command_name]\"), only [other.type] will be available!")
|
||||
continue
|
||||
results += list(list(DMAPI5_CUSTOM_CHAT_COMMAND_NAME = command_name, DMAPI5_CUSTOM_CHAT_COMMAND_HELP_TEXT = stc.help_text, DMAPI5_CUSTOM_CHAT_COMMAND_ADMIN_ONLY = stc.admin_only))
|
||||
custom_commands[command_name] = stc
|
||||
|
||||
@@ -84,6 +84,13 @@ namespace Tgstation.Server.Api.Models.Internal
|
||||
[StringLength(Limits.MaximumStringLength)]
|
||||
public string? AdditionalParameters { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// If process output/error text should be logged.
|
||||
/// </summary>
|
||||
[Required]
|
||||
[ResponseOptions]
|
||||
public bool? LogOutput { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Check if we match a given set of <paramref name="otherParameters"/>. <see cref="StartupTimeout"/> is excluded.
|
||||
/// </summary>
|
||||
@@ -100,7 +107,8 @@ namespace Tgstation.Server.Api.Models.Internal
|
||||
&& Port == otherParameters.Port
|
||||
&& TopicRequestTimeout == otherParameters.TopicRequestTimeout
|
||||
&& AdditionalParameters == otherParameters.AdditionalParameters
|
||||
&& StartProfiler == otherParameters.StartProfiler; // We intentionally don't check StartupTimeout, heartbeat seconds, or heartbeat dump as they don't matter in terms of the watchdog
|
||||
&& StartProfiler == otherParameters.StartProfiler
|
||||
&& LogOutput == otherParameters.LogOutput; // We intentionally don't check StartupTimeout, heartbeat seconds, or heartbeat dump as they don't matter in terms of the watchdog
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,5 +102,10 @@ namespace Tgstation.Server.Api.Rights
|
||||
/// User can change <see cref="Models.Internal.DreamDaemonLaunchParameters.StartProfiler"/>
|
||||
/// </summary>
|
||||
SetProfiler = 131072,
|
||||
|
||||
/// <summary>
|
||||
/// User can change <see cref="Models.Internal.DreamDaemonLaunchParameters.LogOutput"/>
|
||||
/// </summary>
|
||||
SetLogOutput = 262144,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,7 +187,7 @@ namespace Tgstation.Server.Host.Components.Byond
|
||||
try
|
||||
{
|
||||
// noShellExecute because we aren't doing runas shennanigans
|
||||
using var directXInstaller = processExecutor.LaunchProcess(
|
||||
await using var directXInstaller = await processExecutor.LaunchProcess(
|
||||
IOManager.ConcatPath(rbdx, "DXSETUP.exe"),
|
||||
rbdx,
|
||||
"/silent",
|
||||
@@ -228,13 +228,12 @@ namespace Tgstation.Server.Host.Components.Byond
|
||||
Logger.LogInformation("Adding Windows Firewall exception for {path}...", dreamDaemonPath);
|
||||
try
|
||||
{
|
||||
using var netshProcess = processExecutor.LaunchProcess(
|
||||
await using var netshProcess = await processExecutor.LaunchProcess(
|
||||
"netsh.exe",
|
||||
IOManager.ResolvePath(),
|
||||
$"advfirewall firewall add rule name=\"TGS DreamDaemon\" program=\"{dreamDaemonPath}\" protocol=tcp dir=in enable=yes action=allow",
|
||||
true,
|
||||
true,
|
||||
true);
|
||||
readStandardHandles: true,
|
||||
noShellExecute: true);
|
||||
|
||||
int exitCode;
|
||||
using (cancellationToken.Register(() => netshProcess.Terminate()))
|
||||
|
||||
@@ -235,6 +235,7 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
.Select(x => new Models.DreamDaemonSettings
|
||||
{
|
||||
StartupTimeout = x.StartupTimeout,
|
||||
LogOutput = x.LogOutput,
|
||||
})
|
||||
.FirstOrDefaultAsync(cancellationToken)
|
||||
;
|
||||
@@ -325,7 +326,7 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
compileJob = await Compile(
|
||||
revInfo,
|
||||
dreamMakerSettings,
|
||||
ddSettings.StartupTimeout.Value,
|
||||
ddSettings,
|
||||
repo,
|
||||
remoteDeploymentManager,
|
||||
progressReporter,
|
||||
@@ -462,7 +463,7 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
/// </summary>
|
||||
/// <param name="revisionInformation">The <see cref="RevisionInformation"/>.</param>
|
||||
/// <param name="dreamMakerSettings">The <see cref="Api.Models.Internal.DreamMakerSettings"/>.</param>
|
||||
/// <param name="apiValidateTimeout">The API validation timeout.</param>
|
||||
/// <param name="launchParameters">The <see cref="DreamDaemonLaunchParameters"/>.</param>
|
||||
/// <param name="repository">The <see cref="IRepository"/>.</param>
|
||||
/// <param name="remoteDeploymentManager">The <see cref="IRemoteDeploymentManager"/>.</param>
|
||||
/// <param name="progressReporter">The <see cref="JobProgressReporter"/> to report progress of the operation.</param>
|
||||
@@ -473,7 +474,7 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
async Task<Models.CompileJob> Compile(
|
||||
Models.RevisionInformation revisionInformation,
|
||||
Api.Models.Internal.DreamMakerSettings dreamMakerSettings,
|
||||
uint apiValidateTimeout,
|
||||
DreamDaemonLaunchParameters launchParameters,
|
||||
IRepository repository,
|
||||
IRemoteDeploymentManager remoteDeploymentManager,
|
||||
JobProgressReporter progressReporter,
|
||||
@@ -525,10 +526,10 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
await RunCompileJob(
|
||||
job,
|
||||
dreamMakerSettings,
|
||||
launchParameters,
|
||||
byondLock,
|
||||
repository,
|
||||
remoteDeploymentManager,
|
||||
apiValidateTimeout,
|
||||
combinedTokenSource.Token)
|
||||
;
|
||||
}
|
||||
@@ -559,19 +560,19 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
/// </summary>
|
||||
/// <param name="job">The <see cref="CompileJob"/> to run and populate.</param>
|
||||
/// <param name="dreamMakerSettings">The <see cref="Api.Models.Internal.DreamMakerSettings"/> to use.</param>
|
||||
/// <param name="launchParameters">The <see cref="DreamDaemonLaunchParameters"/> to use.</param>
|
||||
/// <param name="byondLock">The <see cref="IByondExecutableLock"/> to use.</param>
|
||||
/// <param name="repository">The <see cref="IRepository"/> to use.</param>
|
||||
/// <param name="remoteDeploymentManager">The <see cref="IRemoteDeploymentManager"/> to use.</param>
|
||||
/// <param name="apiValidateTimeout">The timeout for validating the DMAPI.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the running operation.</returns>
|
||||
async Task RunCompileJob(
|
||||
Models.CompileJob job,
|
||||
Api.Models.Internal.DreamMakerSettings dreamMakerSettings,
|
||||
DreamDaemonLaunchParameters launchParameters,
|
||||
IByondExecutableLock byondLock,
|
||||
IRepository repository,
|
||||
IRemoteDeploymentManager remoteDeploymentManager,
|
||||
uint apiValidateTimeout,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var outputDirectory = job.DirectoryName.ToString();
|
||||
@@ -655,14 +656,14 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
|
||||
currentStage = "Validating DMAPI";
|
||||
await VerifyApi(
|
||||
apiValidateTimeout,
|
||||
launchParameters.StartupTimeout.Value,
|
||||
dreamMakerSettings.ApiValidationSecurityLevel.Value,
|
||||
job,
|
||||
byondLock,
|
||||
dreamMakerSettings.ApiValidationPort.Value,
|
||||
dreamMakerSettings.RequireDMApiValidation.Value,
|
||||
cancellationToken)
|
||||
;
|
||||
launchParameters.LogOutput.Value,
|
||||
cancellationToken);
|
||||
}
|
||||
catch (JobException)
|
||||
{
|
||||
@@ -717,9 +718,8 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
/// <returns>A <see cref="Task"/> representing the running operation.</returns>
|
||||
async Task ProgressTask(JobProgressReporter progressReporter, TimeSpan? estimatedDuration, CancellationToken cancellationToken)
|
||||
{
|
||||
var noEstimate = !estimatedDuration.HasValue;
|
||||
progressReporter.StageName = currentStage;
|
||||
double? lastReport = noEstimate ? null : 0;
|
||||
double? lastReport = estimatedDuration.HasValue ? 0 : null;
|
||||
progressReporter.ReportProgress(lastReport);
|
||||
|
||||
var minimumSleepInterval = TimeSpan.FromMilliseconds(250);
|
||||
@@ -738,25 +738,31 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
{
|
||||
for (var iteration = 0; iteration < (estimatedDuration.HasValue ? 99 : Int32.MaxValue); ++iteration)
|
||||
{
|
||||
var nextInterval = DateTimeOffset.UtcNow + sleepInterval;
|
||||
do
|
||||
if (estimatedDuration.HasValue)
|
||||
{
|
||||
var remainingSleepThisInterval = nextInterval - DateTimeOffset.UtcNow;
|
||||
var nextSleepSpan = remainingSleepThisInterval < minimumSleepInterval ? remainingSleepThisInterval : minimumSleepInterval;
|
||||
var nextInterval = DateTimeOffset.UtcNow + sleepInterval;
|
||||
do
|
||||
{
|
||||
var remainingSleepThisInterval = nextInterval - DateTimeOffset.UtcNow;
|
||||
var nextSleepSpan = remainingSleepThisInterval < minimumSleepInterval ? remainingSleepThisInterval : minimumSleepInterval;
|
||||
|
||||
await Task.Delay(nextSleepSpan, cancellationToken);
|
||||
progressReporter.StageName = currentStage;
|
||||
progressReporter.ReportProgress(lastReport);
|
||||
await Task.Delay(nextSleepSpan, cancellationToken);
|
||||
progressReporter.StageName = currentStage;
|
||||
progressReporter.ReportProgress(lastReport);
|
||||
}
|
||||
while (DateTimeOffset.UtcNow < nextInterval);
|
||||
}
|
||||
while (DateTimeOffset.UtcNow < nextInterval);
|
||||
else
|
||||
await Task.Delay(minimumSleepInterval, cancellationToken);
|
||||
|
||||
progressReporter.StageName = currentStage;
|
||||
lastReport = noEstimate ? null : sleepInterval * (iteration + 1) / estimatedDuration.Value;
|
||||
lastReport = estimatedDuration.HasValue ? sleepInterval * (iteration + 1) / estimatedDuration.Value : null;
|
||||
progressReporter.ReportProgress(lastReport);
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
catch (OperationCanceledException ex)
|
||||
{
|
||||
logger.LogTrace(ex, "ProgressTask aborted.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -769,6 +775,7 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
/// <param name="byondLock">The current <see cref="IByondExecutableLock"/>.</param>
|
||||
/// <param name="portToUse">The port to use for API validation.</param>
|
||||
/// <param name="requireValidate">If the API validation is required to complete the deployment.</param>
|
||||
/// <param name="logOutput">If output should be logged to the DreamDaemon Diagnostics folder.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the running operation.</returns>
|
||||
async Task VerifyApi(
|
||||
@@ -778,6 +785,7 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
IByondExecutableLock byondLock,
|
||||
ushort portToUse,
|
||||
bool requireValidate,
|
||||
bool logOutput,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
logger.LogTrace("Verifying {0}DMAPI...", requireValidate ? "required " : String.Empty);
|
||||
@@ -791,6 +799,7 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
TopicRequestTimeout = 0, // not used
|
||||
HeartbeatSeconds = 0, // not used
|
||||
StartProfiler = false,
|
||||
LogOutput = logOutput,
|
||||
};
|
||||
|
||||
job.MinimumSecurityLevel = securityLevel; // needed for the TempDmbProvider
|
||||
@@ -854,14 +863,13 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
/// <returns>A <see cref="Task"/> representing the running operation.</returns>
|
||||
async Task<int> RunDreamMaker(string dreamMakerPath, Models.CompileJob job, CancellationToken cancellationToken)
|
||||
{
|
||||
using var dm = processExecutor.LaunchProcess(
|
||||
await using var dm = await processExecutor.LaunchProcess(
|
||||
dreamMakerPath,
|
||||
ioManager.ResolvePath(
|
||||
job.DirectoryName.ToString()),
|
||||
$"-clean {job.DmeName}.{DmeExtension}",
|
||||
true,
|
||||
true,
|
||||
true);
|
||||
readStandardHandles: true,
|
||||
noShellExecute: true);
|
||||
|
||||
if (sessionConfiguration.LowPriorityDeploymentProcesses)
|
||||
dm.AdjustPriority(false);
|
||||
|
||||
@@ -271,6 +271,7 @@ namespace Tgstation.Server.Host.Components
|
||||
cryptographySuite,
|
||||
assemblyInformationProvider,
|
||||
gameIoManager,
|
||||
diagnosticsIOManager,
|
||||
chatManager,
|
||||
networkPromptReaper,
|
||||
platformIdentifier,
|
||||
@@ -308,10 +309,11 @@ namespace Tgstation.Server.Host.Components
|
||||
remoteDeploymentManagerFactory,
|
||||
metadata,
|
||||
metadata.DreamDaemonSettings);
|
||||
eventConsumer.SetWatchdog(watchdog);
|
||||
commandFactory.SetWatchdog(watchdog);
|
||||
try
|
||||
{
|
||||
eventConsumer.SetWatchdog(watchdog);
|
||||
commandFactory.SetWatchdog(watchdog);
|
||||
|
||||
Instance instance = null;
|
||||
var dreamMaker = new DreamMaker(
|
||||
byond,
|
||||
|
||||
@@ -275,7 +275,7 @@ namespace Tgstation.Server.Host.Components.Session
|
||||
byondLock.Dispose();
|
||||
}
|
||||
|
||||
process.Dispose();
|
||||
await process.DisposeAsync();
|
||||
bridgeRegistration?.Dispose();
|
||||
ReattachInformation.Dmb?.Dispose(); // will be null when released
|
||||
chatTrackingContext.Dispose();
|
||||
@@ -656,8 +656,8 @@ namespace Tgstation.Server.Host.Components.Session
|
||||
|
||||
var result = new LaunchResult
|
||||
{
|
||||
ExitCode = process.Lifetime.IsCompleted ? (int?)await process.Lifetime : null,
|
||||
StartupTime = startupTask.IsCompleted ? (TimeSpan?)(DateTimeOffset.UtcNow - startTime) : null,
|
||||
ExitCode = process.Lifetime.IsCompleted ? await process.Lifetime : null,
|
||||
StartupTime = startupTask.IsCompleted ? (DateTimeOffset.UtcNow - startTime) : null,
|
||||
};
|
||||
|
||||
logger.LogTrace("Launch result: {0}", result);
|
||||
|
||||
@@ -6,6 +6,8 @@ using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Byond.TopicSender;
|
||||
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using Tgstation.Server.Api;
|
||||
@@ -30,6 +32,11 @@ namespace Tgstation.Server.Host.Components.Session
|
||||
/// <inheritdoc />
|
||||
sealed class SessionControllerFactory : ISessionControllerFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Path in Diagnostics folder to DreamDaemon logs.
|
||||
/// </summary>
|
||||
const string DreamDaemonLogsPath = "DreamDaemonLogs";
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IProcessExecutor"/> for the <see cref="SessionControllerFactory"/>.
|
||||
/// </summary>
|
||||
@@ -56,9 +63,14 @@ namespace Tgstation.Server.Host.Components.Session
|
||||
readonly IAssemblyInformationProvider assemblyInformationProvider;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IIOManager"/> for the <see cref="SessionControllerFactory"/>.
|
||||
/// The <see cref="IIOManager"/> for the Game directory.
|
||||
/// </summary>
|
||||
readonly IIOManager ioManager;
|
||||
readonly IIOManager gameIOManager;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IIOManager"/> for the Diagnostics directory.
|
||||
/// </summary>
|
||||
readonly IIOManager diagnosticsIOManager;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IChatManager"/> for the <see cref="SessionControllerFactory"/>.
|
||||
@@ -168,7 +180,8 @@ namespace Tgstation.Server.Host.Components.Session
|
||||
/// <param name="cryptographySuite">The value of <see cref="cryptographySuite"/>.</param>
|
||||
/// <param name="assemblyInformationProvider">The value of <see cref="assemblyInformationProvider"/>.</param>
|
||||
/// <param name="instance">The value of <see cref="instance"/>.</param>
|
||||
/// <param name="ioManager">The value of <see cref="ioManager"/>.</param>
|
||||
/// <param name="gameIOManager">The value of <see cref="gameIOManager"/>.</param>
|
||||
/// <param name="diagnosticsIOManager">The value of <see cref="diagnosticsIOManager"/>.</param>
|
||||
/// <param name="chat">The value of <see cref="chat"/>.</param>
|
||||
/// <param name="networkPromptReaper">The value of <see cref="networkPromptReaper"/>.</param>
|
||||
/// <param name="platformIdentifier">The value of <see cref="platformIdentifier"/>.</param>
|
||||
@@ -184,7 +197,8 @@ namespace Tgstation.Server.Host.Components.Session
|
||||
ITopicClientFactory topicClientFactory,
|
||||
ICryptographySuite cryptographySuite,
|
||||
IAssemblyInformationProvider assemblyInformationProvider,
|
||||
IIOManager ioManager,
|
||||
IIOManager gameIOManager,
|
||||
IIOManager diagnosticsIOManager,
|
||||
IChatManager chat,
|
||||
INetworkPromptReaper networkPromptReaper,
|
||||
IPlatformIdentifier platformIdentifier,
|
||||
@@ -201,7 +215,8 @@ namespace Tgstation.Server.Host.Components.Session
|
||||
this.topicClientFactory = topicClientFactory ?? throw new ArgumentNullException(nameof(topicClientFactory));
|
||||
this.cryptographySuite = cryptographySuite ?? throw new ArgumentNullException(nameof(cryptographySuite));
|
||||
this.assemblyInformationProvider = assemblyInformationProvider ?? throw new ArgumentNullException(nameof(assemblyInformationProvider));
|
||||
this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
|
||||
this.gameIOManager = gameIOManager ?? throw new ArgumentNullException(nameof(gameIOManager));
|
||||
this.diagnosticsIOManager = diagnosticsIOManager ?? throw new ArgumentNullException(nameof(diagnosticsIOManager));
|
||||
this.chat = chat ?? throw new ArgumentNullException(nameof(chat));
|
||||
this.networkPromptReaper = networkPromptReaper ?? throw new ArgumentNullException(nameof(networkPromptReaper));
|
||||
this.platformIdentifier = platformIdentifier ?? throw new ArgumentNullException(nameof(platformIdentifier));
|
||||
@@ -215,7 +230,6 @@ namespace Tgstation.Server.Host.Components.Session
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
#pragma warning disable CA1506 // TODO: Decomplexify
|
||||
public async Task<ISessionController> LaunchNew(
|
||||
IDmbProvider dmbProvider,
|
||||
IByondExecutableLock currentByondLock,
|
||||
@@ -248,125 +262,65 @@ namespace Tgstation.Server.Host.Components.Session
|
||||
throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Invalid DreamDaemonSecurity value: {0}", dmbProvider.CompileJob.MinimumSecurityLevel));
|
||||
}
|
||||
|
||||
var chatTrackingContext = chat.CreateTrackingContext();
|
||||
// get the byond lock
|
||||
var byondLock = currentByondLock ?? await byond.UseExecutables(Version.Parse(dmbProvider.CompileJob.ByondVersion), cancellationToken);
|
||||
try
|
||||
{
|
||||
// get the byond lock
|
||||
var byondLock = currentByondLock ?? await byond.UseExecutables(Version.Parse(dmbProvider.CompileJob.ByondVersion), cancellationToken);
|
||||
logger.LogDebug(
|
||||
"Launching session with CompileJob {compileJobId}...",
|
||||
dmbProvider.CompileJob.Id);
|
||||
|
||||
if (launchParameters.SecurityLevel == DreamDaemonSecurity.Trusted)
|
||||
await byondLock.TrustDmbPath(gameIOManager.ConcatPath(dmbProvider.Directory, dmbProvider.DmbName), cancellationToken);
|
||||
|
||||
PortBindTest(launchParameters.Port.Value);
|
||||
await CheckPagerIsNotRunning(cancellationToken);
|
||||
|
||||
string outputFilePath = null;
|
||||
if (launchParameters.LogOutput.Value)
|
||||
{
|
||||
await diagnosticsIOManager.CreateDirectory(DreamDaemonLogsPath, cancellationToken);
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
outputFilePath = diagnosticsIOManager.ResolvePath(
|
||||
diagnosticsIOManager.ConcatPath(
|
||||
DreamDaemonLogsPath,
|
||||
$"dd-utc-{DateTimeOffset.UtcNow.ToString("yyyy-MM-dd-HH-mm-ss", CultureInfo.InvariantCulture)}{(apiValidate ? "-dmapi" : String.Empty)}.log"));
|
||||
|
||||
logger.LogInformation("Logging DreamDaemon output to {path}...", outputFilePath);
|
||||
}
|
||||
else if (!byondLock.SupportsCli)
|
||||
outputFilePath = gameIOManager.ConcatPath(dmbProvider.Directory, $"{Guid.NewGuid()}.dd.log");
|
||||
|
||||
var accessIdentifier = cryptographySuite.GetSecureString();
|
||||
|
||||
var byondTopicSender = topicClientFactory.CreateTopicClient(
|
||||
TimeSpan.FromMilliseconds(
|
||||
launchParameters.TopicRequestTimeout.Value));
|
||||
|
||||
if (!apiValidate && dmbProvider.CompileJob.DMApiVersion == null)
|
||||
logger.LogDebug("Session will have no DMAPI support!");
|
||||
|
||||
// launch dd
|
||||
var process = await CreateDreamDaemonProcess(
|
||||
dmbProvider,
|
||||
byondTopicSender,
|
||||
byondLock,
|
||||
launchParameters,
|
||||
accessIdentifier,
|
||||
outputFilePath,
|
||||
apiValidate,
|
||||
cancellationToken);
|
||||
|
||||
try
|
||||
{
|
||||
logger.LogDebug(
|
||||
"Launching session with CompileJob {0}...",
|
||||
dmbProvider.CompileJob.Id);
|
||||
|
||||
if (launchParameters.SecurityLevel == DreamDaemonSecurity.Trusted)
|
||||
await byondLock.TrustDmbPath(ioManager.ConcatPath(dmbProvider.Directory, dmbProvider.DmbName), cancellationToken);
|
||||
|
||||
PortBindTest(launchParameters.Port.Value);
|
||||
await CheckPagerIsNotRunning(cancellationToken);
|
||||
|
||||
var accessIdentifier = cryptographySuite.GetSecureString();
|
||||
|
||||
var byondTopicSender = topicClientFactory.CreateTopicClient(
|
||||
TimeSpan.FromMilliseconds(
|
||||
launchParameters.TopicRequestTimeout.Value));
|
||||
|
||||
// set command line options
|
||||
// more sanitization here cause it uses the same scheme
|
||||
var parameters = $"{DMApiConstants.ParamApiVersion}={byondTopicSender.SanitizeString(DMApiConstants.InteropVersion.Semver().ToString())}&{byondTopicSender.SanitizeString(DMApiConstants.ParamServerPort)}={serverPortProvider.HttpApiPort}&{byondTopicSender.SanitizeString(DMApiConstants.ParamAccessIdentifier)}={byondTopicSender.SanitizeString(accessIdentifier)}";
|
||||
|
||||
if (!String.IsNullOrEmpty(launchParameters.AdditionalParameters))
|
||||
parameters = $"{parameters}&{launchParameters.AdditionalParameters}";
|
||||
|
||||
// important to run on all ports to allow port changing
|
||||
Guid? logFileGuid = null;
|
||||
var arguments = String.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
"{0} -port {1} -ports 1-65535 {2}-close -verbose -{3} -{4}{5}{6} -params \"{7}\"",
|
||||
dmbProvider.DmbName,
|
||||
launchParameters.Port.Value,
|
||||
launchParameters.AllowWebClient.Value ? "-webclient " : String.Empty,
|
||||
SecurityWord(launchParameters.SecurityLevel.Value),
|
||||
VisibilityWord(launchParameters.Visibility.Value),
|
||||
!byondLock.SupportsCli
|
||||
? $" -logself -log {logFileGuid = Guid.NewGuid()}"
|
||||
: !platformIdentifier.IsWindows // Just use stdout on if CLI is supported
|
||||
? " -logself"
|
||||
: String.Empty, // Windows doesn't output anything to dd.exe if -logself is set?
|
||||
launchParameters.StartProfiler.Value
|
||||
? " -profile"
|
||||
: String.Empty,
|
||||
parameters);
|
||||
|
||||
if (!apiValidate && dmbProvider.CompileJob.DMApiVersion == null)
|
||||
logger.LogDebug("Session will have no DMAPI support!");
|
||||
|
||||
// launch dd
|
||||
var process = processExecutor.LaunchProcess(
|
||||
byondLock.DreamDaemonPath,
|
||||
dmbProvider.Directory,
|
||||
arguments,
|
||||
byondLock.SupportsCli,
|
||||
byondLock.SupportsCli,
|
||||
byondLock.SupportsCli);
|
||||
|
||||
var cliSupported = byondLock.SupportsCli;
|
||||
async Task<string> GetDDOutput()
|
||||
{
|
||||
// DCT x2: None available
|
||||
if (cliSupported)
|
||||
return await process.GetCombinedOutput(default);
|
||||
|
||||
var logFilePath = ioManager.ConcatPath(dmbProvider.Directory, logFileGuid.ToString());
|
||||
try
|
||||
{
|
||||
var dreamDaemonLogBytes = await ioManager.ReadAllBytes(
|
||||
logFilePath,
|
||||
default)
|
||||
;
|
||||
|
||||
return Encoding.UTF8.GetString(dreamDaemonLogBytes);
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
// DCT: No token available
|
||||
await ioManager.DeleteFile(logFilePath, default);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogWarning(ex, "Failed to delete DreamDaemon log file {0}!", logFilePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Log DD output
|
||||
async Task PostLifetime()
|
||||
{
|
||||
try
|
||||
{
|
||||
var ddOutput = await GetDDOutput();
|
||||
logger.LogTrace(
|
||||
"DreamDaemon Output:{0}{1}",
|
||||
Environment.NewLine,
|
||||
ddOutput);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogWarning(ex, "Error reading DreamDaemon output!");
|
||||
}
|
||||
}
|
||||
var chatTrackingContext = chat.CreateTrackingContext();
|
||||
|
||||
try
|
||||
{
|
||||
networkPromptReaper.RegisterProcess(process);
|
||||
|
||||
var runtimeInformation = CreateRuntimeInformation(
|
||||
dmbProvider,
|
||||
chatTrackingContext,
|
||||
launchParameters.SecurityLevel.Value,
|
||||
launchParameters.Visibility.Value,
|
||||
launchParameters,
|
||||
apiValidate);
|
||||
|
||||
var reattachInformation = new ReattachInformation(
|
||||
@@ -387,56 +341,38 @@ namespace Tgstation.Server.Host.Components.Session
|
||||
chat,
|
||||
assemblyInformationProvider,
|
||||
loggerFactory.CreateLogger<SessionController>(),
|
||||
PostLifetime,
|
||||
() => !launchParameters.LogOutput.Value
|
||||
? LogDDOutput(process, outputFilePath, byondLock.SupportsCli, default) // DCT: None available
|
||||
: Task.CompletedTask,
|
||||
launchParameters.StartupTimeout,
|
||||
false,
|
||||
apiValidate);
|
||||
|
||||
if (apiValidate)
|
||||
{
|
||||
if (sessionConfiguration.HighPriorityLiveDreamDaemon)
|
||||
process.AdjustPriority(true);
|
||||
}
|
||||
else if (sessionConfiguration.LowPriorityDeploymentProcesses)
|
||||
process.AdjustPriority(false);
|
||||
|
||||
// If this isnt a staging DD (From a Deployment), fire off an event
|
||||
if (!apiValidate)
|
||||
await eventConsumer.HandleEvent(
|
||||
EventType.DreamDaemonLaunch,
|
||||
new List<string>
|
||||
{
|
||||
process.Id.ToString(CultureInfo.InvariantCulture),
|
||||
},
|
||||
cancellationToken)
|
||||
;
|
||||
|
||||
return sessionController;
|
||||
}
|
||||
catch
|
||||
{
|
||||
using (process)
|
||||
{
|
||||
process.Terminate();
|
||||
await process.Lifetime;
|
||||
throw;
|
||||
}
|
||||
chatTrackingContext.Dispose();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (currentByondLock == null)
|
||||
byondLock.Dispose();
|
||||
throw;
|
||||
await using (process)
|
||||
{
|
||||
process.Terminate();
|
||||
await process.Lifetime;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
chatTrackingContext.Dispose();
|
||||
if (currentByondLock == null)
|
||||
byondLock.Dispose();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
#pragma warning restore CA1506
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<ISessionController> Reattach(
|
||||
@@ -448,30 +384,30 @@ namespace Tgstation.Server.Host.Components.Session
|
||||
|
||||
logger.LogTrace("Begin session reattach...");
|
||||
var byondTopicSender = topicClientFactory.CreateTopicClient(reattachInformation.TopicRequestTimeout);
|
||||
var chatTrackingContext = chat.CreateTrackingContext();
|
||||
var byondLock = await byond.UseExecutables(Version.Parse(reattachInformation.Dmb.CompileJob.ByondVersion), cancellationToken);
|
||||
|
||||
try
|
||||
{
|
||||
var byondLock = await byond.UseExecutables(Version.Parse(reattachInformation.Dmb.CompileJob.ByondVersion), cancellationToken);
|
||||
logger.LogDebug(
|
||||
"Attaching to session PID: {0}, CompileJob: {1}...",
|
||||
reattachInformation.ProcessId,
|
||||
reattachInformation.Dmb.CompileJob.Id);
|
||||
|
||||
var process = processExecutor.GetProcess(reattachInformation.ProcessId);
|
||||
if (process == null)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
logger.LogDebug(
|
||||
"Attaching to session PID: {0}, CompileJob: {1}...",
|
||||
reattachInformation.ProcessId,
|
||||
reattachInformation.Dmb.CompileJob.Id);
|
||||
|
||||
var process = processExecutor.GetProcess(reattachInformation.ProcessId);
|
||||
if (process == null)
|
||||
return null;
|
||||
networkPromptReaper.RegisterProcess(process);
|
||||
|
||||
var chatTrackingContext = chat.CreateTrackingContext();
|
||||
try
|
||||
{
|
||||
networkPromptReaper.RegisterProcess(process);
|
||||
var runtimeInformation = CreateRuntimeInformation(
|
||||
reattachInformation.Dmb,
|
||||
chatTrackingContext,
|
||||
null,
|
||||
null,
|
||||
false);
|
||||
reattachInformation.SetRuntimeInformation(runtimeInformation);
|
||||
|
||||
@@ -497,19 +433,160 @@ namespace Tgstation.Server.Host.Components.Session
|
||||
|
||||
return controller;
|
||||
}
|
||||
finally
|
||||
catch
|
||||
{
|
||||
process?.Dispose();
|
||||
chatTrackingContext.Dispose();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
finally
|
||||
catch
|
||||
{
|
||||
byondLock?.Dispose();
|
||||
await process.DisposeAsync();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
finally
|
||||
catch
|
||||
{
|
||||
chatTrackingContext?.Dispose();
|
||||
byondLock.Dispose();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the DreamDaemon <see cref="IProcess"/>.
|
||||
/// </summary>
|
||||
/// <param name="dmbProvider">The <see cref="IDmbProvider"/>.</param>
|
||||
/// <param name="byondTopicSender">The <see cref="ITopicClient"/> to use for sanitization.</param>
|
||||
/// <param name="byondLock">The <see cref="IByondExecutableLock"/>.</param>
|
||||
/// <param name="launchParameters">The <see cref="DreamDaemonLaunchParameters"/>.</param>
|
||||
/// <param name="accessIdentifier">The secure string to use for the session.</param>
|
||||
/// <param name="logFilePath">The path to log DreamDaemon output to.</param>
|
||||
/// <param name="apiValidate">If we are only validating the DMAPI then exiting.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in the DreamDaemon <see cref="IProcess"/>.</returns>
|
||||
async Task<IProcess> CreateDreamDaemonProcess(
|
||||
IDmbProvider dmbProvider,
|
||||
ITopicClient byondTopicSender,
|
||||
IByondExecutableLock byondLock,
|
||||
DreamDaemonLaunchParameters launchParameters,
|
||||
string accessIdentifier,
|
||||
string logFilePath,
|
||||
bool apiValidate,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
// set command line options
|
||||
// more sanitization here cause it uses the same scheme
|
||||
var parameters = $"{DMApiConstants.ParamApiVersion}={byondTopicSender.SanitizeString(DMApiConstants.InteropVersion.Semver().ToString())}&{byondTopicSender.SanitizeString(DMApiConstants.ParamServerPort)}={serverPortProvider.HttpApiPort}&{byondTopicSender.SanitizeString(DMApiConstants.ParamAccessIdentifier)}={byondTopicSender.SanitizeString(accessIdentifier)}";
|
||||
|
||||
if (!String.IsNullOrEmpty(launchParameters.AdditionalParameters))
|
||||
parameters = $"{parameters}&{launchParameters.AdditionalParameters}";
|
||||
|
||||
// important to run on all ports to allow port changing
|
||||
var arguments = String.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
"{0} -port {1} -ports 1-65535 {2}-close -verbose -{3} -{4}{5}{6} -params \"{7}\"",
|
||||
dmbProvider.DmbName,
|
||||
launchParameters.Port.Value,
|
||||
launchParameters.AllowWebClient.Value ? "-webclient " : String.Empty,
|
||||
SecurityWord(launchParameters.SecurityLevel.Value),
|
||||
VisibilityWord(launchParameters.Visibility.Value),
|
||||
!byondLock.SupportsCli
|
||||
? $" -logself -log {logFilePath}"
|
||||
: !platformIdentifier.IsWindows // Just use stdout on if CLI is supported
|
||||
? " -logself"
|
||||
: String.Empty, // Windows doesn't output anything to dd.exe if -logself is set?
|
||||
launchParameters.StartProfiler.Value
|
||||
? " -profile"
|
||||
: String.Empty,
|
||||
parameters);
|
||||
|
||||
var process = await processExecutor.LaunchProcess(
|
||||
byondLock.DreamDaemonPath,
|
||||
dmbProvider.Directory,
|
||||
arguments,
|
||||
logFilePath,
|
||||
byondLock.SupportsCli,
|
||||
true);
|
||||
|
||||
try
|
||||
{
|
||||
if (apiValidate)
|
||||
{
|
||||
if (sessionConfiguration.HighPriorityLiveDreamDaemon)
|
||||
process.AdjustPriority(true);
|
||||
}
|
||||
else if (sessionConfiguration.LowPriorityDeploymentProcesses)
|
||||
process.AdjustPriority(false);
|
||||
|
||||
networkPromptReaper.RegisterProcess(process);
|
||||
|
||||
// If this isnt a staging DD (From a Deployment), fire off an event
|
||||
if (!apiValidate)
|
||||
await eventConsumer.HandleEvent(
|
||||
EventType.DreamDaemonLaunch,
|
||||
new List<string>
|
||||
{
|
||||
process.Id.ToString(CultureInfo.InvariantCulture),
|
||||
},
|
||||
cancellationToken);
|
||||
|
||||
return process;
|
||||
}
|
||||
catch
|
||||
{
|
||||
await using (process)
|
||||
{
|
||||
process.Terminate();
|
||||
await process.Lifetime;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to log DreamDaemon output.
|
||||
/// </summary>
|
||||
/// <param name="process">The DreamDaemon <see cref="IProcess"/>.</param>
|
||||
/// <param name="outputFilePath">The path to the DreamDaemon log file. Will be deleted.</param>
|
||||
/// <param name="cliSupported">If DreamDaemon was launched with CLI capabilities.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the running operation.</returns>
|
||||
async Task LogDDOutput(IProcess process, string outputFilePath, bool cliSupported, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
string ddOutput;
|
||||
if (cliSupported)
|
||||
ddOutput = await process.GetCombinedOutput(cancellationToken);
|
||||
else
|
||||
try
|
||||
{
|
||||
var dreamDaemonLogBytes = await gameIOManager.ReadAllBytes(
|
||||
outputFilePath,
|
||||
cancellationToken);
|
||||
|
||||
ddOutput = Encoding.UTF8.GetString(dreamDaemonLogBytes);
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
await gameIOManager.DeleteFile(outputFilePath, cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogWarning(ex, "Failed to delete DreamDaemon log file {outputFilePath}!", outputFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
logger.LogTrace(
|
||||
"DreamDaemon Output:{newLine}{output}",
|
||||
Environment.NewLine,
|
||||
ddOutput);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogWarning(ex, "Error reading DreamDaemon output!");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -518,23 +595,21 @@ namespace Tgstation.Server.Host.Components.Session
|
||||
/// </summary>
|
||||
/// <param name="dmbProvider">The <see cref="IDmbProvider"/>.</param>
|
||||
/// <param name="chatTrackingContext">The <see cref="IChatTrackingContext"/>.</param>
|
||||
/// <param name="securityLevel">The <see cref="DreamDaemonSecurity"/> level if any.</param>
|
||||
/// <param name="visibility">The <see cref="DreamDaemonVisibility"/> if any.</param>
|
||||
/// <param name="launchParameters">The <see cref="DreamDaemonLaunchParameters"/> if any.</param>
|
||||
/// <param name="apiValidateOnly">The value of <see cref="RuntimeInformation.ApiValidateOnly"/>.</param>
|
||||
/// <returns>A new <see cref="RuntimeInformation"/> class.</returns>
|
||||
RuntimeInformation CreateRuntimeInformation(
|
||||
IDmbProvider dmbProvider,
|
||||
IChatTrackingContext chatTrackingContext,
|
||||
DreamDaemonSecurity? securityLevel,
|
||||
DreamDaemonVisibility? visibility,
|
||||
DreamDaemonLaunchParameters launchParameters,
|
||||
bool apiValidateOnly)
|
||||
=> new RuntimeInformation(
|
||||
chatTrackingContext,
|
||||
dmbProvider,
|
||||
assemblyInformationProvider.Version,
|
||||
instance.Name,
|
||||
securityLevel,
|
||||
visibility,
|
||||
launchParameters?.SecurityLevel,
|
||||
launchParameters?.Visibility,
|
||||
serverPortProvider.HttpApiPort,
|
||||
apiValidateOnly);
|
||||
|
||||
@@ -548,12 +623,12 @@ namespace Tgstation.Server.Host.Components.Session
|
||||
if (!platformIdentifier.IsWindows)
|
||||
return;
|
||||
|
||||
using var otherProcess = processExecutor.GetProcessByName("byond");
|
||||
await using var otherProcess = processExecutor.GetProcessByName("byond");
|
||||
if (otherProcess == null)
|
||||
return;
|
||||
|
||||
var otherUsernameTask = otherProcess.GetExecutingUsername(cancellationToken);
|
||||
using var ourProcess = processExecutor.GetCurrentProcess();
|
||||
await using var ourProcess = processExecutor.GetCurrentProcess();
|
||||
var ourUserName = await ourProcess.GetExecutingUsername(cancellationToken);
|
||||
var otherUserName = await otherUsernameTask;
|
||||
|
||||
|
||||
@@ -99,7 +99,7 @@ namespace Tgstation.Server.Host.Components.Session
|
||||
{
|
||||
try
|
||||
{
|
||||
using var process = processExecutor.GetProcess(reattachInfo.ProcessId);
|
||||
await using var process = processExecutor.GetProcess(reattachInfo.ProcessId);
|
||||
if (process != null)
|
||||
{
|
||||
if (reattachInfo == result)
|
||||
|
||||
@@ -562,7 +562,7 @@ namespace Tgstation.Server.Host.Components.StaticFiles
|
||||
foreach (var scriptFile in scriptFiles)
|
||||
{
|
||||
logger.LogTrace("Running event script {scriptFile}...", scriptFile);
|
||||
using (var script = processExecutor.LaunchProcess(
|
||||
await using (var script = await processExecutor.LaunchProcess(
|
||||
ioManager.ConcatPath(resolvedScriptsDir, scriptFile),
|
||||
resolvedScriptsDir,
|
||||
String.Join(
|
||||
@@ -576,9 +576,8 @@ namespace Tgstation.Server.Host.Components.StaticFiles
|
||||
|
||||
return $"\"{arg}\"";
|
||||
})),
|
||||
true,
|
||||
true,
|
||||
true))
|
||||
readStandardHandles: true,
|
||||
noShellExecute: true))
|
||||
using (cancellationToken.Register(() => script.Terminate()))
|
||||
{
|
||||
var exitCode = await script.Lifetime;
|
||||
|
||||
@@ -150,7 +150,8 @@ namespace Tgstation.Server.Host.Controllers
|
||||
| DreamDaemonRights.SetTopicTimeout
|
||||
| DreamDaemonRights.SetAdditionalParameters
|
||||
| DreamDaemonRights.SetVisibility
|
||||
| DreamDaemonRights.SetProfiler)]
|
||||
| DreamDaemonRights.SetProfiler
|
||||
| DreamDaemonRights.SetLogOutput)]
|
||||
[ProducesResponseType(typeof(DreamDaemonResponse), 200)]
|
||||
[ProducesResponseType(typeof(ErrorMessageResponse), 410)]
|
||||
#pragma warning disable CA1502 // TODO: Decomplexify
|
||||
@@ -224,7 +225,8 @@ namespace Tgstation.Server.Host.Controllers
|
||||
|| CheckModified(x => x.DumpOnHeartbeatRestart, DreamDaemonRights.CreateDump)
|
||||
|| CheckModified(x => x.TopicRequestTimeout, DreamDaemonRights.SetTopicTimeout)
|
||||
|| CheckModified(x => x.AdditionalParameters, DreamDaemonRights.SetAdditionalParameters)
|
||||
|| CheckModified(x => x.StartProfiler, DreamDaemonRights.SetProfiler))
|
||||
|| CheckModified(x => x.StartProfiler, DreamDaemonRights.SetProfiler)
|
||||
|| CheckModified(x => x.LogOutput, DreamDaemonRights.SetLogOutput))
|
||||
return Forbid();
|
||||
|
||||
await DatabaseContext.Save(cancellationToken);
|
||||
@@ -365,6 +367,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
result.TopicRequestTimeout = settings.TopicRequestTimeout.Value;
|
||||
result.AdditionalParameters = settings.AdditionalParameters;
|
||||
result.StartProfiler = settings.StartProfiler;
|
||||
result.LogOutput = settings.LogOutput;
|
||||
}
|
||||
|
||||
if (revision)
|
||||
|
||||
@@ -725,6 +725,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
TopicRequestTimeout = generalConfiguration.ByondTopicTimeout,
|
||||
AdditionalParameters = String.Empty,
|
||||
StartProfiler = false,
|
||||
LogOutput = false,
|
||||
},
|
||||
DreamMakerSettings = new DreamMakerSettings
|
||||
{
|
||||
|
||||
@@ -379,22 +379,22 @@ namespace Tgstation.Server.Host.Database
|
||||
/// <summary>
|
||||
/// Used by unit tests to remind us to setup the correct MSSQL migration downgrades.
|
||||
/// </summary>
|
||||
internal static readonly Type MSLatestMigration = typeof(MSAddProfiler);
|
||||
internal static readonly Type MSLatestMigration = typeof(MSAddDreamDaemonLogOutput);
|
||||
|
||||
/// <summary>
|
||||
/// Used by unit tests to remind us to setup the correct MYSQL migration downgrades.
|
||||
/// </summary>
|
||||
internal static readonly Type MYLatestMigration = typeof(MYAddProfiler);
|
||||
internal static readonly Type MYLatestMigration = typeof(MYAddDreamDaemonLogOutput);
|
||||
|
||||
/// <summary>
|
||||
/// Used by unit tests to remind us to setup the correct PostgresSQL migration downgrades.
|
||||
/// </summary>
|
||||
internal static readonly Type PGLatestMigration = typeof(PGAddProfiler);
|
||||
internal static readonly Type PGLatestMigration = typeof(PGAddDreamDaemonLogOutput);
|
||||
|
||||
/// <summary>
|
||||
/// Used by unit tests to remind us to setup the correct SQLite migration downgrades.
|
||||
/// </summary>
|
||||
internal static readonly Type SLLatestMigration = typeof(SLAddProfiler);
|
||||
internal static readonly Type SLLatestMigration = typeof(SLAddDreamDaemonLogOutput);
|
||||
|
||||
/// <inheritdoc />
|
||||
#pragma warning disable CA1502 // Cyclomatic complexity
|
||||
@@ -425,6 +425,15 @@ namespace Tgstation.Server.Host.Database
|
||||
|
||||
string BadDatabaseType() => throw new ArgumentException($"Invalid DatabaseType: {currentDatabaseType}", nameof(currentDatabaseType));
|
||||
|
||||
if (targetVersion < new Version(5, 7, 0))
|
||||
targetMigration = currentDatabaseType switch
|
||||
{
|
||||
DatabaseType.MySql => nameof(MYAddProfiler),
|
||||
DatabaseType.PostgresSql => nameof(PGAddProfiler),
|
||||
DatabaseType.SqlServer => nameof(MSAddProfiler),
|
||||
DatabaseType.Sqlite => nameof(SLAddProfiler),
|
||||
_ => BadDatabaseType(),
|
||||
};
|
||||
if (targetVersion < new Version(4, 19, 0))
|
||||
targetMigration = currentDatabaseType switch
|
||||
{
|
||||
|
||||
Generated
+1054
File diff suppressed because it is too large
Load Diff
+39
@@ -0,0 +1,39 @@
|
||||
using System;
|
||||
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds the DreamDaemon LogOutput column for MSSQL.
|
||||
/// </summary>
|
||||
public partial class MSAddDreamDaemonLogOutput : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
if (migrationBuilder == null)
|
||||
throw new ArgumentNullException(nameof(migrationBuilder));
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "LogOutput",
|
||||
table: "DreamDaemonSettings",
|
||||
type: "bit",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
if (migrationBuilder == null)
|
||||
throw new ArgumentNullException(nameof(migrationBuilder));
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "LogOutput",
|
||||
table: "DreamDaemonSettings");
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
+1048
File diff suppressed because it is too large
Load Diff
+39
@@ -0,0 +1,39 @@
|
||||
using System;
|
||||
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds the DreamDaemon LogOutput column for PostgresSQL.
|
||||
/// </summary>
|
||||
public partial class PGAddDreamDaemonLogOutput : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
if (migrationBuilder == null)
|
||||
throw new ArgumentNullException(nameof(migrationBuilder));
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "LogOutput",
|
||||
table: "DreamDaemonSettings",
|
||||
type: "boolean",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
if (migrationBuilder == null)
|
||||
throw new ArgumentNullException(nameof(migrationBuilder));
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "LogOutput",
|
||||
table: "DreamDaemonSettings");
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
+1019
File diff suppressed because it is too large
Load Diff
+39
@@ -0,0 +1,39 @@
|
||||
using System;
|
||||
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds the DreamDaemon LogOutput column for SQLite.
|
||||
/// </summary>
|
||||
public partial class SLAddDreamDaemonLogOutput : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
if (migrationBuilder == null)
|
||||
throw new ArgumentNullException(nameof(migrationBuilder));
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "LogOutput",
|
||||
table: "DreamDaemonSettings",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
if (migrationBuilder == null)
|
||||
throw new ArgumentNullException(nameof(migrationBuilder));
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "LogOutput",
|
||||
table: "DreamDaemonSettings");
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
+1087
File diff suppressed because it is too large
Load Diff
+276
@@ -0,0 +1,276 @@
|
||||
using System;
|
||||
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds the DreamDaemon LogOutput column for MYSQL.
|
||||
/// </summary>
|
||||
/// <remarks>Note the version upgrade of Pomelo caused some freakyness. The down migrations should be harmless here.</remarks>
|
||||
public partial class MYAddDreamDaemonLogOutput : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
if (migrationBuilder == null)
|
||||
throw new ArgumentNullException(nameof(migrationBuilder));
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "LogOutput",
|
||||
table: "DreamDaemonSettings",
|
||||
type: "tinyint(1)",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
if (migrationBuilder == null)
|
||||
throw new ArgumentNullException(nameof(migrationBuilder));
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "LogOutput",
|
||||
table: "DreamDaemonSettings");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "PasswordHash",
|
||||
table: "Users",
|
||||
type: "longtext CHARACTER SET utf8mb4",
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "longtext",
|
||||
oldNullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
.OldAnnotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "Url",
|
||||
table: "TestMerges",
|
||||
type: "longtext CHARACTER SET utf8mb4",
|
||||
nullable: false,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "longtext")
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
.OldAnnotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "TitleAtMerge",
|
||||
table: "TestMerges",
|
||||
type: "longtext CHARACTER SET utf8mb4",
|
||||
nullable: false,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "longtext")
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
.OldAnnotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "Comment",
|
||||
table: "TestMerges",
|
||||
type: "longtext CHARACTER SET utf8mb4",
|
||||
maxLength: 10000,
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "longtext",
|
||||
oldMaxLength: 10000,
|
||||
oldNullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
.OldAnnotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "BodyAtMerge",
|
||||
table: "TestMerges",
|
||||
type: "longtext CHARACTER SET utf8mb4",
|
||||
nullable: false,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "longtext")
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
.OldAnnotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "Author",
|
||||
table: "TestMerges",
|
||||
type: "longtext CHARACTER SET utf8mb4",
|
||||
nullable: false,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "longtext")
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
.OldAnnotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "CommitterName",
|
||||
table: "RepositorySettings",
|
||||
type: "longtext CHARACTER SET utf8mb4",
|
||||
maxLength: 10000,
|
||||
nullable: false,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "longtext",
|
||||
oldMaxLength: 10000)
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
.OldAnnotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "CommitterEmail",
|
||||
table: "RepositorySettings",
|
||||
type: "longtext CHARACTER SET utf8mb4",
|
||||
maxLength: 10000,
|
||||
nullable: false,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "longtext",
|
||||
oldMaxLength: 10000)
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
.OldAnnotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "AccessUser",
|
||||
table: "RepositorySettings",
|
||||
type: "longtext CHARACTER SET utf8mb4",
|
||||
maxLength: 10000,
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "longtext",
|
||||
oldMaxLength: 10000,
|
||||
oldNullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
.OldAnnotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "AccessToken",
|
||||
table: "RepositorySettings",
|
||||
type: "longtext CHARACTER SET utf8mb4",
|
||||
maxLength: 10000,
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "longtext",
|
||||
oldMaxLength: 10000,
|
||||
oldNullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
.OldAnnotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "AccessIdentifier",
|
||||
table: "ReattachInformations",
|
||||
type: "longtext CHARACTER SET utf8mb4",
|
||||
nullable: false,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "longtext")
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
.OldAnnotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "ExceptionDetails",
|
||||
table: "Jobs",
|
||||
type: "longtext CHARACTER SET utf8mb4",
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "longtext",
|
||||
oldNullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
.OldAnnotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "Description",
|
||||
table: "Jobs",
|
||||
type: "longtext CHARACTER SET utf8mb4",
|
||||
nullable: false,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "longtext")
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
.OldAnnotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "ProjectName",
|
||||
table: "DreamMakerSettings",
|
||||
type: "longtext CHARACTER SET utf8mb4",
|
||||
maxLength: 10000,
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "longtext",
|
||||
oldMaxLength: 10000,
|
||||
oldNullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
.OldAnnotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "AdditionalParameters",
|
||||
table: "DreamDaemonSettings",
|
||||
type: "longtext CHARACTER SET utf8mb4",
|
||||
maxLength: 10000,
|
||||
nullable: false,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "longtext",
|
||||
oldMaxLength: 10000)
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
.OldAnnotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "RepositoryOrigin",
|
||||
table: "CompileJobs",
|
||||
type: "longtext CHARACTER SET utf8mb4",
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "longtext",
|
||||
oldNullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
.OldAnnotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "Output",
|
||||
table: "CompileJobs",
|
||||
type: "longtext CHARACTER SET utf8mb4",
|
||||
nullable: false,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "longtext")
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
.OldAnnotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "DmeName",
|
||||
table: "CompileJobs",
|
||||
type: "longtext CHARACTER SET utf8mb4",
|
||||
nullable: false,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "longtext")
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
.OldAnnotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "ByondVersion",
|
||||
table: "CompileJobs",
|
||||
type: "longtext CHARACTER SET utf8mb4",
|
||||
nullable: false,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "longtext")
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
.OldAnnotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "Tag",
|
||||
table: "ChatChannels",
|
||||
type: "longtext CHARACTER SET utf8mb4",
|
||||
maxLength: 10000,
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "longtext",
|
||||
oldMaxLength: 10000,
|
||||
oldNullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
.OldAnnotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "ConnectionString",
|
||||
table: "ChatBots",
|
||||
type: "longtext CHARACTER SET utf8mb4",
|
||||
maxLength: 10000,
|
||||
nullable: false,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "longtext",
|
||||
oldMaxLength: 10000)
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
.OldAnnotation("MySql:CharSet", "utf8mb4");
|
||||
}
|
||||
}
|
||||
}
|
||||
+234
-55
@@ -4,16 +4,19 @@ using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
[DbContext(typeof(MySqlDatabaseContext))]
|
||||
partial class MySqlDatabaseContextModelSnapshot : ModelSnapshot
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "3.1.20")
|
||||
.HasAnnotation("ProductVersion", "6.0.15")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
|
||||
@@ -28,8 +31,10 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
|
||||
b.Property<string>("ConnectionString")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4")
|
||||
.HasMaxLength(10000);
|
||||
.HasMaxLength(10000)
|
||||
.HasColumnType("longtext");
|
||||
|
||||
MySqlPropertyBuilderExtensions.HasCharSet(b.Property<string>("ConnectionString"), "utf8mb4");
|
||||
|
||||
b.Property<bool?>("Enabled")
|
||||
.HasColumnType("tinyint(1)");
|
||||
@@ -39,8 +44,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("varchar(100) CHARACTER SET utf8mb4")
|
||||
.HasMaxLength(100);
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("varchar(100)");
|
||||
|
||||
b.Property<int>("Provider")
|
||||
.HasColumnType("int");
|
||||
@@ -70,8 +75,10 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasColumnType("bigint unsigned");
|
||||
|
||||
b.Property<string>("IrcChannel")
|
||||
.HasColumnType("varchar(100) CHARACTER SET utf8mb4")
|
||||
.HasMaxLength(100);
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("varchar(100)");
|
||||
|
||||
MySqlPropertyBuilderExtensions.HasCharSet(b.Property<string>("IrcChannel"), "utf8mb4");
|
||||
|
||||
b.Property<bool?>("IsAdminChannel")
|
||||
.IsRequired()
|
||||
@@ -86,8 +93,10 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<string>("Tag")
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4")
|
||||
.HasMaxLength(10000);
|
||||
.HasMaxLength(10000)
|
||||
.HasColumnType("longtext");
|
||||
|
||||
MySqlPropertyBuilderExtensions.HasCharSet(b.Property<string>("Tag"), "utf8mb4");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
@@ -108,7 +117,9 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
|
||||
b.Property<string>("ByondVersion")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4");
|
||||
.HasColumnType("longtext");
|
||||
|
||||
MySqlPropertyBuilderExtensions.HasCharSet(b.Property<string>("ByondVersion"), "utf8mb4");
|
||||
|
||||
b.Property<int?>("DMApiMajorVersion")
|
||||
.HasColumnType("int");
|
||||
@@ -125,7 +136,9 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
|
||||
b.Property<string>("DmeName")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4");
|
||||
.HasColumnType("longtext");
|
||||
|
||||
MySqlPropertyBuilderExtensions.HasCharSet(b.Property<string>("DmeName"), "utf8mb4");
|
||||
|
||||
b.Property<int?>("GitHubDeploymentId")
|
||||
.HasColumnType("int");
|
||||
@@ -141,10 +154,14 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
|
||||
b.Property<string>("Output")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4");
|
||||
.HasColumnType("longtext");
|
||||
|
||||
MySqlPropertyBuilderExtensions.HasCharSet(b.Property<string>("Output"), "utf8mb4");
|
||||
|
||||
b.Property<string>("RepositoryOrigin")
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4");
|
||||
.HasColumnType("longtext");
|
||||
|
||||
MySqlPropertyBuilderExtensions.HasCharSet(b.Property<string>("RepositoryOrigin"), "utf8mb4");
|
||||
|
||||
b.Property<long>("RevisionInformationId")
|
||||
.HasColumnType("bigint");
|
||||
@@ -169,8 +186,10 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
|
||||
b.Property<string>("AdditionalParameters")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4")
|
||||
.HasMaxLength(10000);
|
||||
.HasMaxLength(10000)
|
||||
.HasColumnType("longtext");
|
||||
|
||||
MySqlPropertyBuilderExtensions.HasCharSet(b.Property<string>("AdditionalParameters"), "utf8mb4");
|
||||
|
||||
b.Property<bool?>("AllowWebClient")
|
||||
.IsRequired()
|
||||
@@ -191,6 +210,10 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
b.Property<long>("InstanceId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<bool?>("LogOutput")
|
||||
.IsRequired()
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<ushort?>("Port")
|
||||
.IsRequired()
|
||||
.HasColumnType("smallint unsigned");
|
||||
@@ -238,8 +261,10 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("ProjectName")
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4")
|
||||
.HasMaxLength(10000);
|
||||
.HasMaxLength(10000)
|
||||
.HasColumnType("longtext");
|
||||
|
||||
MySqlPropertyBuilderExtensions.HasCharSet(b.Property<string>("ProjectName"), "utf8mb4");
|
||||
|
||||
b.Property<bool?>("RequireDMApiValidation")
|
||||
.IsRequired()
|
||||
@@ -276,8 +301,10 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("varchar(100) CHARACTER SET utf8mb4")
|
||||
.HasMaxLength(100);
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("varchar(100)");
|
||||
|
||||
MySqlPropertyBuilderExtensions.HasCharSet(b.Property<string>("Name"), "utf8mb4");
|
||||
|
||||
b.Property<bool?>("Online")
|
||||
.IsRequired()
|
||||
@@ -285,10 +312,14 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
|
||||
b.Property<string>("Path")
|
||||
.IsRequired()
|
||||
.HasColumnType("varchar(255) CHARACTER SET utf8mb4");
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
MySqlPropertyBuilderExtensions.HasCharSet(b.Property<string>("Path"), "utf8mb4");
|
||||
|
||||
b.Property<string>("SwarmIdentifer")
|
||||
.HasColumnType("varchar(255) CHARACTER SET utf8mb4");
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
MySqlPropertyBuilderExtensions.HasCharSet(b.Property<string>("SwarmIdentifer"), "utf8mb4");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
@@ -362,13 +393,17 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
|
||||
b.Property<string>("Description")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4");
|
||||
.HasColumnType("longtext");
|
||||
|
||||
MySqlPropertyBuilderExtensions.HasCharSet(b.Property<string>("Description"), "utf8mb4");
|
||||
|
||||
b.Property<uint?>("ErrorCode")
|
||||
.HasColumnType("int unsigned");
|
||||
|
||||
b.Property<string>("ExceptionDetails")
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4");
|
||||
.HasColumnType("longtext");
|
||||
|
||||
MySqlPropertyBuilderExtensions.HasCharSet(b.Property<string>("ExceptionDetails"), "utf8mb4");
|
||||
|
||||
b.Property<long>("InstanceId")
|
||||
.HasColumnType("bigint");
|
||||
@@ -402,8 +437,10 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
|
||||
b.Property<string>("ExternalUserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("varchar(100) CHARACTER SET utf8mb4")
|
||||
.HasMaxLength(100);
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("varchar(100)");
|
||||
|
||||
MySqlPropertyBuilderExtensions.HasCharSet(b.Property<string>("ExternalUserId"), "utf8mb4");
|
||||
|
||||
b.Property<int>("Provider")
|
||||
.HasColumnType("int");
|
||||
@@ -458,7 +495,9 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
|
||||
b.Property<string>("AccessIdentifier")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4");
|
||||
.HasColumnType("longtext");
|
||||
|
||||
MySqlPropertyBuilderExtensions.HasCharSet(b.Property<string>("AccessIdentifier"), "utf8mb4");
|
||||
|
||||
b.Property<long>("CompileJobId")
|
||||
.HasColumnType("bigint");
|
||||
@@ -492,12 +531,16 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("AccessToken")
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4")
|
||||
.HasMaxLength(10000);
|
||||
.HasMaxLength(10000)
|
||||
.HasColumnType("longtext");
|
||||
|
||||
MySqlPropertyBuilderExtensions.HasCharSet(b.Property<string>("AccessToken"), "utf8mb4");
|
||||
|
||||
b.Property<string>("AccessUser")
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4")
|
||||
.HasMaxLength(10000);
|
||||
.HasMaxLength(10000)
|
||||
.HasColumnType("longtext");
|
||||
|
||||
MySqlPropertyBuilderExtensions.HasCharSet(b.Property<string>("AccessUser"), "utf8mb4");
|
||||
|
||||
b.Property<bool?>("AutoUpdatesKeepTestMerges")
|
||||
.IsRequired()
|
||||
@@ -509,13 +552,17 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
|
||||
b.Property<string>("CommitterEmail")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4")
|
||||
.HasMaxLength(10000);
|
||||
.HasMaxLength(10000)
|
||||
.HasColumnType("longtext");
|
||||
|
||||
MySqlPropertyBuilderExtensions.HasCharSet(b.Property<string>("CommitterEmail"), "utf8mb4");
|
||||
|
||||
b.Property<string>("CommitterName")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4")
|
||||
.HasMaxLength(10000);
|
||||
.HasMaxLength(10000)
|
||||
.HasColumnType("longtext");
|
||||
|
||||
MySqlPropertyBuilderExtensions.HasCharSet(b.Property<string>("CommitterName"), "utf8mb4");
|
||||
|
||||
b.Property<bool?>("CreateGitHubDeployments")
|
||||
.IsRequired()
|
||||
@@ -577,16 +624,20 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
|
||||
b.Property<string>("CommitSha")
|
||||
.IsRequired()
|
||||
.HasColumnType("varchar(40) CHARACTER SET utf8mb4")
|
||||
.HasMaxLength(40);
|
||||
.HasMaxLength(40)
|
||||
.HasColumnType("varchar(40)");
|
||||
|
||||
MySqlPropertyBuilderExtensions.HasCharSet(b.Property<string>("CommitSha"), "utf8mb4");
|
||||
|
||||
b.Property<long>("InstanceId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("OriginCommitSha")
|
||||
.IsRequired()
|
||||
.HasColumnType("varchar(40) CHARACTER SET utf8mb4")
|
||||
.HasMaxLength(40);
|
||||
.HasMaxLength(40)
|
||||
.HasColumnType("varchar(40)");
|
||||
|
||||
MySqlPropertyBuilderExtensions.HasCharSet(b.Property<string>("OriginCommitSha"), "utf8mb4");
|
||||
|
||||
b.Property<DateTimeOffset>("Timestamp")
|
||||
.HasColumnType("datetime(6)");
|
||||
@@ -607,15 +658,21 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
|
||||
b.Property<string>("Author")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4");
|
||||
.HasColumnType("longtext");
|
||||
|
||||
MySqlPropertyBuilderExtensions.HasCharSet(b.Property<string>("Author"), "utf8mb4");
|
||||
|
||||
b.Property<string>("BodyAtMerge")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4");
|
||||
.HasColumnType("longtext");
|
||||
|
||||
MySqlPropertyBuilderExtensions.HasCharSet(b.Property<string>("BodyAtMerge"), "utf8mb4");
|
||||
|
||||
b.Property<string>("Comment")
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4")
|
||||
.HasMaxLength(10000);
|
||||
.HasMaxLength(10000)
|
||||
.HasColumnType("longtext");
|
||||
|
||||
MySqlPropertyBuilderExtensions.HasCharSet(b.Property<string>("Comment"), "utf8mb4");
|
||||
|
||||
b.Property<DateTimeOffset>("MergedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
@@ -632,16 +689,22 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
|
||||
b.Property<string>("TargetCommitSha")
|
||||
.IsRequired()
|
||||
.HasColumnType("varchar(40) CHARACTER SET utf8mb4")
|
||||
.HasMaxLength(40);
|
||||
.HasMaxLength(40)
|
||||
.HasColumnType("varchar(40)");
|
||||
|
||||
MySqlPropertyBuilderExtensions.HasCharSet(b.Property<string>("TargetCommitSha"), "utf8mb4");
|
||||
|
||||
b.Property<string>("TitleAtMerge")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4");
|
||||
.HasColumnType("longtext");
|
||||
|
||||
MySqlPropertyBuilderExtensions.HasCharSet(b.Property<string>("TitleAtMerge"), "utf8mb4");
|
||||
|
||||
b.Property<string>("Url")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4");
|
||||
.HasColumnType("longtext");
|
||||
|
||||
MySqlPropertyBuilderExtensions.HasCharSet(b.Property<string>("Url"), "utf8mb4");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
@@ -661,8 +724,10 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
|
||||
b.Property<string>("CanonicalName")
|
||||
.IsRequired()
|
||||
.HasColumnType("varchar(100) CHARACTER SET utf8mb4")
|
||||
.HasMaxLength(100);
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("varchar(100)");
|
||||
|
||||
MySqlPropertyBuilderExtensions.HasCharSet(b.Property<string>("CanonicalName"), "utf8mb4");
|
||||
|
||||
b.Property<DateTimeOffset?>("CreatedAt")
|
||||
.IsRequired()
|
||||
@@ -683,15 +748,21 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("varchar(100) CHARACTER SET utf8mb4")
|
||||
.HasMaxLength(100);
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("varchar(100)");
|
||||
|
||||
MySqlPropertyBuilderExtensions.HasCharSet(b.Property<string>("Name"), "utf8mb4");
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4");
|
||||
.HasColumnType("longtext");
|
||||
|
||||
MySqlPropertyBuilderExtensions.HasCharSet(b.Property<string>("PasswordHash"), "utf8mb4");
|
||||
|
||||
b.Property<string>("SystemIdentifier")
|
||||
.HasColumnType("varchar(100) CHARACTER SET utf8mb4")
|
||||
.HasMaxLength(100);
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("varchar(100)");
|
||||
|
||||
MySqlPropertyBuilderExtensions.HasCharSet(b.Property<string>("SystemIdentifier"), "utf8mb4");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
@@ -716,8 +787,10 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("varchar(100) CHARACTER SET utf8mb4")
|
||||
.HasMaxLength(100);
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("varchar(100)");
|
||||
|
||||
MySqlPropertyBuilderExtensions.HasCharSet(b.Property<string>("Name"), "utf8mb4");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
@@ -734,6 +807,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasForeignKey("InstanceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Instance");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
|
||||
@@ -743,6 +818,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasForeignKey("ChatSettingsId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("ChatSettings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
|
||||
@@ -758,6 +835,10 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasForeignKey("RevisionInformationId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Job");
|
||||
|
||||
b.Navigation("RevisionInformation");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
|
||||
@@ -767,6 +848,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasForeignKey("Tgstation.Server.Host.Models.DreamDaemonSettings", "InstanceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Instance");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
|
||||
@@ -776,6 +859,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasForeignKey("Tgstation.Server.Host.Models.DreamMakerSettings", "InstanceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Instance");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.InstancePermissionSet", b =>
|
||||
@@ -791,6 +876,10 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasForeignKey("PermissionSetId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Instance");
|
||||
|
||||
b.Navigation("PermissionSet");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
|
||||
@@ -810,6 +899,12 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasForeignKey("StartedById")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("CancelledBy");
|
||||
|
||||
b.Navigation("Instance");
|
||||
|
||||
b.Navigation("StartedBy");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.OAuthConnection", b =>
|
||||
@@ -818,6 +913,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.WithMany("OAuthConnections")
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.PermissionSet", b =>
|
||||
@@ -831,6 +928,10 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.WithOne("PermissionSet")
|
||||
.HasForeignKey("Tgstation.Server.Host.Models.PermissionSet", "UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.Navigation("Group");
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
|
||||
@@ -840,6 +941,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasForeignKey("CompileJobId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("CompileJob");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
|
||||
@@ -849,6 +952,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasForeignKey("Tgstation.Server.Host.Models.RepositorySettings", "InstanceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Instance");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
|
||||
@@ -864,6 +969,10 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasForeignKey("TestMergeId")
|
||||
.OnDelete(DeleteBehavior.ClientNoAction)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("RevisionInformation");
|
||||
|
||||
b.Navigation("TestMerge");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
|
||||
@@ -873,6 +982,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasForeignKey("InstanceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Instance");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
|
||||
@@ -888,6 +999,10 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasForeignKey("Tgstation.Server.Host.Models.TestMerge", "PrimaryRevisionInformationId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("MergedBy");
|
||||
|
||||
b.Navigation("PrimaryRevisionInformation");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
|
||||
@@ -899,6 +1014,70 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
b.HasOne("Tgstation.Server.Host.Models.UserGroup", "Group")
|
||||
.WithMany("Users")
|
||||
.HasForeignKey("GroupId");
|
||||
|
||||
b.Navigation("CreatedBy");
|
||||
|
||||
b.Navigation("Group");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
|
||||
{
|
||||
b.Navigation("Channels");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b =>
|
||||
{
|
||||
b.Navigation("ChatSettings");
|
||||
|
||||
b.Navigation("DreamDaemonSettings");
|
||||
|
||||
b.Navigation("DreamMakerSettings");
|
||||
|
||||
b.Navigation("InstancePermissionSets");
|
||||
|
||||
b.Navigation("Jobs");
|
||||
|
||||
b.Navigation("RepositorySettings");
|
||||
|
||||
b.Navigation("RevisionInformations");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.PermissionSet", b =>
|
||||
{
|
||||
b.Navigation("InstancePermissionSets");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
|
||||
{
|
||||
b.Navigation("ActiveTestMerges");
|
||||
|
||||
b.Navigation("CompileJobs");
|
||||
|
||||
b.Navigation("PrimaryTestMerge");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
|
||||
{
|
||||
b.Navigation("RevisonInformations");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
|
||||
{
|
||||
b.Navigation("CreatedUsers");
|
||||
|
||||
b.Navigation("OAuthConnections");
|
||||
|
||||
b.Navigation("PermissionSet");
|
||||
|
||||
b.Navigation("TestMerges");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.UserGroup", b =>
|
||||
{
|
||||
b.Navigation("PermissionSet")
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Users");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
|
||||
+207
-77
@@ -3,35 +3,39 @@ using System;
|
||||
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
[DbContext(typeof(PostgresSqlDatabaseContext))]
|
||||
partial class PostgresSqlDatabaseContextModelSnapshot : ModelSnapshot
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn)
|
||||
.HasAnnotation("ProductVersion", "3.1.20")
|
||||
.HasAnnotation("ProductVersion", "6.0.15")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
|
||||
{
|
||||
b.Property<long?>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long?>("Id"));
|
||||
|
||||
b.Property<int>("ChannelLimit")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("ConnectionString")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(10000)")
|
||||
.HasMaxLength(10000);
|
||||
.HasMaxLength(10000)
|
||||
.HasColumnType("character varying(10000)");
|
||||
|
||||
b.Property<bool?>("Enabled")
|
||||
.HasColumnType("boolean");
|
||||
@@ -41,8 +45,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(100)")
|
||||
.HasMaxLength(100);
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("character varying(100)");
|
||||
|
||||
b.Property<int>("Provider")
|
||||
.HasColumnType("integer");
|
||||
@@ -62,8 +66,9 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<long>("ChatSettingsId")
|
||||
.HasColumnType("bigint");
|
||||
@@ -72,8 +77,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasColumnType("numeric(20,0)");
|
||||
|
||||
b.Property<string>("IrcChannel")
|
||||
.HasColumnType("character varying(100)")
|
||||
.HasMaxLength(100);
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("character varying(100)");
|
||||
|
||||
b.Property<bool?>("IsAdminChannel")
|
||||
.IsRequired()
|
||||
@@ -88,8 +93,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<string>("Tag")
|
||||
.HasColumnType("character varying(10000)")
|
||||
.HasMaxLength(10000);
|
||||
.HasMaxLength(10000)
|
||||
.HasColumnType("character varying(10000)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
@@ -106,8 +111,9 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
b.Property<long?>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long?>("Id"));
|
||||
|
||||
b.Property<string>("ByondVersion")
|
||||
.IsRequired()
|
||||
@@ -168,13 +174,14 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<string>("AdditionalParameters")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(10000)")
|
||||
.HasMaxLength(10000);
|
||||
.HasMaxLength(10000)
|
||||
.HasColumnType("character varying(10000)");
|
||||
|
||||
b.Property<bool?>("AllowWebClient")
|
||||
.IsRequired()
|
||||
@@ -194,6 +201,10 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
b.Property<long>("InstanceId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<bool?>("LogOutput")
|
||||
.IsRequired()
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<int>("Port")
|
||||
.HasColumnType("integer");
|
||||
|
||||
@@ -225,8 +236,9 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<int>("ApiValidationPort")
|
||||
.HasColumnType("integer");
|
||||
@@ -238,8 +250,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("ProjectName")
|
||||
.HasColumnType("character varying(10000)")
|
||||
.HasMaxLength(10000);
|
||||
.HasMaxLength(10000)
|
||||
.HasColumnType("character varying(10000)");
|
||||
|
||||
b.Property<bool?>("RequireDMApiValidation")
|
||||
.IsRequired()
|
||||
@@ -261,8 +273,9 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
b.Property<long?>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long?>("Id"));
|
||||
|
||||
b.Property<long>("AutoUpdateInterval")
|
||||
.HasColumnType("bigint");
|
||||
@@ -275,8 +288,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(100)")
|
||||
.HasMaxLength(100);
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("character varying(100)");
|
||||
|
||||
b.Property<bool?>("Online")
|
||||
.IsRequired()
|
||||
@@ -301,8 +314,9 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<decimal>("ByondRights")
|
||||
.HasColumnType("numeric(20,0)");
|
||||
@@ -345,8 +359,9 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
b.Property<long?>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long?>("Id"));
|
||||
|
||||
b.Property<decimal?>("CancelRight")
|
||||
.HasColumnType("numeric(20,0)");
|
||||
@@ -399,13 +414,14 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<string>("ExternalUserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(100)")
|
||||
.HasMaxLength(100);
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("character varying(100)");
|
||||
|
||||
b.Property<int>("Provider")
|
||||
.HasColumnType("integer");
|
||||
@@ -427,8 +443,9 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
b.Property<long?>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long?>("Id"));
|
||||
|
||||
b.Property<decimal>("AdministrationRights")
|
||||
.HasColumnType("numeric(20,0)");
|
||||
@@ -457,8 +474,9 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<string>("AccessIdentifier")
|
||||
.IsRequired()
|
||||
@@ -493,16 +511,17 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<string>("AccessToken")
|
||||
.HasColumnType("character varying(10000)")
|
||||
.HasMaxLength(10000);
|
||||
.HasMaxLength(10000)
|
||||
.HasColumnType("character varying(10000)");
|
||||
|
||||
b.Property<string>("AccessUser")
|
||||
.HasColumnType("character varying(10000)")
|
||||
.HasMaxLength(10000);
|
||||
.HasMaxLength(10000)
|
||||
.HasColumnType("character varying(10000)");
|
||||
|
||||
b.Property<bool?>("AutoUpdatesKeepTestMerges")
|
||||
.IsRequired()
|
||||
@@ -514,13 +533,13 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
|
||||
b.Property<string>("CommitterEmail")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(10000)")
|
||||
.HasMaxLength(10000);
|
||||
.HasMaxLength(10000)
|
||||
.HasColumnType("character varying(10000)");
|
||||
|
||||
b.Property<string>("CommitterName")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(10000)")
|
||||
.HasMaxLength(10000);
|
||||
.HasMaxLength(10000)
|
||||
.HasColumnType("character varying(10000)");
|
||||
|
||||
b.Property<bool?>("CreateGitHubDeployments")
|
||||
.IsRequired()
|
||||
@@ -557,8 +576,9 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<long>("RevisionInformationId")
|
||||
.HasColumnType("bigint");
|
||||
@@ -579,21 +599,22 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<string>("CommitSha")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(40)")
|
||||
.HasMaxLength(40);
|
||||
.HasMaxLength(40)
|
||||
.HasColumnType("character varying(40)");
|
||||
|
||||
b.Property<long>("InstanceId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("OriginCommitSha")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(40)")
|
||||
.HasMaxLength(40);
|
||||
.HasMaxLength(40)
|
||||
.HasColumnType("character varying(40)");
|
||||
|
||||
b.Property<DateTimeOffset>("Timestamp")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
@@ -610,8 +631,9 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<string>("Author")
|
||||
.IsRequired()
|
||||
@@ -622,8 +644,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Comment")
|
||||
.HasColumnType("character varying(10000)")
|
||||
.HasMaxLength(10000);
|
||||
.HasMaxLength(10000)
|
||||
.HasColumnType("character varying(10000)");
|
||||
|
||||
b.Property<DateTimeOffset>("MergedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
@@ -640,8 +662,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
|
||||
b.Property<string>("TargetCommitSha")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(40)")
|
||||
.HasMaxLength(40);
|
||||
.HasMaxLength(40)
|
||||
.HasColumnType("character varying(40)");
|
||||
|
||||
b.Property<string>("TitleAtMerge")
|
||||
.IsRequired()
|
||||
@@ -665,13 +687,14 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
b.Property<long?>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long?>("Id"));
|
||||
|
||||
b.Property<string>("CanonicalName")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(100)")
|
||||
.HasMaxLength(100);
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("character varying(100)");
|
||||
|
||||
b.Property<DateTimeOffset?>("CreatedAt")
|
||||
.IsRequired()
|
||||
@@ -692,15 +715,15 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(100)")
|
||||
.HasMaxLength(100);
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("character varying(100)");
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("SystemIdentifier")
|
||||
.HasColumnType("character varying(100)")
|
||||
.HasMaxLength(100);
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("character varying(100)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
@@ -721,13 +744,14 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
b.Property<long?>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long?>("Id"));
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(100)")
|
||||
.HasMaxLength(100);
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("character varying(100)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
@@ -744,6 +768,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasForeignKey("InstanceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Instance");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
|
||||
@@ -753,6 +779,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasForeignKey("ChatSettingsId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("ChatSettings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
|
||||
@@ -768,6 +796,10 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasForeignKey("RevisionInformationId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Job");
|
||||
|
||||
b.Navigation("RevisionInformation");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
|
||||
@@ -777,6 +809,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasForeignKey("Tgstation.Server.Host.Models.DreamDaemonSettings", "InstanceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Instance");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
|
||||
@@ -786,6 +820,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasForeignKey("Tgstation.Server.Host.Models.DreamMakerSettings", "InstanceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Instance");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.InstancePermissionSet", b =>
|
||||
@@ -801,6 +837,10 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasForeignKey("PermissionSetId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Instance");
|
||||
|
||||
b.Navigation("PermissionSet");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
|
||||
@@ -820,6 +860,12 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasForeignKey("StartedById")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("CancelledBy");
|
||||
|
||||
b.Navigation("Instance");
|
||||
|
||||
b.Navigation("StartedBy");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.OAuthConnection", b =>
|
||||
@@ -828,6 +874,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.WithMany("OAuthConnections")
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.PermissionSet", b =>
|
||||
@@ -841,6 +889,10 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.WithOne("PermissionSet")
|
||||
.HasForeignKey("Tgstation.Server.Host.Models.PermissionSet", "UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.Navigation("Group");
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
|
||||
@@ -850,6 +902,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasForeignKey("CompileJobId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("CompileJob");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
|
||||
@@ -859,6 +913,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasForeignKey("Tgstation.Server.Host.Models.RepositorySettings", "InstanceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Instance");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
|
||||
@@ -874,6 +930,10 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasForeignKey("TestMergeId")
|
||||
.OnDelete(DeleteBehavior.ClientNoAction)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("RevisionInformation");
|
||||
|
||||
b.Navigation("TestMerge");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
|
||||
@@ -883,6 +943,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasForeignKey("InstanceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Instance");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
|
||||
@@ -898,6 +960,10 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasForeignKey("Tgstation.Server.Host.Models.TestMerge", "PrimaryRevisionInformationId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("MergedBy");
|
||||
|
||||
b.Navigation("PrimaryRevisionInformation");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
|
||||
@@ -909,6 +975,70 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
b.HasOne("Tgstation.Server.Host.Models.UserGroup", "Group")
|
||||
.WithMany("Users")
|
||||
.HasForeignKey("GroupId");
|
||||
|
||||
b.Navigation("CreatedBy");
|
||||
|
||||
b.Navigation("Group");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
|
||||
{
|
||||
b.Navigation("Channels");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b =>
|
||||
{
|
||||
b.Navigation("ChatSettings");
|
||||
|
||||
b.Navigation("DreamDaemonSettings");
|
||||
|
||||
b.Navigation("DreamMakerSettings");
|
||||
|
||||
b.Navigation("InstancePermissionSets");
|
||||
|
||||
b.Navigation("Jobs");
|
||||
|
||||
b.Navigation("RepositorySettings");
|
||||
|
||||
b.Navigation("RevisionInformations");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.PermissionSet", b =>
|
||||
{
|
||||
b.Navigation("InstancePermissionSets");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
|
||||
{
|
||||
b.Navigation("ActiveTestMerges");
|
||||
|
||||
b.Navigation("CompileJobs");
|
||||
|
||||
b.Navigation("PrimaryTestMerge");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
|
||||
{
|
||||
b.Navigation("RevisonInformations");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
|
||||
{
|
||||
b.Navigation("CreatedUsers");
|
||||
|
||||
b.Navigation("OAuthConnections");
|
||||
|
||||
b.Navigation("PermissionSet");
|
||||
|
||||
b.Navigation("TestMerges");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.UserGroup", b =>
|
||||
{
|
||||
b.Navigation("PermissionSet")
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Users");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
|
||||
+208
-78
@@ -3,35 +3,39 @@ using System;
|
||||
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
[DbContext(typeof(SqlServerDatabaseContext))]
|
||||
partial class SqlServerDatabaseContextModelSnapshot : ModelSnapshot
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "3.1.20")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128)
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
.HasAnnotation("ProductVersion", "6.0.15")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1);
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
|
||||
{
|
||||
b.Property<long?>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
.HasColumnType("bigint");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long?>("Id"), 1L, 1);
|
||||
|
||||
b.Property<int>("ChannelLimit")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("ConnectionString")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)")
|
||||
.HasMaxLength(10000);
|
||||
.HasMaxLength(10000)
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<bool?>("Enabled")
|
||||
.HasColumnType("bit");
|
||||
@@ -41,8 +45,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(100)")
|
||||
.HasMaxLength(100);
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<int>("Provider")
|
||||
.HasColumnType("int");
|
||||
@@ -62,8 +66,9 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
.HasColumnType("bigint");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"), 1L, 1);
|
||||
|
||||
b.Property<long>("ChatSettingsId")
|
||||
.HasColumnType("bigint");
|
||||
@@ -72,8 +77,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasColumnType("decimal(20,0)");
|
||||
|
||||
b.Property<string>("IrcChannel")
|
||||
.HasColumnType("nvarchar(100)")
|
||||
.HasMaxLength(100);
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<bool?>("IsAdminChannel")
|
||||
.IsRequired()
|
||||
@@ -88,8 +93,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Tag")
|
||||
.HasColumnType("nvarchar(max)")
|
||||
.HasMaxLength(10000);
|
||||
.HasMaxLength(10000)
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
@@ -108,8 +113,9 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
b.Property<long?>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
.HasColumnType("bigint");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long?>("Id"), 1L, 1);
|
||||
|
||||
b.Property<string>("ByondVersion")
|
||||
.IsRequired()
|
||||
@@ -170,13 +176,14 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
.HasColumnType("bigint");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"), 1L, 1);
|
||||
|
||||
b.Property<string>("AdditionalParameters")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)")
|
||||
.HasMaxLength(10000);
|
||||
.HasMaxLength(10000)
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<bool?>("AllowWebClient")
|
||||
.IsRequired()
|
||||
@@ -196,6 +203,10 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
b.Property<long>("InstanceId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<bool?>("LogOutput")
|
||||
.IsRequired()
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<int>("Port")
|
||||
.HasColumnType("int");
|
||||
|
||||
@@ -227,8 +238,9 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
.HasColumnType("bigint");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"), 1L, 1);
|
||||
|
||||
b.Property<int>("ApiValidationPort")
|
||||
.HasColumnType("int");
|
||||
@@ -240,8 +252,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("ProjectName")
|
||||
.HasColumnType("nvarchar(max)")
|
||||
.HasMaxLength(10000);
|
||||
.HasMaxLength(10000)
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<bool?>("RequireDMApiValidation")
|
||||
.IsRequired()
|
||||
@@ -263,8 +275,9 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
b.Property<long?>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
.HasColumnType("bigint");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long?>("Id"), 1L, 1);
|
||||
|
||||
b.Property<long>("AutoUpdateInterval")
|
||||
.HasColumnType("bigint");
|
||||
@@ -277,8 +290,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(100)")
|
||||
.HasMaxLength(100);
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<bool?>("Online")
|
||||
.IsRequired()
|
||||
@@ -304,8 +317,9 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
.HasColumnType("bigint");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"), 1L, 1);
|
||||
|
||||
b.Property<decimal>("ByondRights")
|
||||
.HasColumnType("decimal(20,0)");
|
||||
@@ -348,8 +362,9 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
b.Property<long?>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
.HasColumnType("bigint");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long?>("Id"), 1L, 1);
|
||||
|
||||
b.Property<decimal?>("CancelRight")
|
||||
.HasColumnType("decimal(20,0)");
|
||||
@@ -402,13 +417,14 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
.HasColumnType("bigint");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"), 1L, 1);
|
||||
|
||||
b.Property<string>("ExternalUserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(100)")
|
||||
.HasMaxLength(100);
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<int>("Provider")
|
||||
.HasColumnType("int");
|
||||
@@ -430,8 +446,9 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
b.Property<long?>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
.HasColumnType("bigint");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long?>("Id"), 1L, 1);
|
||||
|
||||
b.Property<decimal>("AdministrationRights")
|
||||
.HasColumnType("decimal(20,0)");
|
||||
@@ -462,8 +479,9 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
.HasColumnType("bigint");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"), 1L, 1);
|
||||
|
||||
b.Property<string>("AccessIdentifier")
|
||||
.IsRequired()
|
||||
@@ -498,16 +516,17 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
.HasColumnType("bigint");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"), 1L, 1);
|
||||
|
||||
b.Property<string>("AccessToken")
|
||||
.HasColumnType("nvarchar(max)")
|
||||
.HasMaxLength(10000);
|
||||
.HasMaxLength(10000)
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("AccessUser")
|
||||
.HasColumnType("nvarchar(max)")
|
||||
.HasMaxLength(10000);
|
||||
.HasMaxLength(10000)
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<bool?>("AutoUpdatesKeepTestMerges")
|
||||
.IsRequired()
|
||||
@@ -519,13 +538,13 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
|
||||
b.Property<string>("CommitterEmail")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)")
|
||||
.HasMaxLength(10000);
|
||||
.HasMaxLength(10000)
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("CommitterName")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)")
|
||||
.HasMaxLength(10000);
|
||||
.HasMaxLength(10000)
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<bool?>("CreateGitHubDeployments")
|
||||
.IsRequired()
|
||||
@@ -562,8 +581,9 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
.HasColumnType("bigint");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"), 1L, 1);
|
||||
|
||||
b.Property<long>("RevisionInformationId")
|
||||
.HasColumnType("bigint");
|
||||
@@ -584,21 +604,22 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
.HasColumnType("bigint");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"), 1L, 1);
|
||||
|
||||
b.Property<string>("CommitSha")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(40)")
|
||||
.HasMaxLength(40);
|
||||
.HasMaxLength(40)
|
||||
.HasColumnType("nvarchar(40)");
|
||||
|
||||
b.Property<long>("InstanceId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("OriginCommitSha")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(40)")
|
||||
.HasMaxLength(40);
|
||||
.HasMaxLength(40)
|
||||
.HasColumnType("nvarchar(40)");
|
||||
|
||||
b.Property<DateTimeOffset>("Timestamp")
|
||||
.HasColumnType("datetimeoffset");
|
||||
@@ -615,8 +636,9 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
.HasColumnType("bigint");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"), 1L, 1);
|
||||
|
||||
b.Property<string>("Author")
|
||||
.IsRequired()
|
||||
@@ -627,8 +649,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Comment")
|
||||
.HasColumnType("nvarchar(max)")
|
||||
.HasMaxLength(10000);
|
||||
.HasMaxLength(10000)
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<DateTimeOffset>("MergedAt")
|
||||
.HasColumnType("datetimeoffset");
|
||||
@@ -645,8 +667,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
|
||||
b.Property<string>("TargetCommitSha")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(40)")
|
||||
.HasMaxLength(40);
|
||||
.HasMaxLength(40)
|
||||
.HasColumnType("nvarchar(40)");
|
||||
|
||||
b.Property<string>("TitleAtMerge")
|
||||
.IsRequired()
|
||||
@@ -670,13 +692,14 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
b.Property<long?>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
.HasColumnType("bigint");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long?>("Id"), 1L, 1);
|
||||
|
||||
b.Property<string>("CanonicalName")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(100)")
|
||||
.HasMaxLength(100);
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<DateTimeOffset?>("CreatedAt")
|
||||
.IsRequired()
|
||||
@@ -697,15 +720,15 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(100)")
|
||||
.HasMaxLength(100);
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("SystemIdentifier")
|
||||
.HasColumnType("nvarchar(100)")
|
||||
.HasMaxLength(100);
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
@@ -727,13 +750,14 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
b.Property<long?>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
.HasColumnType("bigint");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long?>("Id"), 1L, 1);
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(100)")
|
||||
.HasMaxLength(100);
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
@@ -750,6 +774,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasForeignKey("InstanceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Instance");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
|
||||
@@ -759,6 +785,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasForeignKey("ChatSettingsId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("ChatSettings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
|
||||
@@ -774,6 +802,10 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasForeignKey("RevisionInformationId")
|
||||
.OnDelete(DeleteBehavior.ClientNoAction)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Job");
|
||||
|
||||
b.Navigation("RevisionInformation");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
|
||||
@@ -783,6 +815,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasForeignKey("Tgstation.Server.Host.Models.DreamDaemonSettings", "InstanceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Instance");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
|
||||
@@ -792,6 +826,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasForeignKey("Tgstation.Server.Host.Models.DreamMakerSettings", "InstanceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Instance");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.InstancePermissionSet", b =>
|
||||
@@ -807,6 +843,10 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasForeignKey("PermissionSetId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Instance");
|
||||
|
||||
b.Navigation("PermissionSet");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
|
||||
@@ -826,6 +866,12 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasForeignKey("StartedById")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("CancelledBy");
|
||||
|
||||
b.Navigation("Instance");
|
||||
|
||||
b.Navigation("StartedBy");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.OAuthConnection", b =>
|
||||
@@ -834,6 +880,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.WithMany("OAuthConnections")
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.PermissionSet", b =>
|
||||
@@ -847,6 +895,10 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.WithOne("PermissionSet")
|
||||
.HasForeignKey("Tgstation.Server.Host.Models.PermissionSet", "UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.Navigation("Group");
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
|
||||
@@ -856,6 +908,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasForeignKey("CompileJobId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("CompileJob");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
|
||||
@@ -865,6 +919,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasForeignKey("Tgstation.Server.Host.Models.RepositorySettings", "InstanceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Instance");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
|
||||
@@ -880,6 +936,10 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasForeignKey("TestMergeId")
|
||||
.OnDelete(DeleteBehavior.ClientNoAction)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("RevisionInformation");
|
||||
|
||||
b.Navigation("TestMerge");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
|
||||
@@ -889,6 +949,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasForeignKey("InstanceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Instance");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
|
||||
@@ -904,6 +966,10 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasForeignKey("Tgstation.Server.Host.Models.TestMerge", "PrimaryRevisionInformationId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("MergedBy");
|
||||
|
||||
b.Navigation("PrimaryRevisionInformation");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
|
||||
@@ -915,6 +981,70 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
b.HasOne("Tgstation.Server.Host.Models.UserGroup", "Group")
|
||||
.WithMany("Users")
|
||||
.HasForeignKey("GroupId");
|
||||
|
||||
b.Navigation("CreatedBy");
|
||||
|
||||
b.Navigation("Group");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
|
||||
{
|
||||
b.Navigation("Channels");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b =>
|
||||
{
|
||||
b.Navigation("ChatSettings");
|
||||
|
||||
b.Navigation("DreamDaemonSettings");
|
||||
|
||||
b.Navigation("DreamMakerSettings");
|
||||
|
||||
b.Navigation("InstancePermissionSets");
|
||||
|
||||
b.Navigation("Jobs");
|
||||
|
||||
b.Navigation("RepositorySettings");
|
||||
|
||||
b.Navigation("RevisionInformations");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.PermissionSet", b =>
|
||||
{
|
||||
b.Navigation("InstancePermissionSets");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
|
||||
{
|
||||
b.Navigation("ActiveTestMerges");
|
||||
|
||||
b.Navigation("CompileJobs");
|
||||
|
||||
b.Navigation("PrimaryTestMerge");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
|
||||
{
|
||||
b.Navigation("RevisonInformations");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
|
||||
{
|
||||
b.Navigation("CreatedUsers");
|
||||
|
||||
b.Navigation("OAuthConnections");
|
||||
|
||||
b.Navigation("PermissionSet");
|
||||
|
||||
b.Navigation("TestMerges");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.UserGroup", b =>
|
||||
{
|
||||
b.Navigation("PermissionSet")
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Users");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
|
||||
+154
-42
@@ -4,16 +4,18 @@ using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
[DbContext(typeof(SqliteDatabaseContext))]
|
||||
partial class SqliteDatabaseContextModelSnapshot : ModelSnapshot
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "3.1.20");
|
||||
modelBuilder.HasAnnotation("ProductVersion", "6.0.15");
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
|
||||
{
|
||||
@@ -27,8 +29,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
|
||||
b.Property<string>("ConnectionString")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(10000);
|
||||
.HasMaxLength(10000)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool?>("Enabled")
|
||||
.HasColumnType("INTEGER");
|
||||
@@ -38,8 +40,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(100);
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Provider")
|
||||
.HasColumnType("INTEGER");
|
||||
@@ -69,8 +71,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("IrcChannel")
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(100);
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool?>("IsAdminChannel")
|
||||
.IsRequired()
|
||||
@@ -85,8 +87,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Tag")
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(10000);
|
||||
.HasMaxLength(10000)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
@@ -168,8 +170,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
|
||||
b.Property<string>("AdditionalParameters")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(10000);
|
||||
.HasMaxLength(10000)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool?>("AllowWebClient")
|
||||
.IsRequired()
|
||||
@@ -190,6 +192,10 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
b.Property<long>("InstanceId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool?>("LogOutput")
|
||||
.IsRequired()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<ushort?>("Port")
|
||||
.IsRequired()
|
||||
.HasColumnType("INTEGER");
|
||||
@@ -237,8 +243,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ProjectName")
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(10000);
|
||||
.HasMaxLength(10000)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool?>("RequireDMApiValidation")
|
||||
.IsRequired()
|
||||
@@ -275,8 +281,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(100);
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool?>("Online")
|
||||
.IsRequired()
|
||||
@@ -401,8 +407,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
|
||||
b.Property<string>("ExternalUserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(100);
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Provider")
|
||||
.HasColumnType("INTEGER");
|
||||
@@ -491,12 +497,12 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("AccessToken")
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(10000);
|
||||
.HasMaxLength(10000)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("AccessUser")
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(10000);
|
||||
.HasMaxLength(10000)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool?>("AutoUpdatesKeepTestMerges")
|
||||
.IsRequired()
|
||||
@@ -508,13 +514,13 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
|
||||
b.Property<string>("CommitterEmail")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(10000);
|
||||
.HasMaxLength(10000)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CommitterName")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(10000);
|
||||
.HasMaxLength(10000)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool?>("CreateGitHubDeployments")
|
||||
.IsRequired()
|
||||
@@ -576,16 +582,16 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
|
||||
b.Property<string>("CommitSha")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(40);
|
||||
.HasMaxLength(40)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<long>("InstanceId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("OriginCommitSha")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(40);
|
||||
.HasMaxLength(40)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset>("Timestamp")
|
||||
.HasColumnType("TEXT");
|
||||
@@ -613,8 +619,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Comment")
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(10000);
|
||||
.HasMaxLength(10000)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset>("MergedAt")
|
||||
.HasColumnType("TEXT");
|
||||
@@ -631,8 +637,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
|
||||
b.Property<string>("TargetCommitSha")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(40);
|
||||
.HasMaxLength(40)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TitleAtMerge")
|
||||
.IsRequired()
|
||||
@@ -660,8 +666,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
|
||||
b.Property<string>("CanonicalName")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(100);
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset?>("CreatedAt")
|
||||
.IsRequired()
|
||||
@@ -682,15 +688,15 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(100);
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("SystemIdentifier")
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(100);
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
@@ -715,8 +721,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(100);
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
@@ -733,6 +739,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasForeignKey("InstanceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Instance");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
|
||||
@@ -742,6 +750,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasForeignKey("ChatSettingsId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("ChatSettings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
|
||||
@@ -757,6 +767,10 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasForeignKey("RevisionInformationId")
|
||||
.OnDelete(DeleteBehavior.ClientNoAction)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Job");
|
||||
|
||||
b.Navigation("RevisionInformation");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
|
||||
@@ -766,6 +780,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasForeignKey("Tgstation.Server.Host.Models.DreamDaemonSettings", "InstanceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Instance");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
|
||||
@@ -775,6 +791,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasForeignKey("Tgstation.Server.Host.Models.DreamMakerSettings", "InstanceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Instance");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.InstancePermissionSet", b =>
|
||||
@@ -790,6 +808,10 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasForeignKey("PermissionSetId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Instance");
|
||||
|
||||
b.Navigation("PermissionSet");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
|
||||
@@ -809,6 +831,12 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasForeignKey("StartedById")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("CancelledBy");
|
||||
|
||||
b.Navigation("Instance");
|
||||
|
||||
b.Navigation("StartedBy");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.OAuthConnection", b =>
|
||||
@@ -817,6 +845,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.WithMany("OAuthConnections")
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.PermissionSet", b =>
|
||||
@@ -830,6 +860,10 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.WithOne("PermissionSet")
|
||||
.HasForeignKey("Tgstation.Server.Host.Models.PermissionSet", "UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.Navigation("Group");
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
|
||||
@@ -839,6 +873,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasForeignKey("CompileJobId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("CompileJob");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
|
||||
@@ -848,6 +884,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasForeignKey("Tgstation.Server.Host.Models.RepositorySettings", "InstanceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Instance");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
|
||||
@@ -863,6 +901,10 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasForeignKey("TestMergeId")
|
||||
.OnDelete(DeleteBehavior.ClientNoAction)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("RevisionInformation");
|
||||
|
||||
b.Navigation("TestMerge");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
|
||||
@@ -872,6 +914,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasForeignKey("InstanceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Instance");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
|
||||
@@ -887,6 +931,10 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.HasForeignKey("Tgstation.Server.Host.Models.TestMerge", "PrimaryRevisionInformationId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("MergedBy");
|
||||
|
||||
b.Navigation("PrimaryRevisionInformation");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
|
||||
@@ -898,6 +946,70 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
b.HasOne("Tgstation.Server.Host.Models.UserGroup", "Group")
|
||||
.WithMany("Users")
|
||||
.HasForeignKey("GroupId");
|
||||
|
||||
b.Navigation("CreatedBy");
|
||||
|
||||
b.Navigation("Group");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
|
||||
{
|
||||
b.Navigation("Channels");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b =>
|
||||
{
|
||||
b.Navigation("ChatSettings");
|
||||
|
||||
b.Navigation("DreamDaemonSettings");
|
||||
|
||||
b.Navigation("DreamMakerSettings");
|
||||
|
||||
b.Navigation("InstancePermissionSets");
|
||||
|
||||
b.Navigation("Jobs");
|
||||
|
||||
b.Navigation("RepositorySettings");
|
||||
|
||||
b.Navigation("RevisionInformations");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.PermissionSet", b =>
|
||||
{
|
||||
b.Navigation("InstancePermissionSets");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
|
||||
{
|
||||
b.Navigation("ActiveTestMerges");
|
||||
|
||||
b.Navigation("CompileJobs");
|
||||
|
||||
b.Navigation("PrimaryTestMerge");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
|
||||
{
|
||||
b.Navigation("RevisonInformations");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
|
||||
{
|
||||
b.Navigation("CreatedUsers");
|
||||
|
||||
b.Navigation("OAuthConnections");
|
||||
|
||||
b.Navigation("PermissionSet");
|
||||
|
||||
b.Navigation("TestMerges");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.UserGroup", b =>
|
||||
{
|
||||
b.Navigation("PermissionSet")
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Users");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ using Microsoft.EntityFrameworkCore;
|
||||
using Pomelo.EntityFrameworkCore.MySql.Infrastructure;
|
||||
|
||||
using Tgstation.Server.Host.Configuration;
|
||||
using Tgstation.Server.Host.Extensions;
|
||||
using Tgstation.Server.Host.Models;
|
||||
|
||||
namespace Tgstation.Server.Host.Database
|
||||
{
|
||||
@@ -60,5 +62,48 @@ namespace Tgstation.Server.Host.Database
|
||||
mySqlOptions.UseQuerySplittingBehavior(QuerySplittingBehavior.SingleQuery);
|
||||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
base.OnModelCreating(modelBuilder);
|
||||
|
||||
// Added to prevent a column type change after upgrading Pomelo.Mysql
|
||||
// Related: https://github.com/PomeloFoundation/Pomelo.EntityFrameworkCore.MySql/issues/1606
|
||||
modelBuilder
|
||||
.MapMySqlTextField<ChatBot>(x => x.ConnectionString)
|
||||
.MapMySqlTextField<ChatChannel>(x => x.Tag)
|
||||
.MapMySqlTextField<ChatChannel>(x => x.IrcChannel)
|
||||
.MapMySqlTextField<CompileJob>(x => x.ByondVersion)
|
||||
.MapMySqlTextField<CompileJob>(x => x.DmeName)
|
||||
.MapMySqlTextField<CompileJob>(x => x.Output)
|
||||
.MapMySqlTextField<CompileJob>(x => x.RepositoryOrigin)
|
||||
.MapMySqlTextField<DreamDaemonSettings>(x => x.AdditionalParameters)
|
||||
.MapMySqlTextField<DreamMakerSettings>(x => x.ProjectName)
|
||||
.MapMySqlTextField<Instance>(x => x.Name)
|
||||
.MapMySqlTextField<Instance>(x => x.Path)
|
||||
.MapMySqlTextField<Instance>(x => x.SwarmIdentifer)
|
||||
.MapMySqlTextField<Job>(x => x.Description)
|
||||
.MapMySqlTextField<Job>(x => x.ExceptionDetails)
|
||||
.MapMySqlTextField<OAuthConnection>(x => x.ExternalUserId)
|
||||
.MapMySqlTextField<ReattachInformation>(x => x.AccessIdentifier)
|
||||
.MapMySqlTextField<RepositorySettings>(x => x.AccessToken)
|
||||
.MapMySqlTextField<RepositorySettings>(x => x.AccessUser)
|
||||
.MapMySqlTextField<RepositorySettings>(x => x.CommitterEmail)
|
||||
.MapMySqlTextField<RepositorySettings>(x => x.CommitterName)
|
||||
.MapMySqlTextField<RevisionInformation>(x => x.CommitSha)
|
||||
.MapMySqlTextField<RevisionInformation>(x => x.OriginCommitSha)
|
||||
.MapMySqlTextField<TestMerge>(x => x.Author)
|
||||
.MapMySqlTextField<TestMerge>(x => x.BodyAtMerge)
|
||||
.MapMySqlTextField<TestMerge>(x => x.Comment)
|
||||
.MapMySqlTextField<TestMerge>(x => x.TargetCommitSha)
|
||||
.MapMySqlTextField<TestMerge>(x => x.TitleAtMerge)
|
||||
.MapMySqlTextField<TestMerge>(x => x.Url)
|
||||
.MapMySqlTextField<User>(x => x.CanonicalName)
|
||||
.MapMySqlTextField<User>(x => x.Name)
|
||||
.MapMySqlTextField<User>(x => x.PasswordHash)
|
||||
.MapMySqlTextField<User>(x => x.SystemIdentifier)
|
||||
.MapMySqlTextField<UserGroup>(x => x.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
using Tgstation.Server.Api.Models;
|
||||
|
||||
namespace Tgstation.Server.Host.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension methods for the <see cref="ModelBuilder"/> <see langword="class"/>.
|
||||
/// </summary>
|
||||
static class ModelBuilderExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Set a given <typeparamref name="TEntity"/>'s property's column charset to "utf8mb4". Only for use with the MySQL/MariaDB provider.
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity">The entity.</typeparam>
|
||||
/// <param name="modelBuilder">The <see cref="ModelBuilder"/>.</param>
|
||||
/// <param name="expression">The <see cref="Expression"/> accessing the relevant property.</param>
|
||||
/// <returns><paramref name="modelBuilder"/>.</returns>
|
||||
public static ModelBuilder MapMySqlTextField<TEntity>(
|
||||
this ModelBuilder modelBuilder,
|
||||
Expression<Func<TEntity, string>> expression)
|
||||
where TEntity : class
|
||||
{
|
||||
var property = modelBuilder
|
||||
.Entity<TEntity>()
|
||||
.Property(expression);
|
||||
property
|
||||
.HasCharSet("utf8mb4");
|
||||
|
||||
var propertyInfo = GetPropertyFromExpression(expression);
|
||||
var stringLengthAttribute = propertyInfo.GetCustomAttribute<StringLengthAttribute>();
|
||||
|
||||
if (stringLengthAttribute?.MaximumLength == Limits.MaximumStringLength)
|
||||
property.HasColumnType("longtext");
|
||||
|
||||
return modelBuilder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the <see cref="PropertyInfo"/> pointed to by an <paramref name="expression"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity">The entity.</typeparam>
|
||||
/// <param name="expression">The <see cref="Expression"/> accessing the relevant property.</param>
|
||||
/// <returns>The <see cref="PropertyInfo"/> pointed to by <paramref name="expression"/>.</returns>
|
||||
static PropertyInfo GetPropertyFromExpression<TEntity>(Expression<Func<TEntity, string>> expression)
|
||||
{
|
||||
MemberExpression memberExpression;
|
||||
|
||||
// this line is necessary, because sometimes the expression comes in as Convert(originalexpression)
|
||||
if (expression.Body is UnaryExpression unaryExpression)
|
||||
if (unaryExpression.Operand is MemberExpression unaryAsMember)
|
||||
memberExpression = unaryAsMember;
|
||||
else
|
||||
throw new ArgumentException("Cannot get property from expression!", nameof(expression));
|
||||
else if (expression.Body is MemberExpression)
|
||||
memberExpression = (MemberExpression)expression.Body;
|
||||
else
|
||||
throw new ArgumentException("Cannot get property from expression!", nameof(expression));
|
||||
|
||||
return (PropertyInfo)memberExpression.Member;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,8 +44,6 @@ namespace Tgstation.Server.Host.IO
|
||||
/// <returns>A <see cref="Task"/> representing the running operation.</returns>
|
||||
static async Task NormalizeAndDelete(DirectoryInfo dir, CancellationToken cancellationToken)
|
||||
{
|
||||
var tasks = new List<Task>();
|
||||
|
||||
// check if we are a symbolic link
|
||||
if (!dir.Attributes.HasFlag(FileAttributes.Directory) || dir.Attributes.HasFlag(FileAttributes.ReparsePoint))
|
||||
{
|
||||
@@ -53,6 +51,9 @@ namespace Tgstation.Server.Host.IO
|
||||
return;
|
||||
}
|
||||
|
||||
await Task.Yield();
|
||||
|
||||
var tasks = new List<Task>();
|
||||
foreach (var subDir in dir.EnumerateDirectories())
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
@@ -102,7 +103,14 @@ namespace Tgstation.Server.Host.IO
|
||||
|
||||
src = ResolvePath(src);
|
||||
dest = ResolvePath(dest);
|
||||
await Task.WhenAll(CopyDirectoryImpl(src, dest, ignore, postCopyCallback, cancellationToken));
|
||||
|
||||
var allTasks = CopyDirectoryImpl(src, dest, ignore, postCopyCallback, cancellationToken);
|
||||
|
||||
// Special tactics, increase the size of the ThreadPool until we have a 10-1 file-thread ratio.
|
||||
var allFileTasks = allTasks.Skip(1);
|
||||
|
||||
var unityTask = Task.WhenAll(allFileTasks);
|
||||
await unityTask.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -115,23 +123,25 @@ namespace Tgstation.Server.Host.IO
|
||||
throw new ArgumentNullException(nameof(src));
|
||||
if (dest == null)
|
||||
throw new ArgumentNullException(nameof(dest));
|
||||
|
||||
// 0 size buffers prevents unnecessary buffering, async mode just uses the copy buffers See https://github.com/dotnet/runtime/blob/ad8031c813bae48d529ed6d265a2441c4b41fe7b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Windows.cs#L163-L169
|
||||
using var srcStream = new FileStream(
|
||||
ResolvePath(src),
|
||||
FileMode.Open,
|
||||
FileAccess.Read,
|
||||
FileShare.Read | FileShare.Delete,
|
||||
DefaultBufferSize,
|
||||
0,
|
||||
FileOptions.Asynchronous | FileOptions.SequentialScan);
|
||||
using var destStream = new FileStream(
|
||||
ResolvePath(dest),
|
||||
FileMode.Create,
|
||||
FileAccess.Write,
|
||||
FileShare.ReadWrite | FileShare.Delete,
|
||||
DefaultBufferSize,
|
||||
FileShare.Read | FileShare.Delete,
|
||||
0,
|
||||
FileOptions.Asynchronous | FileOptions.SequentialScan);
|
||||
|
||||
// value taken from documentation
|
||||
await srcStream.CopyToAsync(destStream, 81920, cancellationToken);
|
||||
await srcStream.CopyToAsync(destStream, DefaultBufferSize, cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -246,16 +256,22 @@ namespace Tgstation.Server.Host.IO
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task WriteAllBytes(string path, byte[] contents, CancellationToken cancellationToken)
|
||||
{
|
||||
using var file = CreateAsyncWriteStream(path);
|
||||
await file.WriteAsync(contents, cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public FileStream CreateAsyncWriteStream(string path)
|
||||
{
|
||||
path = ResolvePath(path);
|
||||
using var file = new FileStream(
|
||||
return new FileStream(
|
||||
path,
|
||||
FileMode.Create,
|
||||
FileAccess.Write,
|
||||
FileShare.ReadWrite,
|
||||
FileShare.Read | FileShare.Delete,
|
||||
DefaultBufferSize,
|
||||
FileOptions.Asynchronous | FileOptions.SequentialScan);
|
||||
await file.WriteAsync(contents, cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -374,7 +390,7 @@ namespace Tgstation.Server.Host.IO
|
||||
/// <param name="ignore">Files and folders to ignore at the root level.</param>
|
||||
/// <param name="postCopyCallback">The optional callback called for each source/dest file pair post copy.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="IEnumerable{T}"/> of <see cref="Task"/>s representing the running operation.</returns>
|
||||
/// <returns>A <see cref="IEnumerable{T}"/> of <see cref="Task"/>s representing the running operations. The first <see cref="Task"/> returned is always the necessary call to <see cref="CreateDirectory(string, CancellationToken)"/>.</returns>
|
||||
IEnumerable<Task> CopyDirectoryImpl(
|
||||
string src,
|
||||
string dest,
|
||||
@@ -383,43 +399,53 @@ namespace Tgstation.Server.Host.IO
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var dir = new DirectoryInfo(src);
|
||||
Task subdirCreationTask = null;
|
||||
foreach (var subDirectory in dir.EnumerateDirectories())
|
||||
{
|
||||
if (ignore != null && ignore.Contains(subDirectory.Name))
|
||||
continue;
|
||||
|
||||
var checkingSubdirCreationTask = true;
|
||||
foreach (var copyTask in CopyDirectoryImpl(subDirectory.FullName, Path.Combine(dest, subDirectory.Name), null, postCopyCallback, cancellationToken))
|
||||
yield return copyTask;
|
||||
{
|
||||
if (subdirCreationTask == null)
|
||||
{
|
||||
subdirCreationTask = copyTask;
|
||||
yield return subdirCreationTask;
|
||||
}
|
||||
else if (!checkingSubdirCreationTask)
|
||||
yield return copyTask;
|
||||
|
||||
checkingSubdirCreationTask = false;
|
||||
}
|
||||
}
|
||||
|
||||
async Task CopyThisDirectory()
|
||||
foreach (var fileInfo in dir.EnumerateFiles())
|
||||
{
|
||||
await CreateDirectory(dest, cancellationToken);
|
||||
|
||||
var fileCopyTasks = new List<Task>();
|
||||
foreach (var fileInfo in dir.EnumerateFiles())
|
||||
if (subdirCreationTask == null)
|
||||
{
|
||||
if (ignore != null && ignore.Contains(fileInfo.Name))
|
||||
return;
|
||||
|
||||
var sourceFile = fileInfo.FullName;
|
||||
var destFile = Path.Combine(dest, fileInfo.Name);
|
||||
|
||||
async Task CopyThisFile()
|
||||
{
|
||||
// Grab all tasks before firing
|
||||
await Task.Yield();
|
||||
await CopyFile(sourceFile, destFile, cancellationToken);
|
||||
if (postCopyCallback != null)
|
||||
await postCopyCallback(sourceFile, destFile);
|
||||
}
|
||||
|
||||
fileCopyTasks.Add(CopyThisFile());
|
||||
subdirCreationTask = CreateDirectory(dest, cancellationToken);
|
||||
yield return subdirCreationTask;
|
||||
}
|
||||
|
||||
await Task.WhenAll(fileCopyTasks);
|
||||
}
|
||||
if (ignore != null && ignore.Contains(fileInfo.Name))
|
||||
continue;
|
||||
|
||||
yield return CopyThisDirectory();
|
||||
var sourceFile = fileInfo.FullName;
|
||||
var destFile = ConcatPath(dest, fileInfo.Name);
|
||||
|
||||
async Task CopyThisFile()
|
||||
{
|
||||
// Grab all tasks before firing
|
||||
await subdirCreationTask;
|
||||
await Task.Yield();
|
||||
await CopyFile(sourceFile, destFile, cancellationToken);
|
||||
if (postCopyCallback != null)
|
||||
await postCopyCallback(sourceFile, destFile);
|
||||
}
|
||||
|
||||
yield return CopyThisFile();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,6 +101,13 @@ namespace Tgstation.Server.Host.IO
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in the files in <paramref name="path"/>.</returns>
|
||||
Task<IReadOnlyList<string>> GetFiles(string path, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="FileStream"/> for writing.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the file to write, will be truncated.</param>
|
||||
/// <returns>The open <see cref="FileStream"/>.</returns>
|
||||
FileStream CreateAsyncWriteStream(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Writes some <paramref name="contents"/> to a file at <paramref name="path"/> overwriting previous content.
|
||||
/// </summary>
|
||||
|
||||
@@ -112,8 +112,7 @@ namespace Tgstation.Server.Host
|
||||
{
|
||||
if (b.FullPath == updatePath && File.Exists(b.FullPath))
|
||||
{
|
||||
if (logger != null)
|
||||
logger.LogInformation("Host watchdog appears to be requesting server termination!");
|
||||
logger?.LogInformation("Host watchdog appears to be requesting server termination!");
|
||||
cancellationTokenSource.Cancel();
|
||||
}
|
||||
};
|
||||
@@ -134,8 +133,7 @@ namespace Tgstation.Server.Host
|
||||
}
|
||||
catch (OperationCanceledException ex)
|
||||
{
|
||||
if (logger != null)
|
||||
logger.LogDebug(ex, "Server run cancelled!");
|
||||
logger?.LogDebug(ex, "Server run cancelled!");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace Tgstation.Server.Host.System
|
||||
/// <summary>
|
||||
/// Abstraction over a <see cref="global::System.Diagnostics.Process"/>.
|
||||
/// </summary>
|
||||
interface IProcess : IProcessBase, IDisposable
|
||||
interface IProcess : IProcessBase, IAsyncDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="IProcess"/>' ID.
|
||||
@@ -19,20 +19,6 @@ namespace Tgstation.Server.Host.System
|
||||
/// </summary>
|
||||
Task Startup { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the stderr output of the <see cref="IProcess"/>.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in the stderr output of the <see cref="IProcess"/>.</returns>
|
||||
Task<string> GetErrorOutput(CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Get the stdout output of the <see cref="IProcess"/>.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in the stdout output of the <see cref="IProcess"/>.</returns>
|
||||
Task<string> GetStandardOutput(CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Get the stderr and stdout output of the <see cref="IProcess"/>.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
namespace Tgstation.Server.Host.System
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Tgstation.Server.Host.System
|
||||
{
|
||||
/// <summary>
|
||||
/// For launching <see cref="IProcess"/>'.
|
||||
@@ -11,16 +13,16 @@
|
||||
/// <param name="fileName">The full path to the executable file.</param>
|
||||
/// <param name="workingDirectory">The working directory for the <see cref="IProcess"/>.</param>
|
||||
/// <param name="arguments">The arguments for the <see cref="IProcess"/>.</param>
|
||||
/// <param name="readOutput">If standard output should be read.</param>
|
||||
/// <param name="readError">If standard error should be read.</param>
|
||||
/// <param name="noShellExecute">If shell execute should not be used. Must be set if <paramref name="readError"/> or <paramref name="readOutput"/> are set.</param>
|
||||
/// <returns>A new <see cref="IProcess"/>.</returns>
|
||||
IProcess LaunchProcess(
|
||||
/// <param name="fileRedirect">File to write process output and error streams to. Requires <paramref name="readStandardHandles"/> to be <see langword="true"/>.</param>
|
||||
/// <param name="readStandardHandles">If the process output and error streams should be read.</param>
|
||||
/// <param name="noShellExecute">If shell execute should not be used. Must be set if <paramref name="readStandardHandles"/> is set.</param>
|
||||
/// <returns>A <see cref="Task"/> resulting in the new <see cref="IProcess"/>.</returns>
|
||||
Task<IProcess> LaunchProcess(
|
||||
string fileName,
|
||||
string workingDirectory,
|
||||
string arguments = null,
|
||||
bool readOutput = false,
|
||||
bool readError = false,
|
||||
string fileRedirect = null,
|
||||
bool readStandardHandles = false,
|
||||
bool noShellExecute = false);
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -90,13 +90,12 @@ namespace Tgstation.Server.Host.System
|
||||
|
||||
string output;
|
||||
int exitCode;
|
||||
using (var gcoreProc = lazyLoadedProcessExecutor.Value.LaunchProcess(
|
||||
await using (var gcoreProc = await lazyLoadedProcessExecutor.Value.LaunchProcess(
|
||||
GCorePath,
|
||||
Environment.CurrentDirectory,
|
||||
$"-o {outputFile} {process.Id}",
|
||||
true,
|
||||
true,
|
||||
true))
|
||||
readStandardHandles: true,
|
||||
noShellExecute: true))
|
||||
{
|
||||
using (cancellationToken.Register(() => gcoreProc.Terminate()))
|
||||
exitCode = await gcoreProc.Lifetime;
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
|
||||
using Tgstation.Server.Host.Extensions;
|
||||
using Tgstation.Server.Host.IO;
|
||||
|
||||
namespace Tgstation.Server.Host.System
|
||||
@@ -39,6 +37,11 @@ namespace Tgstation.Server.Host.System
|
||||
/// </summary>
|
||||
readonly global::System.Diagnostics.Process handle;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="CancellationTokenSource"/> used to shutdown the <see cref="readTask"/>.
|
||||
/// </summary>
|
||||
readonly CancellationTokenSource readerCts;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="global::System.Diagnostics.Process.SafeHandle"/>.
|
||||
/// </summary>
|
||||
@@ -46,38 +49,26 @@ namespace Tgstation.Server.Host.System
|
||||
readonly SafeProcessHandle safeHandle;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="Task{TResult}"/> resulting in the process' standard output text.
|
||||
/// The <see cref="Task{TResult}"/> resulting in the process' standard output/error text.
|
||||
/// </summary>
|
||||
readonly Task<string> standardOutputTask;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="Task{TResult}"/> resulting in the process' standard error text.
|
||||
/// </summary>
|
||||
readonly Task<string> standardErrorTask;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="Task{TResult}"/> resulting in the process' unified standard output and standard error text.
|
||||
/// </summary>
|
||||
readonly StringBuilder combinedStringBuilder;
|
||||
readonly Task<string> readTask;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Process"/> class.
|
||||
/// </summary>
|
||||
/// <param name="processFeatures">The value of <see cref="processFeatures"/>.</param>
|
||||
/// <param name="handle">The value of <see cref="handle"/>.</param>
|
||||
/// <param name="readerCts">The value of <see cref="readerCts"/>.</param>
|
||||
/// <param name="lifetime">The value of <see cref="Lifetime"/>.</param>
|
||||
/// <param name="standardOutputTask">The value of <see cref="standardOutputTask"/>.</param>
|
||||
/// <param name="standardErrorTask">The value of <see cref="standardErrorTask"/>.</param>
|
||||
/// <param name="combinedStringBuilder">The value of <see cref="combinedStringBuilder"/>.</param>
|
||||
/// <param name="readTask">The value of <see cref="readTask"/>.</param>
|
||||
/// <param name="logger">The value of <see cref="logger"/>.</param>
|
||||
/// <param name="preExisting">If <paramref name="handle"/> was NOT just created.</param>
|
||||
public Process(
|
||||
IProcessFeatures processFeatures,
|
||||
global::System.Diagnostics.Process handle,
|
||||
CancellationTokenSource readerCts,
|
||||
Task<int> lifetime,
|
||||
Task<string> standardOutputTask,
|
||||
Task<string> standardErrorTask,
|
||||
StringBuilder combinedStringBuilder,
|
||||
Task<string> readTask,
|
||||
ILogger<Process> logger,
|
||||
bool preExisting)
|
||||
{
|
||||
@@ -87,11 +78,11 @@ namespace Tgstation.Server.Host.System
|
||||
safeHandle = handle.SafeHandle;
|
||||
Id = handle.Id;
|
||||
|
||||
this.readerCts = readerCts;
|
||||
|
||||
this.processFeatures = processFeatures ?? throw new ArgumentNullException(nameof(processFeatures));
|
||||
|
||||
this.standardOutputTask = standardOutputTask;
|
||||
this.standardErrorTask = standardErrorTask;
|
||||
this.combinedStringBuilder = combinedStringBuilder;
|
||||
this.readTask = readTask;
|
||||
|
||||
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
|
||||
@@ -123,42 +114,23 @@ namespace Tgstation.Server.Host.System
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
readerCts?.Cancel();
|
||||
readerCts?.Dispose();
|
||||
if (readTask != null)
|
||||
await readTask;
|
||||
|
||||
safeHandle.Dispose();
|
||||
handle.Dispose();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<string> GetCombinedOutput(CancellationToken cancellationToken)
|
||||
public Task<string> GetCombinedOutput(CancellationToken cancellationToken)
|
||||
{
|
||||
if (combinedStringBuilder == null)
|
||||
if (readTask == null)
|
||||
throw new InvalidOperationException("Output/Error stream reading was not enabled!");
|
||||
await Task.WhenAll(
|
||||
GetStandardOutput(cancellationToken),
|
||||
GetErrorOutput(cancellationToken))
|
||||
;
|
||||
return combinedStringBuilder.ToString().TrimStart(Environment.NewLine.ToCharArray());
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<string> GetErrorOutput(CancellationToken cancellationToken)
|
||||
{
|
||||
if (standardErrorTask == null)
|
||||
throw new InvalidOperationException("Error stream reading was not enabled!");
|
||||
if (!standardErrorTask.IsCompleted)
|
||||
logger.LogTrace("Waiting for PID {0} to close error stream...", Id);
|
||||
return await standardErrorTask.WithToken(cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<string> GetStandardOutput(CancellationToken cancellationToken)
|
||||
{
|
||||
if (standardOutputTask == null)
|
||||
throw new InvalidOperationException("Output stream reading was not enabled!");
|
||||
if (!standardOutputTask.IsCompleted)
|
||||
logger.LogTrace("Waiting for PID {0} to close output stream...", Id);
|
||||
return await standardOutputTask.WithToken(cancellationToken);
|
||||
return readTask;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using Tgstation.Server.Host.Extensions;
|
||||
using Tgstation.Server.Host.IO;
|
||||
|
||||
namespace Tgstation.Server.Host.System
|
||||
{
|
||||
/// <inheritdoc />
|
||||
@@ -15,6 +19,11 @@ namespace Tgstation.Server.Host.System
|
||||
/// </summary>
|
||||
readonly IProcessFeatures processFeatures;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IIOManager"/> for the <see cref="ProcessExecutor"/>.
|
||||
/// </summary>
|
||||
readonly IIOManager ioManager;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ILogger"/> for the <see cref="ProcessExecutor"/>.
|
||||
/// </summary>
|
||||
@@ -25,6 +34,150 @@ namespace Tgstation.Server.Host.System
|
||||
/// </summary>
|
||||
readonly ILoggerFactory loggerFactory;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ProcessExecutor"/> class.
|
||||
/// </summary>
|
||||
/// <param name="processFeatures">The value of <see cref="processFeatures"/>.</param>
|
||||
/// <param name="ioManager">The value of <see cref="ioManager"/>.</param>
|
||||
/// <param name="logger">The value of <see cref="logger"/>.</param>
|
||||
/// <param name="loggerFactory">The value of <see cref="loggerFactory"/>.</param>
|
||||
public ProcessExecutor(
|
||||
IProcessFeatures processFeatures,
|
||||
IIOManager ioManager,
|
||||
ILogger<ProcessExecutor> logger,
|
||||
ILoggerFactory loggerFactory)
|
||||
{
|
||||
this.processFeatures = processFeatures ?? throw new ArgumentNullException(nameof(processFeatures));
|
||||
this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
|
||||
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
this.loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IProcess GetProcess(int id)
|
||||
{
|
||||
logger.LogDebug("Attaching to process {0}...", id);
|
||||
global::System.Diagnostics.Process handle;
|
||||
try
|
||||
{
|
||||
handle = global::System.Diagnostics.Process.GetProcessById(id);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.LogDebug(e, "Unable to get process {0}!", id);
|
||||
return null;
|
||||
}
|
||||
|
||||
return CreateFromExistingHandle(handle);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IProcess GetCurrentProcess()
|
||||
{
|
||||
logger.LogTrace("Getting current process...");
|
||||
var handle = global::System.Diagnostics.Process.GetCurrentProcess();
|
||||
return CreateFromExistingHandle(handle);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IProcess> LaunchProcess(
|
||||
string fileName,
|
||||
string workingDirectory,
|
||||
string arguments,
|
||||
string fileRedirect,
|
||||
bool readStandardHandles,
|
||||
bool noShellExecute)
|
||||
{
|
||||
if (fileName == null)
|
||||
throw new ArgumentNullException(nameof(fileName));
|
||||
if (workingDirectory == null)
|
||||
throw new ArgumentNullException(nameof(workingDirectory));
|
||||
if (arguments == null)
|
||||
throw new ArgumentNullException(nameof(arguments));
|
||||
|
||||
if (!noShellExecute && readStandardHandles)
|
||||
throw new InvalidOperationException("Requesting output/error reading requires noShellExecute to be true!");
|
||||
|
||||
logger.LogDebug(
|
||||
"{0}aunching process in {1}: {2} {3}",
|
||||
noShellExecute ? "L" : "Shell l",
|
||||
workingDirectory,
|
||||
fileName,
|
||||
arguments);
|
||||
var handle = new global::System.Diagnostics.Process();
|
||||
try
|
||||
{
|
||||
handle.StartInfo.FileName = fileName;
|
||||
handle.StartInfo.Arguments = arguments;
|
||||
handle.StartInfo.WorkingDirectory = workingDirectory;
|
||||
|
||||
handle.StartInfo.UseShellExecute = !noShellExecute;
|
||||
|
||||
var processStartTcs = new TaskCompletionSource();
|
||||
var lifetimeTaskTask = AttachExitHandlerBeforeLaunch(handle, processStartTcs.Task);
|
||||
|
||||
Task<string> readTask = null;
|
||||
CancellationTokenSource disposeCts = null;
|
||||
if (readStandardHandles)
|
||||
{
|
||||
handle.StartInfo.RedirectStandardOutput = true;
|
||||
handle.StartInfo.RedirectStandardError = true;
|
||||
|
||||
disposeCts = new CancellationTokenSource();
|
||||
readTask = ConsumeReaders(handle, processStartTcs.Task, fileRedirect, disposeCts.Token);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
handle.Start();
|
||||
|
||||
processStartTcs.SetResult();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
processStartTcs.SetException(ex);
|
||||
throw;
|
||||
}
|
||||
|
||||
var process = new Process(
|
||||
processFeatures,
|
||||
handle,
|
||||
disposeCts,
|
||||
await lifetimeTaskTask, // won't block
|
||||
readTask,
|
||||
loggerFactory.CreateLogger<Process>(),
|
||||
false);
|
||||
|
||||
return process;
|
||||
}
|
||||
catch
|
||||
{
|
||||
handle.Dispose();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IProcess GetProcessByName(string name)
|
||||
{
|
||||
logger.LogTrace("GetProcessByName: {processName}...", name ?? throw new ArgumentNullException(nameof(name)));
|
||||
var procs = global::System.Diagnostics.Process.GetProcessesByName(name);
|
||||
global::System.Diagnostics.Process handle = null;
|
||||
foreach (var proc in procs)
|
||||
if (handle == null)
|
||||
handle = proc;
|
||||
else
|
||||
{
|
||||
logger.LogTrace("Disposing extra found PID: {pid}...", proc.Id);
|
||||
proc.Dispose();
|
||||
}
|
||||
|
||||
if (handle == null)
|
||||
return null;
|
||||
|
||||
return CreateFromExistingHandle(handle);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wrapper for <see cref="AttachExitHandler(global::System.Diagnostics.Process, Func{int})"/> to safely provide the process ID.
|
||||
/// </summary>
|
||||
@@ -92,180 +245,81 @@ namespace Tgstation.Server.Host.System
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ProcessExecutor"/> class.
|
||||
/// Consume the stdout/stderr streams into a <see cref="Task"/>.
|
||||
/// </summary>
|
||||
/// <param name="processFeatures">The value of <see cref="processFeatures"/>.</param>
|
||||
/// <param name="logger">The value of <see cref="logger"/>.</param>
|
||||
/// <param name="loggerFactory">The value of <see cref="loggerFactory"/>.</param>
|
||||
public ProcessExecutor(
|
||||
IProcessFeatures processFeatures,
|
||||
ILogger<ProcessExecutor> logger,
|
||||
ILoggerFactory loggerFactory)
|
||||
/// <param name="handle">The <see cref="global::System.Diagnostics.Process"/>.</param>
|
||||
/// <param name="startTask">The <see cref="Task"/> that completes when <paramref name="handle"/> starts.</param>
|
||||
/// <param name="fileRedirect">The optional path to redirect the streams to.</param>
|
||||
/// <param name="disposeToken">The <see cref="CancellationToken"/> that triggers when the <see cref="Process"/> is disposed.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in the program's output/error text if <paramref name="fileRedirect"/> is <see langword="null"/>, <see langword="null"/> otherwise.</returns>
|
||||
async Task<string> ConsumeReaders(global::System.Diagnostics.Process handle, Task startTask, string fileRedirect, CancellationToken disposeToken)
|
||||
{
|
||||
this.processFeatures = processFeatures ?? throw new ArgumentNullException(nameof(processFeatures));
|
||||
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
this.loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory));
|
||||
}
|
||||
await startTask;
|
||||
|
||||
/// <inheritdoc />
|
||||
public IProcess GetProcess(int id)
|
||||
{
|
||||
logger.LogDebug("Attaching to process {0}...", id);
|
||||
global::System.Diagnostics.Process handle;
|
||||
try
|
||||
var pid = handle.Id;
|
||||
logger.LogTrace("Starting read for PID {pid}...", pid);
|
||||
|
||||
var stdOutHandle = handle.StandardOutput;
|
||||
var stdErrHandle = handle.StandardError;
|
||||
Task<string> outputReadTask = null, errorReadTask = null;
|
||||
bool outputOpen = true, errorOpen = true;
|
||||
async Task<string> GetNextLine()
|
||||
{
|
||||
handle = global::System.Diagnostics.Process.GetProcessById(id);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.LogDebug(e, "Unable to get process {0}!", id);
|
||||
return null;
|
||||
}
|
||||
if (outputOpen && outputReadTask == null)
|
||||
outputReadTask = stdOutHandle.ReadLineAsync();
|
||||
|
||||
return CreateFromExistingHandle(handle);
|
||||
}
|
||||
if (errorOpen && errorReadTask == null)
|
||||
errorReadTask = stdErrHandle.ReadLineAsync();
|
||||
|
||||
/// <inheritdoc />
|
||||
public IProcess GetCurrentProcess()
|
||||
{
|
||||
logger.LogTrace("Getting current process...");
|
||||
var handle = global::System.Diagnostics.Process.GetCurrentProcess();
|
||||
return CreateFromExistingHandle(handle);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IProcess LaunchProcess(
|
||||
string fileName,
|
||||
string workingDirectory,
|
||||
string arguments,
|
||||
bool readOutput,
|
||||
bool readError,
|
||||
bool noShellExecute)
|
||||
{
|
||||
if (fileName == null)
|
||||
throw new ArgumentNullException(nameof(fileName));
|
||||
if (workingDirectory == null)
|
||||
throw new ArgumentNullException(nameof(workingDirectory));
|
||||
if (arguments == null)
|
||||
throw new ArgumentNullException(nameof(arguments));
|
||||
|
||||
if (!noShellExecute && (readOutput || readError))
|
||||
throw new InvalidOperationException("Requesting output/error reading requires noShellExecute to be true!");
|
||||
|
||||
logger.LogDebug(
|
||||
"{0}aunching process in {1}: {2} {3}",
|
||||
noShellExecute ? "L" : "Shell l",
|
||||
workingDirectory,
|
||||
fileName,
|
||||
arguments);
|
||||
var handle = new global::System.Diagnostics.Process();
|
||||
try
|
||||
{
|
||||
handle.StartInfo.FileName = fileName;
|
||||
handle.StartInfo.Arguments = arguments;
|
||||
handle.StartInfo.WorkingDirectory = workingDirectory;
|
||||
|
||||
handle.StartInfo.UseShellExecute = !noShellExecute;
|
||||
|
||||
StringBuilder combinedStringBuilder = null;
|
||||
|
||||
Task<string> outputTask = null;
|
||||
Task<string> errorTask = null;
|
||||
var processStartTcs = new TaskCompletionSource<object>();
|
||||
if (readOutput || readError)
|
||||
var completedTask = await Task.WhenAny(outputReadTask ?? errorReadTask, errorReadTask ?? outputReadTask).WithToken(disposeToken);
|
||||
var line = await completedTask;
|
||||
if (completedTask == outputReadTask)
|
||||
{
|
||||
combinedStringBuilder = new StringBuilder();
|
||||
|
||||
async Task<string> ConsumeReader(Func<TextReader> readerFunc, bool isOutputStream)
|
||||
{
|
||||
var stringBuilder = new StringBuilder();
|
||||
string text;
|
||||
|
||||
await processStartTcs.Task;
|
||||
|
||||
var pid = handle.Id;
|
||||
var streamType = isOutputStream ? "out" : "err";
|
||||
logger.LogTrace("Starting std{0} read for PID {1}...", streamType, pid);
|
||||
|
||||
var reader = readerFunc();
|
||||
while ((text = await reader.ReadLineAsync()) != null)
|
||||
{
|
||||
combinedStringBuilder.AppendLine();
|
||||
combinedStringBuilder.Append(text);
|
||||
stringBuilder.AppendLine();
|
||||
stringBuilder.Append(text);
|
||||
}
|
||||
|
||||
logger.LogTrace("Finished std{0} read for PID {1}", streamType, pid);
|
||||
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
|
||||
if (readOutput)
|
||||
{
|
||||
outputTask = ConsumeReader(() => handle.StandardOutput, true);
|
||||
handle.StartInfo.RedirectStandardOutput = true;
|
||||
}
|
||||
|
||||
if (readError)
|
||||
{
|
||||
errorTask = ConsumeReader(() => handle.StandardError, false);
|
||||
handle.StartInfo.RedirectStandardError = true;
|
||||
}
|
||||
outputReadTask = null;
|
||||
if (line == null)
|
||||
outputOpen = false;
|
||||
}
|
||||
|
||||
var lifetimeTaskTask = AttachExitHandlerBeforeLaunch(handle, processStartTcs.Task);
|
||||
|
||||
try
|
||||
{
|
||||
handle.Start();
|
||||
|
||||
processStartTcs.SetResult(null);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
processStartTcs.SetException(ex);
|
||||
throw;
|
||||
}
|
||||
|
||||
var process = new Process(
|
||||
processFeatures,
|
||||
handle,
|
||||
lifetimeTaskTask.GetAwaiter().GetResult(), // won't block
|
||||
outputTask,
|
||||
errorTask,
|
||||
combinedStringBuilder,
|
||||
loggerFactory.CreateLogger<Process>(),
|
||||
false);
|
||||
|
||||
return process;
|
||||
}
|
||||
catch
|
||||
{
|
||||
handle.Dispose();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IProcess GetProcessByName(string name)
|
||||
{
|
||||
logger.LogTrace("GetProcessByName: {processName}...", name ?? throw new ArgumentNullException(nameof(name)));
|
||||
var procs = global::System.Diagnostics.Process.GetProcessesByName(name);
|
||||
global::System.Diagnostics.Process handle = null;
|
||||
foreach (var proc in procs)
|
||||
if (handle == null)
|
||||
handle = proc;
|
||||
else
|
||||
{
|
||||
logger.LogTrace("Disposing extra found PID: {pid}...", proc.Id);
|
||||
proc.Dispose();
|
||||
errorReadTask = null;
|
||||
if (line == null)
|
||||
errorOpen = false;
|
||||
}
|
||||
|
||||
if (handle == null)
|
||||
return null;
|
||||
if (line == null && (errorOpen || outputOpen))
|
||||
return await GetNextLine();
|
||||
|
||||
return CreateFromExistingHandle(handle);
|
||||
return line;
|
||||
}
|
||||
|
||||
using var fileStream = fileRedirect != null ? ioManager.CreateAsyncWriteStream(fileRedirect) : null;
|
||||
using var writer = fileStream != null ? new StreamWriter(fileStream) : null;
|
||||
|
||||
string text;
|
||||
var stringBuilder = fileStream == null ? new StringBuilder() : null;
|
||||
try
|
||||
{
|
||||
while ((text = await GetNextLine()) != null)
|
||||
{
|
||||
if (fileStream != null)
|
||||
{
|
||||
await writer.WriteLineAsync(text);
|
||||
await writer.FlushAsync();
|
||||
}
|
||||
else
|
||||
stringBuilder.AppendLine(text);
|
||||
}
|
||||
|
||||
logger.LogTrace("Finished read for PID {pid}", pid);
|
||||
}
|
||||
catch (OperationCanceledException ex)
|
||||
{
|
||||
logger.LogWarning(ex, "PID {pid} stream reading interrupted!", pid);
|
||||
if (fileStream != null)
|
||||
await writer.WriteLineAsync("-- Process detached, log truncated. This is likely due a to TGS restart --");
|
||||
}
|
||||
|
||||
return stringBuilder?.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -273,7 +327,7 @@ namespace Tgstation.Server.Host.System
|
||||
/// </summary>
|
||||
/// <param name="handle">The <see cref="global::System.Diagnostics.Process"/> to create a <see cref="IProcess"/> from.</param>
|
||||
/// <returns>The <see cref="IProcess"/> based on <paramref name="handle"/>.</returns>
|
||||
private IProcess CreateFromExistingHandle(global::System.Diagnostics.Process handle)
|
||||
IProcess CreateFromExistingHandle(global::System.Diagnostics.Process handle)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -281,10 +335,9 @@ namespace Tgstation.Server.Host.System
|
||||
return new Process(
|
||||
processFeatures,
|
||||
handle,
|
||||
null,
|
||||
AttachExitHandler(handle, () => pid),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
loggerFactory.CreateLogger<Process>(),
|
||||
true);
|
||||
}
|
||||
|
||||
@@ -59,14 +59,15 @@ namespace Tgstation.Server.Host.System.Tests
|
||||
new Lazy<IProcessExecutor>(() => processExecutor),
|
||||
new DefaultIOManager(new AssemblyInformationProvider()),
|
||||
loggerFactory.CreateLogger<PosixProcessFeatures>()),
|
||||
Mock.Of<IIOManager>(),
|
||||
loggerFactory.CreateLogger<ProcessExecutor>(),
|
||||
loggerFactory);
|
||||
using var subProc = processExecutor
|
||||
await using var subProc = await processExecutor
|
||||
.LaunchProcess(
|
||||
"dotnet",
|
||||
pathToSignalTestApp,
|
||||
$"run -c {CurrentConfig} --no-build",
|
||||
true,
|
||||
null,
|
||||
true,
|
||||
true);
|
||||
|
||||
|
||||
@@ -230,11 +230,12 @@ namespace Tgstation.Server.Tests.Instance
|
||||
IProcessExecutor executor = null;
|
||||
executor = new ProcessExecutor(
|
||||
RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
|
||||
? (IProcessFeatures)new WindowsProcessFeatures(Mock.Of<ILogger<WindowsProcessFeatures>>())
|
||||
? new WindowsProcessFeatures(Mock.Of<ILogger<WindowsProcessFeatures>>())
|
||||
: new PosixProcessFeatures(new Lazy<IProcessExecutor>(() => executor), Mock.Of<IIOManager>(), Mock.Of<ILogger<PosixProcessFeatures>>()),
|
||||
Mock.Of<IIOManager>(),
|
||||
Mock.Of<ILogger<ProcessExecutor>>(),
|
||||
LoggerFactory.Create(x => { }));
|
||||
using var ourProcessHandler = executor
|
||||
await using var ourProcessHandler = executor
|
||||
.GetProcess(ddProc.Id);
|
||||
|
||||
// Ensure it's responding to heartbeats
|
||||
|
||||
@@ -32,6 +32,7 @@ using Tgstation.Server.Host.Configuration;
|
||||
using Tgstation.Server.Host.Database;
|
||||
using Tgstation.Server.Host.Database.Migrations;
|
||||
using Tgstation.Server.Host.Extensions;
|
||||
using Tgstation.Server.Host.IO;
|
||||
using Tgstation.Server.Host.Jobs;
|
||||
using Tgstation.Server.Host.System;
|
||||
using Tgstation.Server.Tests.Instance;
|
||||
@@ -627,7 +628,7 @@ namespace Tgstation.Server.Tests
|
||||
{
|
||||
try
|
||||
{
|
||||
Console.WriteLine($"TEST: CreateAdminClient attempt {I}...");
|
||||
System.Console.WriteLine($"TEST: CreateAdminClient attempt {I}...");
|
||||
return await clientFactory.CreateFromLogin(
|
||||
url,
|
||||
DefaultCredentials.AdminUserName,
|
||||
@@ -753,6 +754,7 @@ namespace Tgstation.Server.Tests
|
||||
TopicRequestTimeout = 1000,
|
||||
AdditionalParameters = String.Empty,
|
||||
StartProfiler = false,
|
||||
LogOutput = true,
|
||||
},
|
||||
DreamMakerSettings = new Host.Models.DreamMakerSettings
|
||||
{
|
||||
@@ -856,7 +858,7 @@ namespace Tgstation.Server.Tests
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"[{DateTimeOffset.UtcNow}] TEST ERROR: {ex}");
|
||||
System.Console.WriteLine($"[{DateTimeOffset.UtcNow}] TEST ERROR: {ex}");
|
||||
serverCts.Cancel();
|
||||
throw;
|
||||
}
|
||||
@@ -991,12 +993,12 @@ namespace Tgstation.Server.Tests
|
||||
}
|
||||
catch (ApiException ex)
|
||||
{
|
||||
Console.WriteLine($"[{DateTimeOffset.UtcNow}] TEST ERROR: {ex.ErrorCode}: {ex.Message}\n{ex.AdditionalServerData}");
|
||||
System.Console.WriteLine($"[{DateTimeOffset.UtcNow}] TEST ERROR: {ex.ErrorCode}: {ex.Message}\n{ex.AdditionalServerData}");
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"[{DateTimeOffset.UtcNow}] TEST ERROR: {ex}");
|
||||
System.Console.WriteLine($"[{DateTimeOffset.UtcNow}] TEST ERROR: {ex}");
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
@@ -1048,17 +1050,17 @@ namespace Tgstation.Server.Tests
|
||||
var platformIdentifier = new PlatformIdentifier();
|
||||
var processExecutor = new ProcessExecutor(
|
||||
Mock.Of<IProcessFeatures>(),
|
||||
Mock.Of<IIOManager>(),
|
||||
Mock.Of<ILogger<ProcessExecutor>>(),
|
||||
LoggerFactory.Create(x => { }));
|
||||
|
||||
using var process = processExecutor.LaunchProcess("test." + platformIdentifier.ScriptFileExtension, ".", String.Empty, true, true, true);
|
||||
await using var process = await processExecutor.LaunchProcess("test." + platformIdentifier.ScriptFileExtension, ".", String.Empty, null, true, true);
|
||||
using var cts = new CancellationTokenSource();
|
||||
cts.CancelAfter(3000);
|
||||
var exitCode = await process.Lifetime.WithToken(cts.Token);
|
||||
|
||||
Assert.AreEqual(0, exitCode);
|
||||
Assert.AreEqual(String.Empty, (await process.GetErrorOutput(default)).Trim());
|
||||
Assert.AreEqual("Hello World!", (await process.GetStandardOutput(default)).Trim());
|
||||
Assert.AreEqual("Hello World!", (await process.GetCombinedOutput(default)).Trim());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
|
||||
Reference in New Issue
Block a user