diff --git a/src/DMAPI/tgs/v4/api.dm b/src/DMAPI/tgs/v4/api.dm
index e5e40bb9eb..e8ba604067 100644
--- a/src/DMAPI/tgs/v4/api.dm
+++ b/src/DMAPI/tgs/v4/api.dm
@@ -18,7 +18,7 @@
var/access_token
var/instance_id
var/instance_name
- var/host_port
+ var/host_path
var/port_1
var/port_2
var/cached_json
@@ -50,7 +50,7 @@
access_token = cached_json["access_token"]
instance_id = text2num(cached_json["instance_id"])
- host_port = text2num(cached_json["host_port"])
+ host_path = cached_json["host_path"]
if(cached_json["api_validate_only"])
Export(TGS4_COMM_VALIDATE)
del(world)
@@ -110,7 +110,7 @@
world.OpenPort(new_port)
/datum/tgs_api/v4/proc/Export(command)
- return world.Export("http://127.0.0.1:[host_port]/Interop/[instance_id]?command=[url_encode(command)]&access_token=[access_token]")
+ return world.Export("[host_path]/Interop/[instance_id]?command=[url_encode(command)]&access_token=[access_token]")
/datum/tgs_api/v4/OnReboot()
var/json = Export(TGS4_COMM_SERVER_REBOOT)
diff --git a/src/DMAPI/tgs/v4/commands.dm b/src/DMAPI/tgs/v4/commands.dm
index bf32f360e3..0d4e72f0fc 100644
--- a/src/DMAPI/tgs/v4/commands.dm
+++ b/src/DMAPI/tgs/v4/commands.dm
@@ -15,7 +15,7 @@
results[command_name] = list("help_text" = stc.help_text, "admin_only" = stc.admin_only)
custom_commands[command_name] = stc
- var/commands_file = cached_json["chat_commands_file"]
+ var/commands_file = cached_json["chat_commands_json"]
if(!commands_file)
return
text2file(json_encode(results), commands_file)
diff --git a/src/Tgstation.Server.Api/Models/Instance.cs b/src/Tgstation.Server.Api/Models/Instance.cs
index bde9f13522..22bd31e1be 100644
--- a/src/Tgstation.Server.Api/Models/Instance.cs
+++ b/src/Tgstation.Server.Api/Models/Instance.cs
@@ -40,5 +40,15 @@ namespace Tgstation.Server.Api.Models
///
[Permissions(WriteRight = InstanceManagerRights.SetConfiguration)]
public bool ConfigurationAllowed { get; set; }
+
+ ///
+ public Instance CloneMetadata() => new Instance
+ {
+ Id = Id,
+ Name = Name,
+ Path = Path,
+ Online = Online,
+ ConfigurationAllowed = ConfigurationAllowed
+ };
}
}
diff --git a/src/Tgstation.Server.Api/Models/Internal/RevisionInformation.cs b/src/Tgstation.Server.Api/Models/Internal/RevisionInformation.cs
index 44eb907a89..a61788167f 100644
--- a/src/Tgstation.Server.Api/Models/Internal/RevisionInformation.cs
+++ b/src/Tgstation.Server.Api/Models/Internal/RevisionInformation.cs
@@ -12,7 +12,7 @@ namespace Tgstation.Server.Api.Models.Internal
///
[Permissions(DenyWrite = true)]
[Required, StringLength(40)]
- public string Revision { get; set; }
+ public string Commit { get; set; }
///
/// The sha of the most recent remote commit
diff --git a/src/Tgstation.Server.Api/Models/Internal/TestMerge.cs b/src/Tgstation.Server.Api/Models/Internal/TestMerge.cs
index 9e1d04f64d..fd6052390a 100644
--- a/src/Tgstation.Server.Api/Models/Internal/TestMerge.cs
+++ b/src/Tgstation.Server.Api/Models/Internal/TestMerge.cs
@@ -31,6 +31,12 @@ namespace Tgstation.Server.Api.Models.Internal
[Required]
public string BodyAtMerge { get; set; }
+ ///
+ /// The URL of the pull request
+ ///
+ [Required]
+ public Uri Url { get; set; }
+
///
/// The author of the pull request
///
diff --git a/src/Tgstation.Server.Host/Components/DreamDaemonExecutor.cs b/src/Tgstation.Server.Host/Components/DreamDaemonExecutor.cs
index f8b74ce401..0f3a47b026 100644
--- a/src/Tgstation.Server.Host/Components/DreamDaemonExecutor.cs
+++ b/src/Tgstation.Server.Host/Components/DreamDaemonExecutor.cs
@@ -1,10 +1,13 @@
-using System;
+using Newtonsoft.Json;
+using System;
using System.Diagnostics;
using System.Globalization;
+using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Tgstation.Server.Api.Models;
using Tgstation.Server.Api.Models.Internal;
+using Tgstation.Server.Host.Components.Models;
using Tgstation.Server.Host.Core;
namespace Tgstation.Server.Host.Components
@@ -53,18 +56,41 @@ namespace Tgstation.Server.Host.Components
}
///
- public async Task RunDreamDaemon(DreamDaemonLaunchParameters launchParameters, TaskCompletionSource
/// The message being sent
- /// The s of the s to send to
+ /// The s of the s to send to
/// The for the operation
/// A representing the running operation
Task SendMessage(string message, IEnumerable channelIds, CancellationToken cancellationToken);
+
+ Task TrackJsons(string basePath, string channelsJsonName, string commandsJsonName, CancellationToken cancellationToken);
}
}
\ No newline at end of file
diff --git a/src/Tgstation.Server.Host/Components/IDmbProvider.cs b/src/Tgstation.Server.Host/Components/IDmbProvider.cs
index b0d190ab34..4825fb2914 100644
--- a/src/Tgstation.Server.Host/Components/IDmbProvider.cs
+++ b/src/Tgstation.Server.Host/Components/IDmbProvider.cs
@@ -1,5 +1,5 @@
using System;
-using System.Threading.Tasks;
+using Tgstation.Server.Host.Models;
namespace Tgstation.Server.Host.Components
{
@@ -22,5 +22,10 @@ namespace Tgstation.Server.Host.Components
/// The secondary game directory with a trailing directory separator
///
string SecondaryDirectory { get; }
+
+ ///
+ /// The of the .dmb
+ ///
+ RevisionInformation RevisionInformation { get; }
}
}
diff --git a/src/Tgstation.Server.Host/Components/IDreamDaemonExecutor.cs b/src/Tgstation.Server.Host/Components/IDreamDaemonExecutor.cs
index c6e444cecd..2f7cbc150d 100644
--- a/src/Tgstation.Server.Host/Components/IDreamDaemonExecutor.cs
+++ b/src/Tgstation.Server.Host/Components/IDreamDaemonExecutor.cs
@@ -1,6 +1,7 @@
using System.Threading;
using System.Threading.Tasks;
using Tgstation.Server.Api.Models.Internal;
+using Tgstation.Server.Host.Components.Models;
namespace Tgstation.Server.Host.Components
{
@@ -15,12 +16,11 @@ namespace Tgstation.Server.Host.Components
/// The
/// The that is completed when dream daemon starts without crashing
/// The path to the dreamdaemon executable
- /// The path to the .dmb to run, the working directory will be derived from this
- /// The access token to be used for communication
- /// If the server should open on or of
+ /// The for the .dmb to run
+ /// The for the run
/// If the resulting process should never be left alive
/// The for the operation
/// A representing the lifetime of the process and resulting in the exit code
- Task RunDreamDaemon(DreamDaemonLaunchParameters launchParameters, TaskCompletionSource onSuccessfulStartup, string dreamDaemonPath, string dmbPath, string accessToken, bool usePrimaryPort, bool alwaysKill, CancellationToken cancellationToken);
+ Task RunDreamDaemon(DreamDaemonLaunchParameters launchParameters, TaskCompletionSource onSuccessfulStartup, string dreamDaemonPath, IDmbProvider dmbProvider, InteropInfo interopInfo, bool alwaysKill, CancellationToken cancellationToken);
}
}
diff --git a/src/Tgstation.Server.Host/Components/IInstance.cs b/src/Tgstation.Server.Host/Components/IInstance.cs
index d28a32d9a6..3735a5cc13 100644
--- a/src/Tgstation.Server.Host/Components/IInstance.cs
+++ b/src/Tgstation.Server.Host/Components/IInstance.cs
@@ -16,6 +16,8 @@ namespace Tgstation.Server.Host.Components
IConfiguration Configuration { get; }
+ Api.Models.Instance GetMetadata();
+
void Rename(string newName);
}
}
\ No newline at end of file
diff --git a/src/Tgstation.Server.Host/Components/IInstanceFactory.cs b/src/Tgstation.Server.Host/Components/IInstanceFactory.cs
index a31d69f2f4..21d905f7a4 100644
--- a/src/Tgstation.Server.Host/Components/IInstanceFactory.cs
+++ b/src/Tgstation.Server.Host/Components/IInstanceFactory.cs
@@ -2,8 +2,16 @@
namespace Tgstation.Server.Host.Components
{
+ ///
+ /// Factory for creating s
+ ///
interface IInstanceFactory
{
- IInstance CreateInstance(Models.Instance metadata);
+ ///
+ /// Create an
+ ///
+ /// The
+ /// A new
+ IInstance CreateInstance(Host.Models.Instance metadata);
}
}
\ No newline at end of file
diff --git a/src/Tgstation.Server.Host/Components/IInstanceManager.cs b/src/Tgstation.Server.Host/Components/IInstanceManager.cs
index 080ab59d06..34c5f6123c 100644
--- a/src/Tgstation.Server.Host/Components/IInstanceManager.cs
+++ b/src/Tgstation.Server.Host/Components/IInstanceManager.cs
@@ -11,33 +11,33 @@ namespace Tgstation.Server.Host.Components
///
/// Get the associated with given
///
- /// The of the desired
+ /// The of the desired
/// The associated with the given
- IInstance GetInstance(Models.Instance metadata);
+ IInstance GetInstance(Host.Models.Instance metadata);
///
/// Online an
///
- /// The of the desired
+ /// The of the desired
/// The for the operation
/// A representing the running operation
- Task OnlineInstance(Models.Instance metadata, CancellationToken cancellationToken);
+ Task OnlineInstance(Host.Models.Instance metadata, CancellationToken cancellationToken);
///
/// Offline an
///
- /// The of the desired
+ /// The of the desired
/// The for the operation
/// A representing the running operation
- Task OfflineInstance(Models.Instance metadata, CancellationToken cancellationToken);
+ Task OfflineInstance(Host.Models.Instance metadata, CancellationToken cancellationToken);
///
/// Move an
///
- /// The of the desired
+ /// The of the desired
/// The new path of the . will have this set on if the operation completes successfully
/// The for the operation
/// A representing the running operation
- Task MoveInstance(Models.Instance metadata, string newPath, CancellationToken cancellationToken);
+ Task MoveInstance(Host.Models.Instance metadata, string newPath, CancellationToken cancellationToken);
}
}
diff --git a/src/Tgstation.Server.Host/Components/Instance.cs b/src/Tgstation.Server.Host/Components/Instance.cs
index e31e5177c6..ce439ee988 100644
--- a/src/Tgstation.Server.Host/Components/Instance.cs
+++ b/src/Tgstation.Server.Host/Components/Instance.cs
@@ -31,6 +31,8 @@ namespace Tgstation.Server.Host.Components
Configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
}
+ public Api.Models.Instance GetMetadata() => metadata.CloneMetadata();
+
public void Rename(string newName)
{
if (String.IsNullOrWhiteSpace(newName))
diff --git a/src/Tgstation.Server.Host/Components/InstanceFactory.cs b/src/Tgstation.Server.Host/Components/InstanceFactory.cs
index d003ad7051..d241d749de 100644
--- a/src/Tgstation.Server.Host/Components/InstanceFactory.cs
+++ b/src/Tgstation.Server.Host/Components/InstanceFactory.cs
@@ -10,7 +10,7 @@ namespace Tgstation.Server.Host.Components
public InstanceFactory(IIOManager ioManager) => this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
///
- public IInstance CreateInstance(Models.Instance metadata)
+ public IInstance CreateInstance(Host.Models.Instance metadata)
{
//Create the ioManager for the instance
diff --git a/src/Tgstation.Server.Host/Components/InstanceManager.cs b/src/Tgstation.Server.Host/Components/InstanceManager.cs
index 1032aff8fa..59f4ee867c 100644
--- a/src/Tgstation.Server.Host/Components/InstanceManager.cs
+++ b/src/Tgstation.Server.Host/Components/InstanceManager.cs
@@ -47,7 +47,7 @@ namespace Tgstation.Server.Host.Components
}
///
- public IInstance GetInstance(Models.Instance metadata)
+ public IInstance GetInstance(Host.Models.Instance metadata)
{
lock (this)
{
@@ -58,7 +58,7 @@ namespace Tgstation.Server.Host.Components
}
///
- public async Task MoveInstance(Models.Instance instance, string newPath, CancellationToken cancellationToken)
+ public async Task MoveInstance(Host.Models.Instance instance, string newPath, CancellationToken cancellationToken)
{
if (newPath == null)
throw new ArgumentNullException(nameof(newPath));
@@ -84,7 +84,7 @@ namespace Tgstation.Server.Host.Components
}
///
- public async Task OfflineInstance(Models.Instance metadata, CancellationToken cancellationToken)
+ public async Task OfflineInstance(Host.Models.Instance metadata, CancellationToken cancellationToken)
{
IInstance instance;
lock (this)
@@ -97,7 +97,7 @@ namespace Tgstation.Server.Host.Components
}
///
- public async Task OnlineInstance(Models.Instance metadata, CancellationToken cancellationToken)
+ public async Task OnlineInstance(Host.Models.Instance metadata, CancellationToken cancellationToken)
{
var instance = instanceFactory.CreateInstance(metadata);
lock (this)
diff --git a/src/Tgstation.Server.Host/Components/Models/InteropInfo.cs b/src/Tgstation.Server.Host/Components/Models/InteropInfo.cs
new file mode 100644
index 0000000000..1376421631
--- /dev/null
+++ b/src/Tgstation.Server.Host/Components/Models/InteropInfo.cs
@@ -0,0 +1,28 @@
+using System.Collections.Generic;
+using Tgstation.Server.Api.Models.Internal;
+
+namespace Tgstation.Server.Host.Components.Models
+{
+ sealed class InteropInfo
+ {
+ public string AccessToken { get; set; }
+
+ public long InstanceId { get; set; }
+
+ public string HostPath { get; set; }
+
+ public bool ApiValidateOnly { get; set; }
+
+ public string InstanceName { get; set; }
+
+ public ushort NextPort { get; set; }
+
+ public string ChatChannelsJson { get; set; }
+
+ public string ChatCommandsJson { get; set; }
+
+ public RevisionInformation Revision { get; set; }
+
+ public List TestMerges { get; } = new List();
+ }
+}
diff --git a/src/Tgstation.Server.Host/Components/Models/TestMerge.cs b/src/Tgstation.Server.Host/Components/Models/TestMerge.cs
new file mode 100644
index 0000000000..1996ad5bc3
--- /dev/null
+++ b/src/Tgstation.Server.Host/Components/Models/TestMerge.cs
@@ -0,0 +1,16 @@
+using Tgstation.Server.Api.Models.Internal;
+
+namespace Tgstation.Server.Host.Components.Models
+{
+ sealed class TestMerge : RevisionInformation
+ {
+ public int Number { get; set; }
+ public string Title { get; set; }
+ public string Body { get; set; }
+ public string Author { get; set; }
+ public string Url { get; set; }
+ public string PullRequestCommit { get; set; }
+ public long TimeMerged { get; set; }
+ public string Comment { get; set; }
+ }
+}
diff --git a/src/Tgstation.Server.Host/Components/Watchdog.cs b/src/Tgstation.Server.Host/Components/Watchdog.cs
index 71688ed6fe..f7eaf3d327 100644
--- a/src/Tgstation.Server.Host/Components/Watchdog.cs
+++ b/src/Tgstation.Server.Host/Components/Watchdog.cs
@@ -2,6 +2,8 @@
using System.Threading;
using System.Threading.Tasks;
using Tgstation.Server.Api.Models.Internal;
+using Tgstation.Server.Host.Components.Models;
+using Tgstation.Server.Host.Core;
using Tgstation.Server.Host.Security;
namespace Tgstation.Server.Host.Components
@@ -14,6 +16,10 @@ namespace Tgstation.Server.Host.Components
///
readonly IByond byond;
///
+ /// The for the
+ ///
+ readonly IChat chat;
+ ///
/// The for the
///
readonly IDreamDaemonExecutor dreamDaemonExecutor;
@@ -33,6 +39,14 @@ namespace Tgstation.Server.Host.Components
/// The for the
///
readonly IEventConsumer eventConsumer;
+ ///
+ /// The for the
+ ///
+ readonly IInstanceManager instanceManager;
+ ///
+ /// The of the
+ ///
+ readonly long instanceId;
///
/// Represents the currently running
@@ -47,19 +61,26 @@ namespace Tgstation.Server.Host.Components
/// Construct a
///
/// The value of
+ /// The value of
/// The value of
/// The value of
/// The value of
/// The value of
/// The value of
- public Watchdog(IByond byond, IDreamDaemonExecutor dreamDaemonExecutor, IInterop interop, IDmbFactory dmbFactory, ICryptographySuite cryptographySuite, IEventConsumer eventConsumer)
+ /// The value of
+ /// The value of
+ public Watchdog(IByond byond, IChat chat, IDreamDaemonExecutor dreamDaemonExecutor, IInterop interop, IDmbFactory dmbFactory, ICryptographySuite cryptographySuite, IEventConsumer eventConsumer, IInstanceManager instanceManager, long instanceId)
{
this.byond = byond ?? throw new ArgumentNullException(nameof(byond));
+ this.chat = chat ?? throw new ArgumentNullException(nameof(chat));
this.dreamDaemonExecutor = dreamDaemonExecutor ?? throw new ArgumentNullException(nameof(dreamDaemonExecutor));
this.interop = interop ?? throw new ArgumentNullException(nameof(interop));
this.dmbFactory = dmbFactory ?? throw new ArgumentNullException(nameof(dmbFactory));
this.cryptographySuite = cryptographySuite ?? throw new ArgumentNullException(nameof(cryptographySuite));
this.eventConsumer = eventConsumer ?? throw new ArgumentNullException(nameof(eventConsumer));
+ this.instanceManager = instanceManager ?? throw new ArgumentNullException(nameof(instanceManager));
+
+ this.instanceId = instanceId;
}
///
@@ -70,19 +91,47 @@ namespace Tgstation.Server.Host.Components
///
/// The for the run
/// The to be completed once the server starts if any
- /// The access token for the server
+ /// The the server
/// The path to the DreamDaemon executable
- /// If a primary server is being launched
/// The for the operation
/// A resulting in the exit code of DreamDaemon
- async Task RunServer(DreamDaemonLaunchParameters launchParameters, TaskCompletionSource onSuccessfulStartup, string accessToken, string dreamDaemonPath, bool isPrimary, CancellationToken cancellationToken)
+ async Task RunServer(DreamDaemonLaunchParameters launchParameters, TaskCompletionSource onSuccessfulStartup, InteropInfo interopInfo, string dreamDaemonPath, CancellationToken cancellationToken)
{
using (var dmb = await dmbFactory.LockNextDmb(cancellationToken).ConfigureAwait(false))
- return await dreamDaemonExecutor.RunDreamDaemon(launchParameters, onSuccessfulStartup, dreamDaemonPath, String.Concat(dmb.PrimaryDirectory, dmb.DmbName), accessToken, isPrimary, false, cancellationToken).ConfigureAwait(false);
+ {
+ var chatJsonGuid = Guid.NewGuid();
+ var isPrimary = interopInfo.NextPort == launchParameters.SecondaryPort;
+
+ //set up chat stuff
+ interopInfo.ChatChannelsJson = String.Concat(chatJsonGuid, ".channels.json");
+ //only track chat commands from primary server
+ if(isPrimary)
+ interopInfo.ChatCommandsJson = String.Concat(chatJsonGuid, ".commands.json");
+
+ //set up revision
+ interopInfo.Revision = dmb.RevisionInformation;
+ foreach (var I in dmb.RevisionInformation.TestMerges)
+ interopInfo.TestMerges.Add(new Models.TestMerge
+ {
+ Author = I.Author,
+ Body = I.BodyAtMerge,
+ Comment = I.Comment,
+ Commit = I.RevisionInformation.Commit,
+ OriginRevision = I.RevisionInformation.OriginRevision,
+ Number = I.Number,
+ PullRequestCommit = I.PullRequestRevision,
+ TimeMerged = I.MergedAt.ToUnixTimeSeconds(),
+ Title = I.TitleAtMerge,
+ Url = I.Url.ToString()
+ });
+
+ using (await chat.TrackJsons(isPrimary ? dmb.PrimaryDirectory : dmb.SecondaryDirectory, interopInfo.ChatChannelsJson, isPrimary ? interopInfo.ChatCommandsJson : null, cancellationToken).ConfigureAwait(false))
+ return await dreamDaemonExecutor.RunDreamDaemon(launchParameters, onSuccessfulStartup, dreamDaemonPath, dmb, interopInfo, false, cancellationToken).ConfigureAwait(false);
+ }
}
///
- /// Locks in a version and runs a server through
+ /// Locks in a version and runs a server through
///
/// The for the run
/// The to be completed once the server starts if any
@@ -97,7 +146,18 @@ namespace Tgstation.Server.Host.Components
try
{
var ddToken = cancellationTokenSource.Token;
- var ddTask = byond.UseExecutable(dreamDaemonPath => RunServer(launchParameters, onSuccessfulStartup, accessToken, dreamDaemonPath, isPrimary, ddToken), false, true);
+
+ var interopInfo = new InteropInfo
+ {
+ AccessToken = accessToken,
+ ApiValidateOnly = false,
+ HostPath = Application.HostingPath,
+ InstanceId = instanceId,
+ NextPort = isPrimary ? launchParameters.SecondaryPort : launchParameters.PrimaryPort,
+ InstanceName = instanceManager.GetInstance(new Host.Models.Instance { Id = instanceId }).GetMetadata().Name,
+ };
+
+ var ddTask = byond.UseExecutable(dreamDaemonPath => RunServer(launchParameters, onSuccessfulStartup, interopInfo, dreamDaemonPath, ddToken), false, true);
interop.SetRun(isPrimary ? launchParameters.PrimaryPort : launchParameters.SecondaryPort, accessToken, isPrimary);
return ddTask;
}
diff --git a/src/Tgstation.Server.Host/Core/Application.cs b/src/Tgstation.Server.Host/Core/Application.cs
index cd65f38a98..095e08b516 100644
--- a/src/Tgstation.Server.Host/Core/Application.cs
+++ b/src/Tgstation.Server.Host/Core/Application.cs
@@ -1,6 +1,7 @@
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
@@ -10,6 +11,7 @@ using Microsoft.IdentityModel.Tokens;
using System;
using System.Globalization;
using System.IdentityModel.Tokens.Jwt;
+using System.Linq;
using System.Reflection;
using Tgstation.Server.Host.Components;
using Tgstation.Server.Host.Configuration;
@@ -24,6 +26,11 @@ namespace Tgstation.Server.Host.Core
///
sealed class Application
{
+ ///
+ /// The url the server can be reached at locally
+ ///
+ public static string HostingPath { get; private set; }
+
///
/// The version of the
///
@@ -151,6 +158,9 @@ namespace Tgstation.Server.Host.Core
if (applicationBuilder == null)
throw new ArgumentNullException(nameof(applicationBuilder));
+ var addresses = applicationBuilder.ServerFeatures.Get();
+ HostingPath = addresses.Addresses.First();
+
if (hostingEnvironment.IsDevelopment())
applicationBuilder.UseDeveloperExceptionPage();
diff --git a/src/Tgstation.Server.Host/Models/DatabaseContext.cs b/src/Tgstation.Server.Host/Models/DatabaseContext.cs
index 4dc62319ba..5f0ffef80f 100644
--- a/src/Tgstation.Server.Host/Models/DatabaseContext.cs
+++ b/src/Tgstation.Server.Host/Models/DatabaseContext.cs
@@ -109,7 +109,7 @@ namespace Tgstation.Server.Host.Models
LogModelBuilderHelper.Build(modelBuilder.Entity());
modelBuilder.Entity().ToTable(nameof(Logs));
- modelBuilder.Entity().HasIndex(x => x.Revision).IsUnique();
+ modelBuilder.Entity().HasIndex(x => x.Commit).IsUnique();
var user = modelBuilder.Entity();
user.HasIndex(x => new { x.Name, x.SystemIdentifier }).IsUnique();
user.HasOne(x => x.CreatedBy).WithMany(x => x.CreatedUsers).OnDelete(DeleteBehavior.Restrict);
diff --git a/src/Tgstation.Server.Host/Models/TestMerge.cs b/src/Tgstation.Server.Host/Models/TestMerge.cs
index e72fc66e3c..05e201c04b 100644
--- a/src/Tgstation.Server.Host/Models/TestMerge.cs
+++ b/src/Tgstation.Server.Host/Models/TestMerge.cs
@@ -10,5 +10,10 @@ namespace Tgstation.Server.Host.Models
///
[Required]
public User MergedBy { get; set; }
+
+ ///
+ /// The for the
+ ///
+ public RevisionInformation RevisionInformation { get; set; }
}
}