Merge pull request #1207 from tgstation/dev [TGSDeploy][APIDeploy][DMDeploy][NugetDeploy]

v4.8.0
This commit is contained in:
Jordan Brown
2021-01-22 15:20:13 -05:00
committed by GitHub
72 changed files with 4243 additions and 207 deletions
+1
View File
@@ -26,6 +26,7 @@ src/DMAPI
src/Tgstation.Server.Host/ClientApp
src/Tgstation.Server.Host/wwwroot
src/Tgstation.Server.Host/appsettings.Development.json
src/Tgstation.Server.Host/appsettings.Development.yml
src/Tgstation.Server.Host/tgs.bat
src/Tgstation.Server.Host/tgs.sh
src/Tgstation.Server.Client
+1 -1
View File
@@ -197,7 +197,7 @@ OAuth providers are hardcoded but it is fairly easy to add new ones. The flow do
1. Create an implementation of [IOAuthValidator](../src/Tgstation.Server.Host/Security/OAuth/IOAuthValidator.cs).
- Most providers can simply override the [GenericOAuthValidator](../src/Tgstation.Server.Host/Security/OAuth/GenericOAuthValidator.cs).
1. Construct the implementation in the [OAuthProviders](../src/Tgstation.Server.Host/Security/OAuth/OAuthProviders.cs) class.
1. Add a null entry to the default [appsettings.json](../src/Tgstation.Server.Host/appsettings.json).
1. Add a null entry to the default [appsettings.yml](../src/Tgstation.Server.Host/appsettings.yml).
1. Update the main [README.md](../README.md) to indicate the new provider.
1. Update the [API documentation](../docs/API.dox) to indicate the new provider.
+4 -3
View File
@@ -11,10 +11,11 @@ artifacts/
*DS_Store
*.sln.ide
/TestResults
/tests/DMAPI/travistester.lk
/tests/DMAPI/travistester.int
/tests/DMAPI/travistester.dmb
*.dmb
*.int
*.lk
/src/Tgstation.Server.Host/appsettings.*.json
/src/Tgstation.Server.Host/appsettings.*.yml
/src/Tgstation.Server.Host/wwwroot
/src/Tgstation.Server.Host/ClientApp
/tools/ReleaseNotes/release_notes.md
+14 -19
View File
@@ -80,19 +80,19 @@ Note that this container is meant to be long running. Updates are handled intern
Note that automatic configuration reloading is currently not supported in the container. See #1143
If using manual configuration, before starting your container make sure the aforementioned `appsettings.Production.json` is setup properly. See below
If using manual configuration, before starting your container make sure the aforementioned `appsettings.Production.yml` is setup properly. See below
### Configuring
The first time you run TGS4 you should be prompted with a configuration wizard which will guide you through setting up your appsettings.Production.json
The first time you run TGS4 you should be prompted with a configuration wizard which will guide you through setting up your `appsettings.Production.yml`
This wizard will, generally, run whenever the server is launched without detecting the config json. Follow the instructions below to perform this process manually.
This wizard will, generally, run whenever the server is launched without detecting the config yml. Follow the instructions below to perform this process manually.
#### Configuration Methods
There are 3 primary supported ways to configure TGS:
- Modify the `appsettings.Production.json` file (Recommended).
- Modify the `appsettings.Production.yml` file (Recommended).
- Set environment variables in the form `Section__Subsection=value` or `Section__ArraySubsection__0=value` for arrays.
- Set command line arguments in the form `--Section:Subsection=value` or `--Section:ArraySubsection:0=value` for arrays.
@@ -100,7 +100,7 @@ The latter two are not recommended as they cannot be dynamically changed at runt
#### Manual Configuration
Create an `appsettings.Production.json` file next to `appsettings.json`. This will override the default settings in appsettings.json with your production settings. There are a few keys meant to be changed by hosts. Modifying any config files while the server is running will trigger a safe restart (Keeps DreamDaemon instances running). Note these are all case-sensitive:
Create an `appsettings.Production.yml` file next to `appsettings.yml`. This will override the default settings in `appsettings.yml` with your production settings. There are a few keys meant to be changed by hosts. Modifying any config files while the server is running will trigger a safe restart (Keeps DreamDaemon instances running). Note these are all case-sensitive:
- `General:ConfigVersion`: Suppresses warnings about out of date config versions. You should change this after updating TGS to one with a new config version. The current version can be found on the releases page for your server version (This field did not exist before v4.4.0).
@@ -143,19 +143,14 @@ Create an `appsettings.Production.json` file next to `appsettings.json`. This wi
- `Swarm:Identifier` should be set uniquely on all swarmed servers. Used to identify the current server. This is also used to select which instances exist on the current machine and should not be changed post-setup.
- `Security:OAuth:<Provider Name>`: Sets the OAuth client ID and secret for a given `<Provider Name>`. The currently supported providers are `GitHub`, `Discord`, and `TGForums`. Setting these fields to `null` disables logins with the provider, but does not stop users from associating their accounts using the API. Sample Entry:
```json
{
"Security": {
"OAuth": {
"Keycloak": {
"ClientId": "...",
"ClientSecret": "...",
"RedirectUrl": "...",
"ServerUrl": "..."
}
}
}
}
```yml
Security:
OAuth:
Keycloak:
ClientId: "..."
ClientSecret: "..."
RedirectUrl: "..."
ServerUrl: "..."
```
The following providers use the `RedirectUrl` setting:
@@ -252,7 +247,7 @@ var/global/client_count = 0
## Remote Access
tgstation-server is an [ASP.Net Core](https://docs.microsoft.com/en-us/aspnet/core/) app based on the Kestrel web server. This section is meant to serve as a general use case overview, but the entire Kestrel configuration can be modified to your liking with the configuration JSON. See [the official documentation](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel) for details.
tgstation-server is an [ASP.Net Core](https://docs.microsoft.com/en-us/aspnet/core/) app based on the Kestrel web server. This section is meant to serve as a general use case overview, but the entire Kestrel configuration can be modified to your liking with the configuration YAML. See [the official documentation](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel) for details.
Exposing the builtin Kestrel server to the internet directly over HTTP is highly not reccommended due to the lack of security. The recommended way to expose tgstation-server to the internet is to host it through a reverse proxy with HTTPS support. Here are some step by step examples to achieve this for major web servers.
+7 -6
View File
@@ -3,12 +3,13 @@
<!-- Integration tests will ensure they match across the board -->
<Import Project="ControlPanelVersion.props" />
<PropertyGroup>
<TgsCoreVersion>4.7.3</TgsCoreVersion>
<TgsConfigVersion>2.2.0</TgsConfigVersion>
<TgsApiVersion>8.2.0</TgsApiVersion>
<TgsClientVersion>9.1.2</TgsClientVersion>
<TgsDmapiVersion>5.2.10</TgsDmapiVersion>
<TgsHostWatchdogVersion>1.1.0</TgsHostWatchdogVersion>
<TgsCoreVersion>4.8.0</TgsCoreVersion>
<TgsConfigVersion>2.3.0</TgsConfigVersion>
<TgsApiVersion>8.3.0</TgsApiVersion>
<TgsClientVersion>9.2.0</TgsClientVersion>
<TgsDmapiVersion>6.0.0</TgsDmapiVersion>
<TgsInteropVersion>5.3.0</TgsInteropVersion>
<TgsHostWatchdogVersion>1.1.1</TgsHostWatchdogVersion>
<TgsContainerScriptVersion>1.2.0</TgsContainerScriptVersion>
</PropertyGroup>
</Project>
+2 -2
View File
@@ -5,8 +5,8 @@ SCRIPT_VERSION="1.2.0"
echo "tgstation-server 4 container startup script v$SCRIPT_VERSION"
echo "PWD: $PWD"
PROD_CONFIG=/config_data/appsettings.Production.json
HOST_CONFIG=/app/appsettings.Production.json
PROD_CONFIG=/config_data/appsettings.Production.yml
HOST_CONFIG=/app/appsettings.Production.yml
if [ ! -f $PROD_CONFIG ]; then
echo "$PROD_CONFIG not detected! Creating empty and running setup wizard..."
+1 -1
View File
@@ -23,7 +23,7 @@ This is a second process spawned by the Host Watchdog which facilitates the vast
The server's entrypoint is in the @ref Tgstation.Server.Host.Program class. This class mainly determines if the Host watchdog is present and creates and runs the @ref Tgstation.Server.Host.Server class. That class then builds an ASP.NET Core web host using the @ref Tgstation.Server.Host.Core.Application class.
The @ref Tgstation.Server.Host.Core.Application class has two methods called by the framework. First the @ref Tgstation.Server.Host.Core.Application.ConfigureServices method sets up dependency injection of interfaces for Controllers, the @ref Tgstation.Server.Host.Database.DatabaseContext, and the component factories of the server. The framework handles constructing these things once the application starts. Configuration is loaded from the appropriate appSettings.json into the @ref Tgstation.Server.Host.Configuration classes for injection as well. Then @ref Tgstation.Server.Host.Core.Application.Configure method is run which sets up the web request pipeline which currently has the following stack of handlers:
The @ref Tgstation.Server.Host.Core.Application class has two methods called by the framework. First the @ref Tgstation.Server.Host.Core.Application.ConfigureServices method sets up dependency injection of interfaces for Controllers, the @ref Tgstation.Server.Host.Database.DatabaseContext, and the component factories of the server. The framework handles constructing these things once the application starts. Configuration is loaded from the appropriate appsettings.yml into the @ref Tgstation.Server.Host.Configuration classes for injection as well. Then @ref Tgstation.Server.Host.Core.Application.Configure method is run which sets up the web request pipeline which currently has the following stack of handlers:
- Catch any exceptions and respond with 500 and detailed HTML error page
- Respond with 503 if the application is still starting or shutting down
+13 -9
View File
@@ -1,6 +1,6 @@
// tgstation-server DMAPI
#define TGS_DMAPI_VERSION "5.2.10"
#define TGS_DMAPI_VERSION "6.0.0"
// All functions and datums outside this document are subject to change with any version and should not be relied on.
@@ -95,8 +95,13 @@
#define TGS_EVENT_WATCHDOG_SHUTDOWN 15
/// Before the watchdog detaches for a TGS update/restart. No parameters.
#define TGS_EVENT_WATCHDOG_DETACH 16
// We don't actually implement this value as the DMAPI can never receive it
// We don't actually implement these 3 events as the DMAPI can never receive them.
// #define TGS_EVENT_WATCHDOG_LAUNCH 17
// #define TGS_EVENT_WATCHDOG_CRASH 18
// #define TGS_EVENT_WORLD_END_PROCESS 19
// #define TGS_EVENT_WORLD_REBOOT 20
/// Watchdog event when TgsInitializationComplete() is called. No parameters.
#define TGS_EVENT_WORLD_PRIME 21
// OTHER ENUMS
@@ -130,7 +135,6 @@
*
* This may use [/world/var/sleep_offline] to make this happen so ensure no changes are made to it while this call is running.
* Afterwards, consider explicitly setting it to what you want to avoid this BYOND bug: http://www.byond.com/forum/post/2575184
* Before this point, note that any static files or directories may be in use by another server. Your code should account for this.
* This function should not be called before ..() in [/world/proc/New].
*/
/world/proc/TgsInitializationComplete()
@@ -140,7 +144,7 @@
#define TGS_TOPIC var/tgs_topic_return = TgsTopic(args[1]); if(tgs_topic_return) return tgs_topic_return
/**
* Call this at the beginning of [world/proc/Reboot].
* Call this as late as possible in [world/proc/Reboot].
*/
/world/proc/TgsReboot()
return
@@ -152,6 +156,8 @@
/datum/tgs_revision_information
/// Full SHA of the commit.
var/commit
/// ISO 8601 timestamp of when the commit was created
var/timestamp
/// Full sha of last known remote commit. This may be null if the TGS repository is not currently tracking a remote branch.
var/origin_commit
@@ -201,9 +207,7 @@
/// An http URL to the test merge source.
var/url
/// The SHA of the test merge when that was merged.
var/pull_request_commit
/// ISO 8601 timestamp of when the test merge was created on TGS.
var/time_merged
var/head_commit
/// Optional comment left by the TGS user who initiated the merge.
var/comment
@@ -263,11 +267,11 @@
// API FUNCTIONS
/// Returns the maximum supported [/datum/tgs_version] of the DMAPI.
/world/proc/TgsMaximumAPIVersion()
/world/proc/TgsMaximumApiVersion()
return
/// Returns the minimum supported [/datum/tgs_version] of the DMAPI.
/world/proc/TgsMinimumAPIVersion()
/world/proc/TgsMinimumApiVersion()
return
/**
+3 -3
View File
@@ -40,7 +40,7 @@
if(5)
api_datum = /datum/tgs_api/v5
var/datum/tgs_version/max_api_version = TgsMaximumAPIVersion();
var/datum/tgs_version/max_api_version = TgsMaximumApiVersion();
if(version.suite != null && version.minor != null && version.patch != null && version.deprecated_patch != null && version.deprefixed_parameter > max_api_version.deprefixed_parameter)
TGS_ERROR_LOG("Detected unknown API version! Defaulting to latest. Update the DMAPI to fix this problem.")
api_datum = /datum/tgs_api/latest
@@ -64,10 +64,10 @@
TGS_WRITE_GLOBAL(tgs, null)
TGS_ERROR_LOG("Failed to activate API!")
/world/TgsMaximumAPIVersion()
/world/TgsMaximumApiVersion()
return new /datum/tgs_version("5.x.x")
/world/TgsMinimumAPIVersion()
/world/TgsMinimumApiVersion()
return new /datum/tgs_version("3.2.x")
/world/TgsInitializationComplete()
+2 -2
View File
@@ -92,7 +92,7 @@
var/list/json = cached_json["testMerges"]
for(var/entry in json)
var/datum/tgs_revision_information/test_merge/tm = new
tm.time_merged = text2num(entry["timeMerged"])
tm.timestamp = text2num(entry["timeMerged"])
var/list/revInfo = entry["revision"]
if(revInfo)
@@ -104,7 +104,7 @@
tm.url = entry["url"]
tm.author = entry["author"]
tm.number = entry["number"]
tm.pull_request_commit = entry["pullRequestRevision"]
tm.head_commit = entry["pullRequestRevision"]
tm.comment = entry["comment"]
cached_test_merges += tm
+1
View File
@@ -79,6 +79,7 @@
#define DMAPI5_TOPIC_RESPONSE_CHAT_RESPONSES "chatResponses"
#define DMAPI5_REVISION_INFORMATION_COMMIT_SHA "commitSha"
#define DMAPI5_REVISION_INFORMATION_TIMESTAMP "timestamp"
#define DMAPI5_REVISION_INFORMATION_ORIGIN_COMMIT_SHA "originCommitSha"
#define DMAPI5_CHAT_USER_ID "id"
+6 -3
View File
@@ -18,7 +18,9 @@
var/initialized = FALSE
/datum/tgs_api/v5/ApiVersion()
return new /datum/tgs_version(TGS_DMAPI_VERSION)
return new /datum/tgs_version(
#include "interop_version.dm"
)
/datum/tgs_api/v5/OnWorldNew(minimum_required_security_level)
server_port = world.params[DMAPI5_PARAM_SERVER_PORT]
@@ -48,6 +50,7 @@
if(istype(revisionData))
revision = new
revision.commit = revisionData[DMAPI5_REVISION_INFORMATION_COMMIT_SHA]
revision.timestamp = revisionData[DMAPI5_REVISION_INFORMATION_TIMESTAMP]
revision.origin_commit = revisionData[DMAPI5_REVISION_INFORMATION_ORIGIN_COMMIT_SHA]
else
TGS_ERROR_LOG("Failed to decode [DMAPI5_RUNTIME_INFORMATION_REVISION] from runtime information!")
@@ -66,12 +69,12 @@
else
TGS_WARNING_LOG("Failed to decode [DMAPI5_TEST_MERGE_REVISION] from test merge #[tm.number]!")
tm.time_merged = text2num(entry[DMAPI5_TEST_MERGE_TIME_MERGED])
tm.timestamp = entry[DMAPI5_TEST_MERGE_TIME_MERGED]
tm.title = entry[DMAPI5_TEST_MERGE_TITLE_AT_MERGE]
tm.body = entry[DMAPI5_TEST_MERGE_BODY_AT_MERGE]
tm.url = entry[DMAPI5_TEST_MERGE_URL]
tm.author = entry[DMAPI5_TEST_MERGE_AUTHOR]
tm.pull_request_commit = entry[DMAPI5_TEST_MERGE_PULL_REQUEST_REVISION]
tm.head_commit = entry[DMAPI5_TEST_MERGE_PULL_REQUEST_REVISION]
tm.comment = entry[DMAPI5_TEST_MERGE_COMMENT]
test_merges += tm
+1
View File
@@ -0,0 +1 @@
"5.3.0"
+1
View File
@@ -79,6 +79,7 @@
#undef DMAPI5_TOPIC_RESPONSE_CHAT_RESPONSES
#undef DMAPI5_REVISION_INFORMATION_COMMIT_SHA
#undef DMAPI5_REVISION_INFORMATION_TIMESTAMP
#undef DMAPI5_REVISION_INFORMATION_ORIGIN_COMMIT_SHA
#undef DMAPI5_CHAT_USER_ID
@@ -1,3 +1,4 @@
using System;
using System.ComponentModel.DataAnnotations;
namespace Tgstation.Server.Api.Models.Internal
@@ -8,14 +9,19 @@ namespace Tgstation.Server.Api.Models.Internal
public class RevisionInformation
{
/// <summary>
/// The revision sha
/// The revision SHA.
/// </summary>
[Required]
[StringLength(Limits.MaximumCommitShaLength)]
public string? CommitSha { get; set; }
/// <summary>
/// The sha of the most recent remote commit
/// The timestamp of the revision.
/// </summary>
public DateTimeOffset Timestamp { get; set; }
/// <summary>
/// The SHA of the most recent remote commit.
/// </summary>
[Required]
[StringLength(Limits.MaximumCommitShaLength)]
@@ -19,7 +19,7 @@ namespace Tgstation.Server.Api.Models
public Version? ApiVersion { get; set; }
/// <summary>
/// The DMAPI version of the host.
/// The DMAPI interop version the server uses.
/// </summary>
public Version? DMApiVersion { get; set; }
+5 -3
View File
@@ -1,4 +1,4 @@
using McMaster.Extensions.CommandLineUtils;
using McMaster.Extensions.CommandLineUtils;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Specialized;
@@ -20,12 +20,10 @@ namespace Tgstation.Server.Host.Service
/// </summary>
sealed class Program
{
#pragma warning disable SA1401 // Fields should be private
/// <summary>
/// The <see cref="IWatchdogFactory"/> for the <see cref="Program"/>
/// </summary>
internal static IWatchdogFactory WatchdogFactory = new WatchdogFactory();
#pragma warning restore SA1401 // Fields should be private
/// <summary>
/// The --uninstall or -u option
@@ -151,6 +149,10 @@ namespace Tgstation.Server.Host.Service
if (Configure)
{
#pragma warning disable CS0618 // Type or member is obsolete
loggerFactory.AddConsole();
#pragma warning restore CS0618 // Type or member is obsolete
// DCT: None available
await WatchdogFactory.CreateWatchdog(loggerFactory).RunAsync(true, Array.Empty<string>(), default).ConfigureAwait(false);
}
@@ -30,6 +30,7 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Logging.EventLog" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.2.0" />
</ItemGroup>
<ItemGroup>
@@ -33,7 +33,7 @@
</ItemGroup>
<ItemGroup>
<None Update="appsettings.json">
<None Update="appsettings.yml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
@@ -96,10 +96,10 @@ namespace Tgstation.Server.Host.Watchdog
foreach (string newPath in Directory.GetFiles(sourcePath, "*.*", SearchOption.AllDirectories))
File.Copy(newPath, newPath.Replace(sourcePath, defaultAssemblyPath), true);
const string AppSettingsJson = "appsettings.json";
var rootJson = Path.Combine(rootLocation, AppSettingsJson);
File.Delete(rootJson);
File.Move(Path.Combine(defaultAssemblyPath, AppSettingsJson), rootJson);
const string AppSettingsYaml = "appsettings.yml";
var rootYaml = Path.Combine(rootLocation, AppSettingsYaml);
File.Delete(rootYaml);
File.Move(Path.Combine(defaultAssemblyPath, AppSettingsYaml), rootYaml);
}
else
Directory.CreateDirectory(assemblyStoragePath);
@@ -525,10 +525,8 @@ namespace Tgstation.Server.Host.Components.Deployment
Action<int> progressReporter,
CancellationToken cancellationToken)
{
#pragma warning disable IDE0016 // Use 'throw' expression
if (job == null)
throw new ArgumentNullException(nameof(job));
#pragma warning restore IDE0016 // Use 'throw' expression
if (databaseContextFactory == null)
throw new ArgumentNullException(nameof(databaseContextFactory));
if (progressReporter == null)
@@ -625,6 +623,7 @@ namespace Tgstation.Server.Host.Components.Deployment
revInfo = new Models.RevisionInformation
{
CommitSha = repoSha,
Timestamp = await repo.TimestampCommit(repoSha, cancellationToken).ConfigureAwait(false),
OriginCommitSha = repoSha,
Instance = new Models.Instance
{
@@ -96,7 +96,7 @@ namespace Tgstation.Server.Host.Components.Events
DeploymentComplete,
/// <summary>
/// Before the watchdog shutsdown. Not sent for graceful shutdowns. No parameters.
/// Before the watchdog shuts down. Not sent for graceful shutdowns. No parameters.
/// </summary>
[EventScript("WatchdogShutdown")]
WatchdogShutdown,
@@ -111,6 +111,30 @@ namespace Tgstation.Server.Host.Components.Events
/// Before the watchdog launches. No parameters.
/// </summary>
[EventScript("WatchdogLaunch")]
WatchdogLaunch
WatchdogLaunch,
/// <summary>
/// Watchdog event when DreamDaemon exits unexpectedly. No parameters.
/// </summary>
[EventScript("WatchdogCrash")]
WatchdogCrash,
/// <summary>
/// In between watchdog DreamDaemon restarts if the process has been force-ended by the DMAPI (TgsEndProcess()). No parameters.
/// </summary>
[EventScript("WorldEndProcess")]
WorldEndProcess,
/// <summary>
/// Watchdog event when TgsReboot() is called. Not synchronous. Called after <see cref="WorldEndProcess"/>. No parameters.
/// </summary>
[EventScript("WorldReboot")]
WorldReboot,
/// <summary>
/// Watchdog event when TgsInitializationsComplete() is called. No parameters.
/// </summary>
[EventScript("WorldPrime")]
WorldPrime,
}
}
@@ -248,6 +248,7 @@ namespace Tgstation.Server.Host.Components
currentRevInfo = new RevisionInformation
{
CommitSha = currentHead,
Timestamp = await repo.TimestampCommit(currentHead, cancellationToken).ConfigureAwait(false),
OriginCommitSha = onOrigin
? currentHead
: await repo.GetOriginSha(cancellationToken).ConfigureAwait(false),
@@ -76,6 +76,7 @@ namespace Tgstation.Server.Host.Components.Interop.Bridge
Revision = new Api.Models.Internal.RevisionInformation
{
CommitSha = dmbProvider.CompileJob.RevisionInformation.CommitSha,
Timestamp = dmbProvider.CompileJob.RevisionInformation.Timestamp,
OriginCommitSha = dmbProvider.CompileJob.RevisionInformation.OriginCommitSha
};
@@ -32,9 +32,9 @@ namespace Tgstation.Server.Host.Components.Interop
public const string TopicData = "tgs_data";
/// <summary>
/// The DMAPI <see cref="Version"/> being used.
/// The DMAPI <see cref="InteropVersion"/> being used.
/// </summary>
public static readonly Version Version = Version.Parse(MasterVersionsAttribute.Instance.RawDMApiVersion);
public static readonly Version InteropVersion = Version.Parse(MasterVersionsAttribute.Instance.RawInteropVersion);
/// <summary>
/// <see cref="JsonSerializerSettings"/> for use when communicating with the DMAPI.
@@ -133,5 +133,13 @@ namespace Tgstation.Server.Host.Components.Repository
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
/// <returns>A <see cref="Task{TResult}"/> resulting in the tracked origin reference's SHA.</returns>
Task<string> GetOriginSha(CancellationToken cancellationToken);
/// <summary>
/// Gets the <see cref="DateTimeOffset"/> a given <paramref name="sha"/> was created on.
/// </summary>
/// <param name="sha">The SHA to timestamp.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
/// <returns>A <see cref="Task{TResult}"/> resulting in the <see cref="DateTimeOffset"/> that <paramref name="sha"/> was created on.</returns>
Task<DateTimeOffset> TimestampCommit(string sha, CancellationToken cancellationToken);
}
}
@@ -777,5 +777,18 @@ namespace Tgstation.Server.Host.Components.Repository
parameters,
repositorySettings,
cancellationToken);
/// <inheritdoc />
public Task<DateTimeOffset> TimestampCommit(string sha, CancellationToken cancellationToken) => Task.Factory.StartNew(() =>
{
if (sha == null)
throw new ArgumentNullException(nameof(sha));
var commit = libGitRepo.Lookup<Commit>(sha);
if (commit == null)
throw new JobException($"Commit {sha} does not exist in the repository!");
return commit.Committer.When;
}, cancellationToken, DefaultIOManager.BlockingTaskCreationOptions, TaskScheduler.Current);
}
}
@@ -49,12 +49,12 @@ namespace Tgstation.Server.Host.Components.Session
RebootState RebootState { get; }
/// <summary>
/// A <see cref="Task"/> that completes when the server calls /world/Reboot()
/// A <see cref="Task"/> that completes when the server calls /world/TgsReboot().
/// </summary>
Task OnReboot { get; }
/// <summary>
/// A <see cref="Task"/> that completes when the server calls /world/TgsInitializationsComplete()
/// A <see cref="Task"/> that completes when the server calls /world/TgsInitializationComplete()
/// </summary>
Task OnPrime { get; }
@@ -66,7 +66,7 @@ namespace Tgstation.Server.Host.Components.Session
public Task OnPrime => primeTcs.Task;
/// <inheritdoc />
public bool DMApiAvailable => reattachInformation.Dmb.CompileJob.DMApiVersion?.Major == DMApiConstants.Version.Major;
public bool DMApiAvailable => reattachInformation.Dmb.CompileJob.DMApiVersion?.Major == DMApiConstants.InteropVersion.Major;
/// <summary>
/// The up to date <see cref="ReattachInformation"/>
@@ -431,7 +431,7 @@ namespace Tgstation.Server.Host.Components.Session
return Error("Missing dmApiVersion field!");
DMApiVersion = parameters.Version;
if (DMApiVersion.Major != DMApiConstants.Version.Major)
if (DMApiVersion.Major != DMApiConstants.InteropVersion.Major)
{
apiValidationStatus = ApiValidationStatus.Incompatible;
return Error("Incompatible dmApiVersion!");
@@ -229,7 +229,7 @@ namespace Tgstation.Server.Host.Components.Session
// set command line options
// more sanitization here cause it uses the same scheme
var parameters = $"{DMApiConstants.ParamApiVersion}={byondTopicSender.SanitizeString(DMApiConstants.Version.Semver().ToString())}&{byondTopicSender.SanitizeString(DMApiConstants.ParamServerPort)}={serverPortProvider.HttpApiPort}&{byondTopicSender.SanitizeString(DMApiConstants.ParamAccessIdentifier)}={byondTopicSender.SanitizeString(accessIdentifier)}";
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}";
@@ -240,7 +240,7 @@ namespace Tgstation.Server.Host.Components.Session
Guid? logFileGuid = null;
var arguments = String.Format(
CultureInfo.InvariantCulture,
"{0} -port {1} -ports 1-65535 {2}-close -{3} -{4}{5} -public -params \"{6}\"",
"{0} -port {1} -ports 1-65535 {2}-close -{3} -{4}{5} -params \"{6}\"",
dmbProvider.DmbName,
launchParameters.Port.Value,
launchParameters.AllowWebClient.Value ? "-webclient " : String.Empty,
@@ -1,6 +1,7 @@
using Microsoft.Extensions.Logging;
using System;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Tgstation.Server.Api.Models.Internal;
@@ -91,7 +92,12 @@ namespace Tgstation.Server.Host.Components.Watchdog
switch (reason)
{
case MonitorActivationReason.ActiveServerCrashed:
string exitWord = Server.TerminationWasRequested ? "exited" : "crashed";
var eventType = Server.TerminationWasRequested
? EventType.WorldEndProcess
: EventType.WatchdogCrash;
await EventConsumer.HandleEvent(eventType, Enumerable.Empty<string>(), cancellationToken).ConfigureAwait(false);
var exitWord = Server.TerminationWasRequested ? "exited" : "crashed";
if (Server.RebootState == Session.RebootState.Shutdown)
{
// the time for graceful shutdown is now
@@ -124,6 +130,8 @@ namespace Tgstation.Server.Host.Components.Watchdog
gracefulRebootRequired = false;
Server.ResetRebootState();
await EventConsumer.HandleEvent(EventType.WorldReboot, Enumerable.Empty<string>(), cancellationToken).ConfigureAwait(false);
switch (rebootState)
{
case Session.RebootState.Normal:
@@ -140,7 +148,6 @@ namespace Tgstation.Server.Host.Components.Watchdog
default:
throw new InvalidOperationException($"Invalid reboot state: {rebootState}");
}
case MonitorActivationReason.ActiveLaunchParametersUpdated:
await Server.SetRebootState(Session.RebootState.Restart, cancellationToken).ConfigureAwait(false);
gracefulRebootRequired = true;
@@ -148,6 +155,9 @@ namespace Tgstation.Server.Host.Components.Watchdog
case MonitorActivationReason.NewDmbAvailable:
await HandleNewDmbAvailable(cancellationToken).ConfigureAwait(false);
break;
case MonitorActivationReason.ActiveServerPrimed:
await EventConsumer.HandleEvent(EventType.WorldPrime, Enumerable.Empty<string>(), cancellationToken).ConfigureAwait(false);
break;
case MonitorActivationReason.Heartbeat:
default:
throw new InvalidOperationException($"Invalid activation reason: {reason}");
@@ -1,4 +1,4 @@
namespace Tgstation.Server.Host.Components.Watchdog
namespace Tgstation.Server.Host.Components.Watchdog
{
/// <summary>
/// Reasons for the monitor to wake up
@@ -29,5 +29,10 @@
/// A heartbeat is required.
/// </summary>
Heartbeat,
/// <summary>
/// Server primed.
/// </summary>
ActiveServerPrimed,
}
}
@@ -83,6 +83,11 @@ namespace Tgstation.Server.Host.Components.Watchdog
/// </summary>
protected IAsyncDelayer AsyncDelayer { get; }
/// <summary>
/// The <see cref="IEventConsumer"/> that is not the <see cref="WatchdogBase"/>
/// </summary>
protected IEventConsumer EventConsumer { get; }
/// <summary>
/// The <see cref="Api.Models.Instance"/> for the <see cref="WatchdogBase"/>.
/// </summary>
@@ -118,11 +123,6 @@ namespace Tgstation.Server.Host.Components.Watchdog
/// </summary>
readonly IIOManager diagnosticsIOManager;
/// <summary>
/// The <see cref="IEventConsumer"/> that is not the <see cref="WatchdogBase"/>
/// </summary>
readonly IEventConsumer eventConsumer;
/// <summary>
/// The <see cref="IRemoteDeploymentManagerFactory"/> for the <see cref="WatchdogBase"/>.
/// </summary>
@@ -179,7 +179,7 @@ namespace Tgstation.Server.Host.Components.Watchdog
/// <param name="serverControl">The <see cref="IServerControl"/> to populate <see cref="restartRegistration"/> with</param>
/// <param name="asyncDelayer">The value of <see cref="AsyncDelayer"/>.</param>
/// <param name="diagnosticsIOManager">The value of <see cref="diagnosticsIOManager"/>.</param>
/// <param name="eventConsumer">The value of <see cref="eventConsumer"/>.</param>
/// <param name="eventConsumer">The value of <see cref="EventConsumer"/>.</param>
/// <param name="remoteDeploymentManagerFactory">The value of <see cref="remoteDeploymentManagerFactory"/>.</param>
/// <param name="logger">The value of <see cref="Logger"/></param>
/// <param name="initialLaunchParameters">The initial value of <see cref="ActiveLaunchParameters"/>. May be modified</param>
@@ -208,7 +208,7 @@ namespace Tgstation.Server.Host.Components.Watchdog
this.jobManager = jobManager ?? throw new ArgumentNullException(nameof(jobManager));
AsyncDelayer = asyncDelayer ?? throw new ArgumentNullException(nameof(asyncDelayer));
this.diagnosticsIOManager = diagnosticsIOManager ?? throw new ArgumentNullException(nameof(diagnosticsIOManager));
this.eventConsumer = eventConsumer ?? throw new ArgumentNullException(nameof(eventConsumer));
EventConsumer = eventConsumer ?? throw new ArgumentNullException(nameof(eventConsumer));
this.remoteDeploymentManagerFactory = remoteDeploymentManagerFactory ?? throw new ArgumentNullException(nameof(remoteDeploymentManagerFactory));
Logger = logger ?? throw new ArgumentNullException(nameof(logger));
ActiveLaunchParameters = initialLaunchParameters ?? throw new ArgumentNullException(nameof(initialLaunchParameters));
@@ -267,7 +267,7 @@ namespace Tgstation.Server.Host.Components.Watchdog
return;
if (!graceful)
{
var eventTask = eventConsumer.HandleEvent(releaseServers ? EventType.WatchdogDetach : EventType.WatchdogShutdown, null, cancellationToken);
var eventTask = EventConsumer.HandleEvent(releaseServers ? EventType.WatchdogDetach : EventType.WatchdogShutdown, null, cancellationToken);
var chatTask = announce ? Chat.QueueWatchdogMessage("Shutting down...", cancellationToken) : Task.CompletedTask;
@@ -378,7 +378,7 @@ namespace Tgstation.Server.Host.Components.Watchdog
cancellationToken); // simple announce
if (reattachInfo == null)
announceTask = Task.WhenAll(
eventConsumer.HandleEvent(EventType.WatchdogLaunch, Enumerable.Empty<string>(), cancellationToken),
EventConsumer.HandleEvent(EventType.WatchdogLaunch, Enumerable.Empty<string>(), cancellationToken),
announceTask);
}
else
@@ -609,6 +609,7 @@ namespace Tgstation.Server.Host.Components.Watchdog
/// </summary>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
/// <returns>A <see cref="Task"/> representing the running operation.</returns>
#pragma warning disable CA1502
private async Task MonitorLifetimes(CancellationToken cancellationToken)
{
Logger.LogTrace("Entered MonitorLifetimes");
@@ -619,6 +620,12 @@ namespace Tgstation.Server.Host.Components.Watchdog
try
{
MonitorAction nextAction = MonitorAction.Continue;
Task activeServerLifetime = null,
activeServerReboot = null,
serverPrimed = null,
activeLaunchParametersChanged = null,
newDmbAvailable = null;
ISessionController lastController = null;
for (ulong iteration = 1; nextAction != MonitorAction.Exit; ++iteration)
using (LogContext.PushProperty("Monitor", iteration))
try
@@ -627,17 +634,44 @@ namespace Tgstation.Server.Host.Components.Watchdog
nextAction = MonitorAction.Continue;
var controller = GetActiveController();
Task activeServerLifetime = controller.Lifetime;
var activeServerReboot = controller.OnReboot;
Task activeLaunchParametersChanged = ActiveParametersUpdated.Task;
var newDmbAvailable = DmbFactory.OnNewerDmb;
void UpdateMonitoredTasks()
{
static void TryUpdateTask(ref Task oldTask, Task newTask)
{
if (oldTask?.IsCompleted == true)
return;
oldTask = newTask;
}
if (lastController == controller)
{
TryUpdateTask(ref activeServerLifetime, controller.Lifetime);
TryUpdateTask(ref activeServerReboot, controller.OnReboot);
TryUpdateTask(ref serverPrimed, controller.OnPrime);
}
else
{
activeServerLifetime = controller.Lifetime;
activeServerReboot = controller.OnReboot;
serverPrimed = controller.OnPrime;
lastController = controller;
}
TryUpdateTask(ref activeLaunchParametersChanged, ActiveParametersUpdated.Task);
TryUpdateTask(ref newDmbAvailable, DmbFactory.OnNewerDmb);
}
UpdateMonitoredTasks();
var heartbeatSeconds = ActiveLaunchParameters.HeartbeatSeconds.Value;
var heartbeat = heartbeatSeconds == 0
|| !controller.DMApiAvailable
? Extensions.TaskExtensions.InfiniteTask()
: Task.Delay(TimeSpan.FromSeconds(heartbeatSeconds), cancellationToken);
: Task.Delay(
TimeSpan.FromSeconds(heartbeatSeconds),
cancellationToken);
// cancel waiting if requested
var cancelTcs = new TaskCompletionSource<object>();
@@ -647,7 +681,8 @@ namespace Tgstation.Server.Host.Components.Watchdog
heartbeat,
newDmbAvailable,
cancelTcs.Task,
activeLaunchParametersChanged);
activeLaunchParametersChanged,
serverPrimed);
// wait for something to happen
using (cancellationToken.Register(() => cancelTcs.SetCanceled()))
@@ -688,7 +723,10 @@ namespace Tgstation.Server.Host.Components.Watchdog
|| CheckActivationReason(ref activeServerReboot, MonitorActivationReason.ActiveServerRebooted)
|| CheckActivationReason(ref newDmbAvailable, MonitorActivationReason.NewDmbAvailable)
|| CheckActivationReason(ref activeLaunchParametersChanged, MonitorActivationReason.ActiveLaunchParametersUpdated)
|| CheckActivationReason(ref heartbeat, MonitorActivationReason.Heartbeat);
|| CheckActivationReason(ref heartbeat, MonitorActivationReason.Heartbeat)
|| CheckActivationReason(ref serverPrimed, MonitorActivationReason.ActiveServerPrimed);
UpdateMonitoredTasks();
if (!anyActivation)
moreActivationsToProcess = false;
@@ -769,6 +807,7 @@ namespace Tgstation.Server.Host.Components.Watchdog
Logger.LogTrace("Monitor exiting...");
}
#pragma warning restore CA1502
/// <summary>
/// Starts all <see cref="ISessionController"/>s.
@@ -1,5 +1,5 @@
# Configuration Classes
These types map directly to the settings used in the [appsettings.json](../appsettings.json) file and its derivatives. See the [Microsoft Docs on the ASP .NET Core configuration system](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-3.1) for details.
These types map directly to the settings used in the [appsettings.yml](../appsettings.yml) file and its derivatives. See the [Microsoft Docs on the ASP .NET Core configuration system](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-3.1) for details.
When making changes here, it's important to also update the config version in [build/Version.props](../../../build/Version.props) according to semver semantics. You'll also need to update the constant in [GeneralConfiguration.cs](./GeneralConfigration.cs).
When making changes here, it's important to also update the config version in [build/Version.props](../../../build/Version.props) according to semver semantics. You'll also need to update the constant in [GeneralConfiguration.cs](./GeneralConfigration.cs).
@@ -1,6 +1,8 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers;
using System;
using System.Net.Mime;
using Tgstation.Server.Host.Core;
@@ -42,6 +44,10 @@ namespace Tgstation.Server.Host.Controllers
var contentTypeProvider = new FileExtensionContentTypeProvider();
if (!contentTypeProvider.TryGetContentType(fileInfo.Name, out var contentType))
contentType = MediaTypeNames.Application.Octet;
else if (contentType == MediaTypeNames.Application.Json)
Response.Headers.Add(
HeaderNames.CacheControl,
new StringValues(new[] { "public", "max-age=31536000", "immutable" }));
return File(appRoute, contentType);
}
@@ -198,7 +198,7 @@ namespace Tgstation.Server.Host.Controllers
{
Version = assemblyInformationProvider.Version,
ApiVersion = ApiHeaders.Version,
DMApiVersion = DMApiConstants.Version,
DMApiVersion = DMApiConstants.InteropVersion,
MinimumPasswordLength = generalConfiguration.MinimumPasswordLength,
InstanceLimit = generalConfiguration.InstanceLimit,
UserLimit = generalConfiguration.UserLimit,
@@ -91,6 +91,7 @@ namespace Tgstation.Server.Host.Controllers
{
Instance = instance,
CommitSha = repoSha,
Timestamp = await repository.TimestampCommit(repoSha, cancellationToken).ConfigureAwait(false),
CompileJobs = new List<Models.CompileJob>(),
ActiveTestMerges = new List<RevInfoTestMerge>() // non null vals for api returns
};
@@ -492,7 +492,7 @@ namespace Tgstation.Server.Host.Core
// End of request pipeline setup
var masterVersionsAttribute = MasterVersionsAttribute.Instance;
logger.LogTrace("Configuration version: {0}", masterVersionsAttribute.RawConfigurationVersion);
logger.LogTrace("DMAPI version: {0}", masterVersionsAttribute.RawDMApiVersion);
logger.LogTrace("DMAPI Interop version: {0}", masterVersionsAttribute.RawInteropVersion);
logger.LogTrace("Web control panel version: {0}", masterVersionsAttribute.RawControlPanelVersion);
logger.LogDebug("Starting hosting on port {0}...", serverPortProvider.HttpApiPort);
@@ -377,22 +377,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(MSAddSwarmIdentifer);
internal static readonly Type MSLatestMigration = typeof(MSAddRevInfoTimestamp);
/// <summary>
/// Used by unit tests to remind us to setup the correct MYSQL migration downgrades.
/// </summary>
internal static readonly Type MYLatestMigration = typeof(MYAddSwarmIdentifer);
internal static readonly Type MYLatestMigration = typeof(MYAddRevInfoTimestamp);
/// <summary>
/// Used by unit tests to remind us to setup the correct PostgresSQL migration downgrades.
/// </summary>
internal static readonly Type PGLatestMigration = typeof(PGAddSwarmIdentifer);
internal static readonly Type PGLatestMigration = typeof(PGAddRevInfoTimestamp);
/// <summary>
/// Used by unit tests to remind us to setup the correct SQLite migration downgrades.
/// </summary>
internal static readonly Type SLLatestMigration = typeof(SLAddSwarmIdentifer);
internal static readonly Type SLLatestMigration = typeof(SLAddRevInfoTimestamp);
/// <inheritdoc />
#pragma warning disable CA1502 // Cyclomatic complexity
@@ -420,6 +420,15 @@ namespace Tgstation.Server.Host.Database
// Update this with new migrations as they are made
string targetMigration = null;
if (targetVersion < new Version(4, 8, 0))
targetMigration = currentDatabaseType switch
{
DatabaseType.MySql => nameof(MYAddSwarmIdentifer),
DatabaseType.PostgresSql => nameof(PGAddSwarmIdentifer),
DatabaseType.SqlServer => nameof(MSAddSwarmIdentifer),
DatabaseType.Sqlite => nameof(SLAddSwarmIdentifer),
_ => throw new ArgumentException($"Invalid DatabaseType: {currentDatabaseType}", nameof(currentDatabaseType)),
};
if (targetVersion < new Version(4, 7, 0))
targetMigration = currentDatabaseType switch
{
@@ -0,0 +1,902 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
namespace Tgstation.Server.Host.Database.Migrations
{
[DbContext(typeof(SqlServerDatabaseContext))]
[Migration("20210112154040_MSAddRevInfoTimestamp")]
partial class MSAddRevInfoTimestamp
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "3.1.10")
.HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<int>("ChannelLimit")
.HasColumnType("int");
b.Property<string>("ConnectionString")
.IsRequired()
.HasColumnType("nvarchar(max)")
.HasMaxLength(10000);
b.Property<bool?>("Enabled")
.HasColumnType("bit");
b.Property<long>("InstanceId")
.HasColumnType("bigint");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("nvarchar(100)")
.HasMaxLength(100);
b.Property<int>("Provider")
.HasColumnType("int");
b.Property<long>("ReconnectionInterval")
.HasColumnType("bigint");
b.HasKey("Id");
b.HasIndex("InstanceId", "Name")
.IsUnique();
b.ToTable("ChatBots");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<long>("ChatSettingsId")
.HasColumnType("bigint");
b.Property<decimal?>("DiscordChannelId")
.HasColumnType("decimal(20,0)");
b.Property<string>("IrcChannel")
.HasColumnType("nvarchar(100)")
.HasMaxLength(100);
b.Property<bool?>("IsAdminChannel")
.IsRequired()
.HasColumnType("bit");
b.Property<bool?>("IsUpdatesChannel")
.IsRequired()
.HasColumnType("bit");
b.Property<bool?>("IsWatchdogChannel")
.IsRequired()
.HasColumnType("bit");
b.Property<string>("Tag")
.HasColumnType("nvarchar(max)")
.HasMaxLength(10000);
b.HasKey("Id");
b.HasIndex("ChatSettingsId", "DiscordChannelId")
.IsUnique()
.HasFilter("[DiscordChannelId] IS NOT NULL");
b.HasIndex("ChatSettingsId", "IrcChannel")
.IsUnique()
.HasFilter("[IrcChannel] IS NOT NULL");
b.ToTable("ChatChannels");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<string>("ByondVersion")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<int?>("DMApiMajorVersion")
.HasColumnType("int");
b.Property<int?>("DMApiMinorVersion")
.HasColumnType("int");
b.Property<int?>("DMApiPatchVersion")
.HasColumnType("int");
b.Property<Guid?>("DirectoryName")
.IsRequired()
.HasColumnType("uniqueidentifier");
b.Property<string>("DmeName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<int?>("GitHubDeploymentId")
.HasColumnType("int");
b.Property<long?>("GitHubRepoId")
.HasColumnType("bigint");
b.Property<long>("JobId")
.HasColumnType("bigint");
b.Property<int?>("MinimumSecurityLevel")
.HasColumnType("int");
b.Property<string>("Output")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("RepositoryOrigin")
.HasColumnType("nvarchar(max)");
b.Property<long>("RevisionInformationId")
.HasColumnType("bigint");
b.HasKey("Id");
b.HasIndex("DirectoryName");
b.HasIndex("JobId")
.IsUnique();
b.HasIndex("RevisionInformationId");
b.ToTable("CompileJobs");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<string>("AdditionalParameters")
.IsRequired()
.HasColumnType("nvarchar(max)")
.HasMaxLength(10000);
b.Property<bool?>("AllowWebClient")
.IsRequired()
.HasColumnType("bit");
b.Property<bool?>("AutoStart")
.IsRequired()
.HasColumnType("bit");
b.Property<long>("HeartbeatSeconds")
.HasColumnType("bigint");
b.Property<long>("InstanceId")
.HasColumnType("bigint");
b.Property<int>("Port")
.HasColumnType("int");
b.Property<int>("SecurityLevel")
.HasColumnType("int");
b.Property<long>("StartupTimeout")
.HasColumnType("bigint");
b.Property<long>("TopicRequestTimeout")
.HasColumnType("bigint");
b.HasKey("Id");
b.HasIndex("InstanceId")
.IsUnique();
b.ToTable("DreamDaemonSettings");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<int>("ApiValidationPort")
.HasColumnType("int");
b.Property<int>("ApiValidationSecurityLevel")
.HasColumnType("int");
b.Property<long>("InstanceId")
.HasColumnType("bigint");
b.Property<string>("ProjectName")
.HasColumnType("nvarchar(max)")
.HasMaxLength(10000);
b.Property<bool?>("RequireDMApiValidation")
.IsRequired()
.HasColumnType("bit");
b.HasKey("Id");
b.HasIndex("InstanceId")
.IsUnique();
b.ToTable("DreamMakerSettings");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<long>("AutoUpdateInterval")
.HasColumnType("bigint");
b.Property<int>("ChatBotLimit")
.HasColumnType("int");
b.Property<int>("ConfigurationType")
.HasColumnType("int");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("nvarchar(max)")
.HasMaxLength(10000);
b.Property<bool?>("Online")
.IsRequired()
.HasColumnType("bit");
b.Property<string>("Path")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.Property<string>("SwarmIdentifer")
.HasColumnType("nvarchar(450)");
b.HasKey("Id");
b.HasIndex("Path", "SwarmIdentifer")
.IsUnique()
.HasFilter("[SwarmIdentifer] IS NOT NULL");
b.ToTable("Instances");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.InstancePermissionSet", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<decimal>("ByondRights")
.HasColumnType("decimal(20,0)");
b.Property<decimal>("ChatBotRights")
.HasColumnType("decimal(20,0)");
b.Property<decimal>("ConfigurationRights")
.HasColumnType("decimal(20,0)");
b.Property<decimal>("DreamDaemonRights")
.HasColumnType("decimal(20,0)");
b.Property<decimal>("DreamMakerRights")
.HasColumnType("decimal(20,0)");
b.Property<long>("InstanceId")
.HasColumnType("bigint");
b.Property<decimal>("InstancePermissionSetRights")
.HasColumnType("decimal(20,0)");
b.Property<long>("PermissionSetId")
.HasColumnType("bigint");
b.Property<decimal>("RepositoryRights")
.HasColumnType("decimal(20,0)");
b.HasKey("Id");
b.HasIndex("InstanceId");
b.HasIndex("PermissionSetId", "InstanceId")
.IsUnique();
b.ToTable("InstancePermissionSets");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<decimal?>("CancelRight")
.HasColumnType("decimal(20,0)");
b.Property<decimal?>("CancelRightsType")
.HasColumnType("decimal(20,0)");
b.Property<bool?>("Cancelled")
.IsRequired()
.HasColumnType("bit");
b.Property<long?>("CancelledById")
.HasColumnType("bigint");
b.Property<string>("Description")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<long?>("ErrorCode")
.HasColumnType("bigint");
b.Property<string>("ExceptionDetails")
.HasColumnType("nvarchar(max)");
b.Property<long>("InstanceId")
.HasColumnType("bigint");
b.Property<DateTimeOffset?>("StartedAt")
.IsRequired()
.HasColumnType("datetimeoffset");
b.Property<long>("StartedById")
.HasColumnType("bigint");
b.Property<DateTimeOffset?>("StoppedAt")
.HasColumnType("datetimeoffset");
b.HasKey("Id");
b.HasIndex("CancelledById");
b.HasIndex("InstanceId");
b.HasIndex("StartedById");
b.ToTable("Jobs");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.OAuthConnection", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<string>("ExternalUserId")
.IsRequired()
.HasColumnType("nvarchar(100)")
.HasMaxLength(100);
b.Property<int>("Provider")
.HasColumnType("int");
b.Property<long?>("UserId")
.HasColumnType("bigint");
b.HasKey("Id");
b.HasIndex("UserId");
b.HasIndex("Provider", "ExternalUserId")
.IsUnique();
b.ToTable("OAuthConnections");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.PermissionSet", b =>
{
b.Property<long?>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<decimal>("AdministrationRights")
.HasColumnType("decimal(20,0)");
b.Property<long?>("GroupId")
.HasColumnType("bigint");
b.Property<decimal>("InstanceManagerRights")
.HasColumnType("decimal(20,0)");
b.Property<long?>("UserId")
.HasColumnType("bigint");
b.HasKey("Id");
b.HasIndex("GroupId")
.IsUnique()
.HasFilter("[GroupId] IS NOT NULL");
b.HasIndex("UserId")
.IsUnique()
.HasFilter("[UserId] IS NOT NULL");
b.ToTable("PermissionSets");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<string>("AccessIdentifier")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<long>("CompileJobId")
.HasColumnType("bigint");
b.Property<int>("LaunchSecurityLevel")
.HasColumnType("int");
b.Property<int>("Port")
.HasColumnType("int");
b.Property<int>("ProcessId")
.HasColumnType("int");
b.Property<int>("RebootState")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("CompileJobId");
b.ToTable("ReattachInformations");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<string>("AccessToken")
.HasColumnType("nvarchar(max)")
.HasMaxLength(10000);
b.Property<string>("AccessUser")
.HasColumnType("nvarchar(max)")
.HasMaxLength(10000);
b.Property<bool?>("AutoUpdatesKeepTestMerges")
.IsRequired()
.HasColumnType("bit");
b.Property<bool?>("AutoUpdatesSynchronize")
.IsRequired()
.HasColumnType("bit");
b.Property<string>("CommitterEmail")
.IsRequired()
.HasColumnType("nvarchar(max)")
.HasMaxLength(10000);
b.Property<string>("CommitterName")
.IsRequired()
.HasColumnType("nvarchar(max)")
.HasMaxLength(10000);
b.Property<bool?>("CreateGitHubDeployments")
.IsRequired()
.HasColumnType("bit");
b.Property<long>("InstanceId")
.HasColumnType("bigint");
b.Property<bool?>("PostTestMergeComment")
.IsRequired()
.HasColumnType("bit");
b.Property<bool?>("PushTestMergeCommits")
.IsRequired()
.HasColumnType("bit");
b.Property<bool?>("ShowTestMergeCommitters")
.IsRequired()
.HasColumnType("bit");
b.HasKey("Id");
b.HasIndex("InstanceId")
.IsUnique();
b.ToTable("RepositorySettings");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<long>("RevisionInformationId")
.HasColumnType("bigint");
b.Property<long>("TestMergeId")
.HasColumnType("bigint");
b.HasKey("Id");
b.HasIndex("RevisionInformationId");
b.HasIndex("TestMergeId");
b.ToTable("RevInfoTestMerges");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<string>("CommitSha")
.IsRequired()
.HasColumnType("nvarchar(40)")
.HasMaxLength(40);
b.Property<long>("InstanceId")
.HasColumnType("bigint");
b.Property<string>("OriginCommitSha")
.IsRequired()
.HasColumnType("nvarchar(40)")
.HasMaxLength(40);
b.Property<DateTimeOffset>("Timestamp")
.HasColumnType("datetimeoffset");
b.HasKey("Id");
b.HasIndex("InstanceId", "CommitSha")
.IsUnique();
b.ToTable("RevisionInformations");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<string>("Author")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("BodyAtMerge")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Comment")
.HasColumnType("nvarchar(max)")
.HasMaxLength(10000);
b.Property<DateTimeOffset>("MergedAt")
.HasColumnType("datetimeoffset");
b.Property<long>("MergedById")
.HasColumnType("bigint");
b.Property<int>("Number")
.HasColumnType("int");
b.Property<long?>("PrimaryRevisionInformationId")
.IsRequired()
.HasColumnType("bigint");
b.Property<string>("TargetCommitSha")
.IsRequired()
.HasColumnType("nvarchar(40)")
.HasMaxLength(40);
b.Property<string>("TitleAtMerge")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Url")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.HasIndex("MergedById");
b.HasIndex("PrimaryRevisionInformationId")
.IsUnique();
b.ToTable("TestMerges");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
{
b.Property<long?>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<string>("CanonicalName")
.IsRequired()
.HasColumnType("nvarchar(100)")
.HasMaxLength(100);
b.Property<DateTimeOffset?>("CreatedAt")
.IsRequired()
.HasColumnType("datetimeoffset");
b.Property<long?>("CreatedById")
.HasColumnType("bigint");
b.Property<bool?>("Enabled")
.IsRequired()
.HasColumnType("bit");
b.Property<long?>("GroupId")
.HasColumnType("bigint");
b.Property<DateTimeOffset?>("LastPasswordUpdate")
.HasColumnType("datetimeoffset");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("nvarchar(100)")
.HasMaxLength(100);
b.Property<string>("PasswordHash")
.HasColumnType("nvarchar(max)");
b.Property<string>("SystemIdentifier")
.HasColumnType("nvarchar(100)")
.HasMaxLength(100);
b.HasKey("Id");
b.HasIndex("CanonicalName")
.IsUnique();
b.HasIndex("CreatedById");
b.HasIndex("GroupId");
b.HasIndex("SystemIdentifier")
.IsUnique()
.HasFilter("[SystemIdentifier] IS NOT NULL");
b.ToTable("Users");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.UserGroup", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<string>("Name")
.IsRequired()
.HasColumnType("nvarchar(100)")
.HasMaxLength(100);
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("Groups");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
{
b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
.WithMany("ChatSettings")
.HasForeignKey("InstanceId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
{
b.HasOne("Tgstation.Server.Host.Models.ChatBot", "ChatSettings")
.WithMany("Channels")
.HasForeignKey("ChatSettingsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
{
b.HasOne("Tgstation.Server.Host.Models.Job", "Job")
.WithOne()
.HasForeignKey("Tgstation.Server.Host.Models.CompileJob", "JobId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
.WithMany("CompileJobs")
.HasForeignKey("RevisionInformationId")
.OnDelete(DeleteBehavior.ClientNoAction)
.IsRequired();
});
modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
{
b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
.WithOne("DreamDaemonSettings")
.HasForeignKey("Tgstation.Server.Host.Models.DreamDaemonSettings", "InstanceId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
{
b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
.WithOne("DreamMakerSettings")
.HasForeignKey("Tgstation.Server.Host.Models.DreamMakerSettings", "InstanceId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Tgstation.Server.Host.Models.InstancePermissionSet", b =>
{
b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
.WithMany("InstancePermissionSets")
.HasForeignKey("InstanceId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Tgstation.Server.Host.Models.PermissionSet", "PermissionSet")
.WithMany("InstancePermissionSets")
.HasForeignKey("PermissionSetId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
{
b.HasOne("Tgstation.Server.Host.Models.User", "CancelledBy")
.WithMany()
.HasForeignKey("CancelledById");
b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
.WithMany("Jobs")
.HasForeignKey("InstanceId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Tgstation.Server.Host.Models.User", "StartedBy")
.WithMany()
.HasForeignKey("StartedById")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Tgstation.Server.Host.Models.OAuthConnection", b =>
{
b.HasOne("Tgstation.Server.Host.Models.User", "User")
.WithMany("OAuthConnections")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Tgstation.Server.Host.Models.PermissionSet", b =>
{
b.HasOne("Tgstation.Server.Host.Models.UserGroup", "Group")
.WithOne("PermissionSet")
.HasForeignKey("Tgstation.Server.Host.Models.PermissionSet", "GroupId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("Tgstation.Server.Host.Models.User", "User")
.WithOne("PermissionSet")
.HasForeignKey("Tgstation.Server.Host.Models.PermissionSet", "UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
{
b.HasOne("Tgstation.Server.Host.Models.CompileJob", "CompileJob")
.WithMany()
.HasForeignKey("CompileJobId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
{
b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
.WithOne("RepositorySettings")
.HasForeignKey("Tgstation.Server.Host.Models.RepositorySettings", "InstanceId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
{
b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
.WithMany("ActiveTestMerges")
.HasForeignKey("RevisionInformationId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Tgstation.Server.Host.Models.TestMerge", "TestMerge")
.WithMany("RevisonInformations")
.HasForeignKey("TestMergeId")
.OnDelete(DeleteBehavior.ClientNoAction)
.IsRequired();
});
modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
{
b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
.WithMany("RevisionInformations")
.HasForeignKey("InstanceId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
{
b.HasOne("Tgstation.Server.Host.Models.User", "MergedBy")
.WithMany("TestMerges")
.HasForeignKey("MergedById")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "PrimaryRevisionInformation")
.WithOne("PrimaryTestMerge")
.HasForeignKey("Tgstation.Server.Host.Models.TestMerge", "PrimaryRevisionInformationId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
{
b.HasOne("Tgstation.Server.Host.Models.User", "CreatedBy")
.WithMany("CreatedUsers")
.HasForeignKey("CreatedById");
b.HasOne("Tgstation.Server.Host.Models.UserGroup", "Group")
.WithMany("Users")
.HasForeignKey("GroupId");
});
#pragma warning restore 612, 618
}
}
}
@@ -0,0 +1,35 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
namespace Tgstation.Server.Host.Database.Migrations
{
/// <summary>
/// Add the Timestamp column to RevisionInformations for MSSQL.
/// </summary>
public partial class MSAddRevInfoTimestamp : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
if (migrationBuilder == null)
throw new ArgumentNullException(nameof(migrationBuilder));
migrationBuilder.AddColumn<DateTimeOffset>(
name: "Timestamp",
table: "RevisionInformations",
nullable: false,
defaultValue: new DateTimeOffset(new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)));
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
if (migrationBuilder == null)
throw new ArgumentNullException(nameof(migrationBuilder));
migrationBuilder.DropColumn(
name: "Timestamp",
table: "RevisionInformations");
}
}
}
@@ -0,0 +1,886 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
namespace Tgstation.Server.Host.Database.Migrations
{
[DbContext(typeof(MySqlDatabaseContext))]
[Migration("20210112154123_MYAddRevInfoTimestamp")]
partial class MYAddRevInfoTimestamp
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "3.1.10")
.HasAnnotation("Relational:MaxIdentifierLength", 64);
modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
b.Property<ushort?>("ChannelLimit")
.IsRequired()
.HasColumnType("smallint unsigned");
b.Property<string>("ConnectionString")
.IsRequired()
.HasColumnType("longtext CHARACTER SET utf8mb4")
.HasMaxLength(10000);
b.Property<bool?>("Enabled")
.HasColumnType("tinyint(1)");
b.Property<long>("InstanceId")
.HasColumnType("bigint");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("varchar(100) CHARACTER SET utf8mb4")
.HasMaxLength(100);
b.Property<int>("Provider")
.HasColumnType("int");
b.Property<uint?>("ReconnectionInterval")
.IsRequired()
.HasColumnType("int unsigned");
b.HasKey("Id");
b.HasIndex("InstanceId", "Name")
.IsUnique();
b.ToTable("ChatBots");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
b.Property<long>("ChatSettingsId")
.HasColumnType("bigint");
b.Property<ulong?>("DiscordChannelId")
.HasColumnType("bigint unsigned");
b.Property<string>("IrcChannel")
.HasColumnType("varchar(100) CHARACTER SET utf8mb4")
.HasMaxLength(100);
b.Property<bool?>("IsAdminChannel")
.IsRequired()
.HasColumnType("tinyint(1)");
b.Property<bool?>("IsUpdatesChannel")
.IsRequired()
.HasColumnType("tinyint(1)");
b.Property<bool?>("IsWatchdogChannel")
.IsRequired()
.HasColumnType("tinyint(1)");
b.Property<string>("Tag")
.HasColumnType("longtext CHARACTER SET utf8mb4")
.HasMaxLength(10000);
b.HasKey("Id");
b.HasIndex("ChatSettingsId", "DiscordChannelId")
.IsUnique();
b.HasIndex("ChatSettingsId", "IrcChannel")
.IsUnique();
b.ToTable("ChatChannels");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
b.Property<string>("ByondVersion")
.IsRequired()
.HasColumnType("longtext CHARACTER SET utf8mb4");
b.Property<int?>("DMApiMajorVersion")
.HasColumnType("int");
b.Property<int?>("DMApiMinorVersion")
.HasColumnType("int");
b.Property<int?>("DMApiPatchVersion")
.HasColumnType("int");
b.Property<Guid?>("DirectoryName")
.IsRequired()
.HasColumnType("char(36)");
b.Property<string>("DmeName")
.IsRequired()
.HasColumnType("longtext CHARACTER SET utf8mb4");
b.Property<int?>("GitHubDeploymentId")
.HasColumnType("int");
b.Property<long?>("GitHubRepoId")
.HasColumnType("bigint");
b.Property<long>("JobId")
.HasColumnType("bigint");
b.Property<int?>("MinimumSecurityLevel")
.HasColumnType("int");
b.Property<string>("Output")
.IsRequired()
.HasColumnType("longtext CHARACTER SET utf8mb4");
b.Property<string>("RepositoryOrigin")
.HasColumnType("longtext CHARACTER SET utf8mb4");
b.Property<long>("RevisionInformationId")
.HasColumnType("bigint");
b.HasKey("Id");
b.HasIndex("DirectoryName");
b.HasIndex("JobId")
.IsUnique();
b.HasIndex("RevisionInformationId");
b.ToTable("CompileJobs");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
b.Property<string>("AdditionalParameters")
.IsRequired()
.HasColumnType("longtext CHARACTER SET utf8mb4")
.HasMaxLength(10000);
b.Property<bool?>("AllowWebClient")
.IsRequired()
.HasColumnType("tinyint(1)");
b.Property<bool?>("AutoStart")
.IsRequired()
.HasColumnType("tinyint(1)");
b.Property<uint?>("HeartbeatSeconds")
.IsRequired()
.HasColumnType("int unsigned");
b.Property<long>("InstanceId")
.HasColumnType("bigint");
b.Property<ushort?>("Port")
.IsRequired()
.HasColumnType("smallint unsigned");
b.Property<int>("SecurityLevel")
.HasColumnType("int");
b.Property<uint?>("StartupTimeout")
.IsRequired()
.HasColumnType("int unsigned");
b.Property<uint?>("TopicRequestTimeout")
.IsRequired()
.HasColumnType("int unsigned");
b.HasKey("Id");
b.HasIndex("InstanceId")
.IsUnique();
b.ToTable("DreamDaemonSettings");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
b.Property<ushort?>("ApiValidationPort")
.IsRequired()
.HasColumnType("smallint unsigned");
b.Property<int>("ApiValidationSecurityLevel")
.HasColumnType("int");
b.Property<long>("InstanceId")
.HasColumnType("bigint");
b.Property<string>("ProjectName")
.HasColumnType("longtext CHARACTER SET utf8mb4")
.HasMaxLength(10000);
b.Property<bool?>("RequireDMApiValidation")
.IsRequired()
.HasColumnType("tinyint(1)");
b.HasKey("Id");
b.HasIndex("InstanceId")
.IsUnique();
b.ToTable("DreamMakerSettings");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
b.Property<uint?>("AutoUpdateInterval")
.IsRequired()
.HasColumnType("int unsigned");
b.Property<ushort?>("ChatBotLimit")
.IsRequired()
.HasColumnType("smallint unsigned");
b.Property<int>("ConfigurationType")
.HasColumnType("int");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("longtext CHARACTER SET utf8mb4")
.HasMaxLength(10000);
b.Property<bool?>("Online")
.IsRequired()
.HasColumnType("tinyint(1)");
b.Property<string>("Path")
.IsRequired()
.HasColumnType("varchar(255) CHARACTER SET utf8mb4");
b.Property<string>("SwarmIdentifer")
.HasColumnType("varchar(255) CHARACTER SET utf8mb4");
b.HasKey("Id");
b.HasIndex("Path", "SwarmIdentifer")
.IsUnique();
b.ToTable("Instances");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.InstancePermissionSet", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
b.Property<ulong>("ByondRights")
.HasColumnType("bigint unsigned");
b.Property<ulong>("ChatBotRights")
.HasColumnType("bigint unsigned");
b.Property<ulong>("ConfigurationRights")
.HasColumnType("bigint unsigned");
b.Property<ulong>("DreamDaemonRights")
.HasColumnType("bigint unsigned");
b.Property<ulong>("DreamMakerRights")
.HasColumnType("bigint unsigned");
b.Property<long>("InstanceId")
.HasColumnType("bigint");
b.Property<ulong>("InstancePermissionSetRights")
.HasColumnType("bigint unsigned");
b.Property<long>("PermissionSetId")
.HasColumnType("bigint");
b.Property<ulong>("RepositoryRights")
.HasColumnType("bigint unsigned");
b.HasKey("Id");
b.HasIndex("InstanceId");
b.HasIndex("PermissionSetId", "InstanceId")
.IsUnique();
b.ToTable("InstancePermissionSets");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
b.Property<ulong?>("CancelRight")
.HasColumnType("bigint unsigned");
b.Property<ulong?>("CancelRightsType")
.HasColumnType("bigint unsigned");
b.Property<bool?>("Cancelled")
.IsRequired()
.HasColumnType("tinyint(1)");
b.Property<long?>("CancelledById")
.HasColumnType("bigint");
b.Property<string>("Description")
.IsRequired()
.HasColumnType("longtext CHARACTER SET utf8mb4");
b.Property<uint?>("ErrorCode")
.HasColumnType("int unsigned");
b.Property<string>("ExceptionDetails")
.HasColumnType("longtext CHARACTER SET utf8mb4");
b.Property<long>("InstanceId")
.HasColumnType("bigint");
b.Property<DateTimeOffset?>("StartedAt")
.IsRequired()
.HasColumnType("datetime(6)");
b.Property<long>("StartedById")
.HasColumnType("bigint");
b.Property<DateTimeOffset?>("StoppedAt")
.HasColumnType("datetime(6)");
b.HasKey("Id");
b.HasIndex("CancelledById");
b.HasIndex("InstanceId");
b.HasIndex("StartedById");
b.ToTable("Jobs");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.OAuthConnection", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
b.Property<string>("ExternalUserId")
.IsRequired()
.HasColumnType("varchar(100) CHARACTER SET utf8mb4")
.HasMaxLength(100);
b.Property<int>("Provider")
.HasColumnType("int");
b.Property<long?>("UserId")
.HasColumnType("bigint");
b.HasKey("Id");
b.HasIndex("UserId");
b.HasIndex("Provider", "ExternalUserId")
.IsUnique();
b.ToTable("OAuthConnections");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.PermissionSet", b =>
{
b.Property<long?>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
b.Property<ulong>("AdministrationRights")
.HasColumnType("bigint unsigned");
b.Property<long?>("GroupId")
.HasColumnType("bigint");
b.Property<ulong>("InstanceManagerRights")
.HasColumnType("bigint unsigned");
b.Property<long?>("UserId")
.HasColumnType("bigint");
b.HasKey("Id");
b.HasIndex("GroupId")
.IsUnique();
b.HasIndex("UserId")
.IsUnique();
b.ToTable("PermissionSets");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
b.Property<string>("AccessIdentifier")
.IsRequired()
.HasColumnType("longtext CHARACTER SET utf8mb4");
b.Property<long>("CompileJobId")
.HasColumnType("bigint");
b.Property<int>("LaunchSecurityLevel")
.HasColumnType("int");
b.Property<ushort>("Port")
.HasColumnType("smallint unsigned");
b.Property<int>("ProcessId")
.HasColumnType("int");
b.Property<int>("RebootState")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("CompileJobId");
b.ToTable("ReattachInformations");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
b.Property<string>("AccessToken")
.HasColumnType("longtext CHARACTER SET utf8mb4")
.HasMaxLength(10000);
b.Property<string>("AccessUser")
.HasColumnType("longtext CHARACTER SET utf8mb4")
.HasMaxLength(10000);
b.Property<bool?>("AutoUpdatesKeepTestMerges")
.IsRequired()
.HasColumnType("tinyint(1)");
b.Property<bool?>("AutoUpdatesSynchronize")
.IsRequired()
.HasColumnType("tinyint(1)");
b.Property<string>("CommitterEmail")
.IsRequired()
.HasColumnType("longtext CHARACTER SET utf8mb4")
.HasMaxLength(10000);
b.Property<string>("CommitterName")
.IsRequired()
.HasColumnType("longtext CHARACTER SET utf8mb4")
.HasMaxLength(10000);
b.Property<bool?>("CreateGitHubDeployments")
.IsRequired()
.HasColumnType("tinyint(1)");
b.Property<long>("InstanceId")
.HasColumnType("bigint");
b.Property<bool?>("PostTestMergeComment")
.IsRequired()
.HasColumnType("tinyint(1)");
b.Property<bool?>("PushTestMergeCommits")
.IsRequired()
.HasColumnType("tinyint(1)");
b.Property<bool?>("ShowTestMergeCommitters")
.IsRequired()
.HasColumnType("tinyint(1)");
b.HasKey("Id");
b.HasIndex("InstanceId")
.IsUnique();
b.ToTable("RepositorySettings");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
b.Property<long>("RevisionInformationId")
.HasColumnType("bigint");
b.Property<long>("TestMergeId")
.HasColumnType("bigint");
b.HasKey("Id");
b.HasIndex("RevisionInformationId");
b.HasIndex("TestMergeId");
b.ToTable("RevInfoTestMerges");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
b.Property<string>("CommitSha")
.IsRequired()
.HasColumnType("varchar(40) CHARACTER SET utf8mb4")
.HasMaxLength(40);
b.Property<long>("InstanceId")
.HasColumnType("bigint");
b.Property<string>("OriginCommitSha")
.IsRequired()
.HasColumnType("varchar(40) CHARACTER SET utf8mb4")
.HasMaxLength(40);
b.Property<DateTimeOffset>("Timestamp")
.HasColumnType("datetime(6)");
b.HasKey("Id");
b.HasIndex("InstanceId", "CommitSha")
.IsUnique();
b.ToTable("RevisionInformations");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
b.Property<string>("Author")
.IsRequired()
.HasColumnType("longtext CHARACTER SET utf8mb4");
b.Property<string>("BodyAtMerge")
.IsRequired()
.HasColumnType("longtext CHARACTER SET utf8mb4");
b.Property<string>("Comment")
.HasColumnType("longtext CHARACTER SET utf8mb4")
.HasMaxLength(10000);
b.Property<DateTimeOffset>("MergedAt")
.HasColumnType("datetime(6)");
b.Property<long>("MergedById")
.HasColumnType("bigint");
b.Property<int>("Number")
.HasColumnType("int");
b.Property<long?>("PrimaryRevisionInformationId")
.IsRequired()
.HasColumnType("bigint");
b.Property<string>("TargetCommitSha")
.IsRequired()
.HasColumnType("varchar(40) CHARACTER SET utf8mb4")
.HasMaxLength(40);
b.Property<string>("TitleAtMerge")
.IsRequired()
.HasColumnType("longtext CHARACTER SET utf8mb4");
b.Property<string>("Url")
.IsRequired()
.HasColumnType("longtext CHARACTER SET utf8mb4");
b.HasKey("Id");
b.HasIndex("MergedById");
b.HasIndex("PrimaryRevisionInformationId")
.IsUnique();
b.ToTable("TestMerges");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
{
b.Property<long?>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
b.Property<string>("CanonicalName")
.IsRequired()
.HasColumnType("varchar(100) CHARACTER SET utf8mb4")
.HasMaxLength(100);
b.Property<DateTimeOffset?>("CreatedAt")
.IsRequired()
.HasColumnType("datetime(6)");
b.Property<long?>("CreatedById")
.HasColumnType("bigint");
b.Property<bool?>("Enabled")
.IsRequired()
.HasColumnType("tinyint(1)");
b.Property<long?>("GroupId")
.HasColumnType("bigint");
b.Property<DateTimeOffset?>("LastPasswordUpdate")
.HasColumnType("datetime(6)");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("varchar(100) CHARACTER SET utf8mb4")
.HasMaxLength(100);
b.Property<string>("PasswordHash")
.HasColumnType("longtext CHARACTER SET utf8mb4");
b.Property<string>("SystemIdentifier")
.HasColumnType("varchar(100) CHARACTER SET utf8mb4")
.HasMaxLength(100);
b.HasKey("Id");
b.HasIndex("CanonicalName")
.IsUnique();
b.HasIndex("CreatedById");
b.HasIndex("GroupId");
b.HasIndex("SystemIdentifier")
.IsUnique();
b.ToTable("Users");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.UserGroup", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("varchar(100) CHARACTER SET utf8mb4")
.HasMaxLength(100);
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("Groups");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
{
b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
.WithMany("ChatSettings")
.HasForeignKey("InstanceId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
{
b.HasOne("Tgstation.Server.Host.Models.ChatBot", "ChatSettings")
.WithMany("Channels")
.HasForeignKey("ChatSettingsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
{
b.HasOne("Tgstation.Server.Host.Models.Job", "Job")
.WithOne()
.HasForeignKey("Tgstation.Server.Host.Models.CompileJob", "JobId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
.WithMany("CompileJobs")
.HasForeignKey("RevisionInformationId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
{
b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
.WithOne("DreamDaemonSettings")
.HasForeignKey("Tgstation.Server.Host.Models.DreamDaemonSettings", "InstanceId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
{
b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
.WithOne("DreamMakerSettings")
.HasForeignKey("Tgstation.Server.Host.Models.DreamMakerSettings", "InstanceId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Tgstation.Server.Host.Models.InstancePermissionSet", b =>
{
b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
.WithMany("InstancePermissionSets")
.HasForeignKey("InstanceId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Tgstation.Server.Host.Models.PermissionSet", "PermissionSet")
.WithMany("InstancePermissionSets")
.HasForeignKey("PermissionSetId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
{
b.HasOne("Tgstation.Server.Host.Models.User", "CancelledBy")
.WithMany()
.HasForeignKey("CancelledById");
b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
.WithMany("Jobs")
.HasForeignKey("InstanceId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Tgstation.Server.Host.Models.User", "StartedBy")
.WithMany()
.HasForeignKey("StartedById")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Tgstation.Server.Host.Models.OAuthConnection", b =>
{
b.HasOne("Tgstation.Server.Host.Models.User", "User")
.WithMany("OAuthConnections")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Tgstation.Server.Host.Models.PermissionSet", b =>
{
b.HasOne("Tgstation.Server.Host.Models.UserGroup", "Group")
.WithOne("PermissionSet")
.HasForeignKey("Tgstation.Server.Host.Models.PermissionSet", "GroupId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("Tgstation.Server.Host.Models.User", "User")
.WithOne("PermissionSet")
.HasForeignKey("Tgstation.Server.Host.Models.PermissionSet", "UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
{
b.HasOne("Tgstation.Server.Host.Models.CompileJob", "CompileJob")
.WithMany()
.HasForeignKey("CompileJobId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
{
b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
.WithOne("RepositorySettings")
.HasForeignKey("Tgstation.Server.Host.Models.RepositorySettings", "InstanceId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
{
b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
.WithMany("ActiveTestMerges")
.HasForeignKey("RevisionInformationId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Tgstation.Server.Host.Models.TestMerge", "TestMerge")
.WithMany("RevisonInformations")
.HasForeignKey("TestMergeId")
.OnDelete(DeleteBehavior.ClientNoAction)
.IsRequired();
});
modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
{
b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
.WithMany("RevisionInformations")
.HasForeignKey("InstanceId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
{
b.HasOne("Tgstation.Server.Host.Models.User", "MergedBy")
.WithMany("TestMerges")
.HasForeignKey("MergedById")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "PrimaryRevisionInformation")
.WithOne("PrimaryTestMerge")
.HasForeignKey("Tgstation.Server.Host.Models.TestMerge", "PrimaryRevisionInformationId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
{
b.HasOne("Tgstation.Server.Host.Models.User", "CreatedBy")
.WithMany("CreatedUsers")
.HasForeignKey("CreatedById");
b.HasOne("Tgstation.Server.Host.Models.UserGroup", "Group")
.WithMany("Users")
.HasForeignKey("GroupId");
});
#pragma warning restore 612, 618
}
}
}
@@ -0,0 +1,35 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
namespace Tgstation.Server.Host.Database.Migrations
{
/// <summary>
/// Add the Timestamp column to RevisionInformations for MYSQL.
/// </summary>
public partial class MYAddRevInfoTimestamp : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
if (migrationBuilder == null)
throw new ArgumentNullException(nameof(migrationBuilder));
migrationBuilder.AddColumn<DateTimeOffset>(
name: "Timestamp",
table: "RevisionInformations",
nullable: false,
defaultValue: new DateTimeOffset(new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)));
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
if (migrationBuilder == null)
throw new ArgumentNullException(nameof(migrationBuilder));
migrationBuilder.DropColumn(
name: "Timestamp",
table: "RevisionInformations");
}
}
}
@@ -0,0 +1,896 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
namespace Tgstation.Server.Host.Database.Migrations
{
[DbContext(typeof(PostgresSqlDatabaseContext))]
[Migration("20210112154203_PGAddRevInfoTimestamp")]
partial class PGAddRevInfoTimestamp
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn)
.HasAnnotation("ProductVersion", "3.1.10")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
b.Property<int>("ChannelLimit")
.HasColumnType("integer");
b.Property<string>("ConnectionString")
.IsRequired()
.HasColumnType("character varying(10000)")
.HasMaxLength(10000);
b.Property<bool?>("Enabled")
.HasColumnType("boolean");
b.Property<long>("InstanceId")
.HasColumnType("bigint");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("character varying(100)")
.HasMaxLength(100);
b.Property<int>("Provider")
.HasColumnType("integer");
b.Property<long>("ReconnectionInterval")
.HasColumnType("bigint");
b.HasKey("Id");
b.HasIndex("InstanceId", "Name")
.IsUnique();
b.ToTable("ChatBots");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
b.Property<long>("ChatSettingsId")
.HasColumnType("bigint");
b.Property<decimal?>("DiscordChannelId")
.HasColumnType("numeric(20,0)");
b.Property<string>("IrcChannel")
.HasColumnType("character varying(100)")
.HasMaxLength(100);
b.Property<bool?>("IsAdminChannel")
.IsRequired()
.HasColumnType("boolean");
b.Property<bool?>("IsUpdatesChannel")
.IsRequired()
.HasColumnType("boolean");
b.Property<bool?>("IsWatchdogChannel")
.IsRequired()
.HasColumnType("boolean");
b.Property<string>("Tag")
.HasColumnType("character varying(10000)")
.HasMaxLength(10000);
b.HasKey("Id");
b.HasIndex("ChatSettingsId", "DiscordChannelId")
.IsUnique();
b.HasIndex("ChatSettingsId", "IrcChannel")
.IsUnique();
b.ToTable("ChatChannels");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
b.Property<string>("ByondVersion")
.IsRequired()
.HasColumnType("text");
b.Property<int?>("DMApiMajorVersion")
.HasColumnType("integer");
b.Property<int?>("DMApiMinorVersion")
.HasColumnType("integer");
b.Property<int?>("DMApiPatchVersion")
.HasColumnType("integer");
b.Property<Guid?>("DirectoryName")
.IsRequired()
.HasColumnType("uuid");
b.Property<string>("DmeName")
.IsRequired()
.HasColumnType("text");
b.Property<int?>("GitHubDeploymentId")
.HasColumnType("integer");
b.Property<long?>("GitHubRepoId")
.HasColumnType("bigint");
b.Property<long>("JobId")
.HasColumnType("bigint");
b.Property<int?>("MinimumSecurityLevel")
.HasColumnType("integer");
b.Property<string>("Output")
.IsRequired()
.HasColumnType("text");
b.Property<string>("RepositoryOrigin")
.HasColumnType("text");
b.Property<long>("RevisionInformationId")
.HasColumnType("bigint");
b.HasKey("Id");
b.HasIndex("DirectoryName");
b.HasIndex("JobId")
.IsUnique();
b.HasIndex("RevisionInformationId");
b.ToTable("CompileJobs");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
b.Property<string>("AdditionalParameters")
.IsRequired()
.HasColumnType("character varying(10000)")
.HasMaxLength(10000);
b.Property<bool?>("AllowWebClient")
.IsRequired()
.HasColumnType("boolean");
b.Property<bool?>("AutoStart")
.IsRequired()
.HasColumnType("boolean");
b.Property<long>("HeartbeatSeconds")
.HasColumnType("bigint");
b.Property<long>("InstanceId")
.HasColumnType("bigint");
b.Property<int>("Port")
.HasColumnType("integer");
b.Property<int>("SecurityLevel")
.HasColumnType("integer");
b.Property<long>("StartupTimeout")
.HasColumnType("bigint");
b.Property<long>("TopicRequestTimeout")
.HasColumnType("bigint");
b.HasKey("Id");
b.HasIndex("InstanceId")
.IsUnique();
b.ToTable("DreamDaemonSettings");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
b.Property<int>("ApiValidationPort")
.HasColumnType("integer");
b.Property<int>("ApiValidationSecurityLevel")
.HasColumnType("integer");
b.Property<long>("InstanceId")
.HasColumnType("bigint");
b.Property<string>("ProjectName")
.HasColumnType("character varying(10000)")
.HasMaxLength(10000);
b.Property<bool?>("RequireDMApiValidation")
.IsRequired()
.HasColumnType("boolean");
b.HasKey("Id");
b.HasIndex("InstanceId")
.IsUnique();
b.ToTable("DreamMakerSettings");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
b.Property<long>("AutoUpdateInterval")
.HasColumnType("bigint");
b.Property<int>("ChatBotLimit")
.HasColumnType("integer");
b.Property<int>("ConfigurationType")
.HasColumnType("integer");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("character varying(10000)")
.HasMaxLength(10000);
b.Property<bool?>("Online")
.IsRequired()
.HasColumnType("boolean");
b.Property<string>("Path")
.IsRequired()
.HasColumnType("text");
b.Property<string>("SwarmIdentifer")
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("Path", "SwarmIdentifer")
.IsUnique();
b.ToTable("Instances");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.InstancePermissionSet", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
b.Property<decimal>("ByondRights")
.HasColumnType("numeric(20,0)");
b.Property<decimal>("ChatBotRights")
.HasColumnType("numeric(20,0)");
b.Property<decimal>("ConfigurationRights")
.HasColumnType("numeric(20,0)");
b.Property<decimal>("DreamDaemonRights")
.HasColumnType("numeric(20,0)");
b.Property<decimal>("DreamMakerRights")
.HasColumnType("numeric(20,0)");
b.Property<long>("InstanceId")
.HasColumnType("bigint");
b.Property<decimal>("InstancePermissionSetRights")
.HasColumnType("numeric(20,0)");
b.Property<long>("PermissionSetId")
.HasColumnType("bigint");
b.Property<decimal>("RepositoryRights")
.HasColumnType("numeric(20,0)");
b.HasKey("Id");
b.HasIndex("InstanceId");
b.HasIndex("PermissionSetId", "InstanceId")
.IsUnique();
b.ToTable("InstancePermissionSets");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
b.Property<decimal?>("CancelRight")
.HasColumnType("numeric(20,0)");
b.Property<decimal?>("CancelRightsType")
.HasColumnType("numeric(20,0)");
b.Property<bool?>("Cancelled")
.IsRequired()
.HasColumnType("boolean");
b.Property<long?>("CancelledById")
.HasColumnType("bigint");
b.Property<string>("Description")
.IsRequired()
.HasColumnType("text");
b.Property<long?>("ErrorCode")
.HasColumnType("bigint");
b.Property<string>("ExceptionDetails")
.HasColumnType("text");
b.Property<long>("InstanceId")
.HasColumnType("bigint");
b.Property<DateTimeOffset?>("StartedAt")
.IsRequired()
.HasColumnType("timestamp with time zone");
b.Property<long>("StartedById")
.HasColumnType("bigint");
b.Property<DateTimeOffset?>("StoppedAt")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.HasIndex("CancelledById");
b.HasIndex("InstanceId");
b.HasIndex("StartedById");
b.ToTable("Jobs");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.OAuthConnection", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
b.Property<string>("ExternalUserId")
.IsRequired()
.HasColumnType("character varying(100)")
.HasMaxLength(100);
b.Property<int>("Provider")
.HasColumnType("integer");
b.Property<long?>("UserId")
.HasColumnType("bigint");
b.HasKey("Id");
b.HasIndex("UserId");
b.HasIndex("Provider", "ExternalUserId")
.IsUnique();
b.ToTable("OAuthConnections");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.PermissionSet", b =>
{
b.Property<long?>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
b.Property<decimal>("AdministrationRights")
.HasColumnType("numeric(20,0)");
b.Property<long?>("GroupId")
.HasColumnType("bigint");
b.Property<decimal>("InstanceManagerRights")
.HasColumnType("numeric(20,0)");
b.Property<long?>("UserId")
.HasColumnType("bigint");
b.HasKey("Id");
b.HasIndex("GroupId")
.IsUnique();
b.HasIndex("UserId")
.IsUnique();
b.ToTable("PermissionSets");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
b.Property<string>("AccessIdentifier")
.IsRequired()
.HasColumnType("text");
b.Property<long>("CompileJobId")
.HasColumnType("bigint");
b.Property<int>("LaunchSecurityLevel")
.HasColumnType("integer");
b.Property<int>("Port")
.HasColumnType("integer");
b.Property<int>("ProcessId")
.HasColumnType("integer");
b.Property<int>("RebootState")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("CompileJobId");
b.ToTable("ReattachInformations");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
b.Property<string>("AccessToken")
.HasColumnType("character varying(10000)")
.HasMaxLength(10000);
b.Property<string>("AccessUser")
.HasColumnType("character varying(10000)")
.HasMaxLength(10000);
b.Property<bool?>("AutoUpdatesKeepTestMerges")
.IsRequired()
.HasColumnType("boolean");
b.Property<bool?>("AutoUpdatesSynchronize")
.IsRequired()
.HasColumnType("boolean");
b.Property<string>("CommitterEmail")
.IsRequired()
.HasColumnType("character varying(10000)")
.HasMaxLength(10000);
b.Property<string>("CommitterName")
.IsRequired()
.HasColumnType("character varying(10000)")
.HasMaxLength(10000);
b.Property<bool?>("CreateGitHubDeployments")
.IsRequired()
.HasColumnType("boolean");
b.Property<long>("InstanceId")
.HasColumnType("bigint");
b.Property<bool?>("PostTestMergeComment")
.IsRequired()
.HasColumnType("boolean");
b.Property<bool?>("PushTestMergeCommits")
.IsRequired()
.HasColumnType("boolean");
b.Property<bool?>("ShowTestMergeCommitters")
.IsRequired()
.HasColumnType("boolean");
b.HasKey("Id");
b.HasIndex("InstanceId")
.IsUnique();
b.ToTable("RepositorySettings");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
b.Property<long>("RevisionInformationId")
.HasColumnType("bigint");
b.Property<long>("TestMergeId")
.HasColumnType("bigint");
b.HasKey("Id");
b.HasIndex("RevisionInformationId");
b.HasIndex("TestMergeId");
b.ToTable("RevInfoTestMerges");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
b.Property<string>("CommitSha")
.IsRequired()
.HasColumnType("character varying(40)")
.HasMaxLength(40);
b.Property<long>("InstanceId")
.HasColumnType("bigint");
b.Property<string>("OriginCommitSha")
.IsRequired()
.HasColumnType("character varying(40)")
.HasMaxLength(40);
b.Property<DateTimeOffset>("Timestamp")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.HasIndex("InstanceId", "CommitSha")
.IsUnique();
b.ToTable("RevisionInformations");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
b.Property<string>("Author")
.IsRequired()
.HasColumnType("text");
b.Property<string>("BodyAtMerge")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Comment")
.HasColumnType("character varying(10000)")
.HasMaxLength(10000);
b.Property<DateTimeOffset>("MergedAt")
.HasColumnType("timestamp with time zone");
b.Property<long>("MergedById")
.HasColumnType("bigint");
b.Property<int>("Number")
.HasColumnType("integer");
b.Property<long?>("PrimaryRevisionInformationId")
.IsRequired()
.HasColumnType("bigint");
b.Property<string>("TargetCommitSha")
.IsRequired()
.HasColumnType("character varying(40)")
.HasMaxLength(40);
b.Property<string>("TitleAtMerge")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Url")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("MergedById");
b.HasIndex("PrimaryRevisionInformationId")
.IsUnique();
b.ToTable("TestMerges");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
{
b.Property<long?>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
b.Property<string>("CanonicalName")
.IsRequired()
.HasColumnType("character varying(100)")
.HasMaxLength(100);
b.Property<DateTimeOffset?>("CreatedAt")
.IsRequired()
.HasColumnType("timestamp with time zone");
b.Property<long?>("CreatedById")
.HasColumnType("bigint");
b.Property<bool?>("Enabled")
.IsRequired()
.HasColumnType("boolean");
b.Property<long?>("GroupId")
.HasColumnType("bigint");
b.Property<DateTimeOffset?>("LastPasswordUpdate")
.HasColumnType("timestamp with time zone");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("character varying(100)")
.HasMaxLength(100);
b.Property<string>("PasswordHash")
.HasColumnType("text");
b.Property<string>("SystemIdentifier")
.HasColumnType("character varying(100)")
.HasMaxLength(100);
b.HasKey("Id");
b.HasIndex("CanonicalName")
.IsUnique();
b.HasIndex("CreatedById");
b.HasIndex("GroupId");
b.HasIndex("SystemIdentifier")
.IsUnique();
b.ToTable("Users");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.UserGroup", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
b.Property<string>("Name")
.IsRequired()
.HasColumnType("character varying(100)")
.HasMaxLength(100);
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("Groups");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
{
b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
.WithMany("ChatSettings")
.HasForeignKey("InstanceId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
{
b.HasOne("Tgstation.Server.Host.Models.ChatBot", "ChatSettings")
.WithMany("Channels")
.HasForeignKey("ChatSettingsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
{
b.HasOne("Tgstation.Server.Host.Models.Job", "Job")
.WithOne()
.HasForeignKey("Tgstation.Server.Host.Models.CompileJob", "JobId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
.WithMany("CompileJobs")
.HasForeignKey("RevisionInformationId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
{
b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
.WithOne("DreamDaemonSettings")
.HasForeignKey("Tgstation.Server.Host.Models.DreamDaemonSettings", "InstanceId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
{
b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
.WithOne("DreamMakerSettings")
.HasForeignKey("Tgstation.Server.Host.Models.DreamMakerSettings", "InstanceId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Tgstation.Server.Host.Models.InstancePermissionSet", b =>
{
b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
.WithMany("InstancePermissionSets")
.HasForeignKey("InstanceId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Tgstation.Server.Host.Models.PermissionSet", "PermissionSet")
.WithMany("InstancePermissionSets")
.HasForeignKey("PermissionSetId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
{
b.HasOne("Tgstation.Server.Host.Models.User", "CancelledBy")
.WithMany()
.HasForeignKey("CancelledById");
b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
.WithMany("Jobs")
.HasForeignKey("InstanceId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Tgstation.Server.Host.Models.User", "StartedBy")
.WithMany()
.HasForeignKey("StartedById")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Tgstation.Server.Host.Models.OAuthConnection", b =>
{
b.HasOne("Tgstation.Server.Host.Models.User", "User")
.WithMany("OAuthConnections")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Tgstation.Server.Host.Models.PermissionSet", b =>
{
b.HasOne("Tgstation.Server.Host.Models.UserGroup", "Group")
.WithOne("PermissionSet")
.HasForeignKey("Tgstation.Server.Host.Models.PermissionSet", "GroupId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("Tgstation.Server.Host.Models.User", "User")
.WithOne("PermissionSet")
.HasForeignKey("Tgstation.Server.Host.Models.PermissionSet", "UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
{
b.HasOne("Tgstation.Server.Host.Models.CompileJob", "CompileJob")
.WithMany()
.HasForeignKey("CompileJobId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
{
b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
.WithOne("RepositorySettings")
.HasForeignKey("Tgstation.Server.Host.Models.RepositorySettings", "InstanceId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
{
b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
.WithMany("ActiveTestMerges")
.HasForeignKey("RevisionInformationId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Tgstation.Server.Host.Models.TestMerge", "TestMerge")
.WithMany("RevisonInformations")
.HasForeignKey("TestMergeId")
.OnDelete(DeleteBehavior.ClientNoAction)
.IsRequired();
});
modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
{
b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
.WithMany("RevisionInformations")
.HasForeignKey("InstanceId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
{
b.HasOne("Tgstation.Server.Host.Models.User", "MergedBy")
.WithMany("TestMerges")
.HasForeignKey("MergedById")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "PrimaryRevisionInformation")
.WithOne("PrimaryTestMerge")
.HasForeignKey("Tgstation.Server.Host.Models.TestMerge", "PrimaryRevisionInformationId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
{
b.HasOne("Tgstation.Server.Host.Models.User", "CreatedBy")
.WithMany("CreatedUsers")
.HasForeignKey("CreatedById");
b.HasOne("Tgstation.Server.Host.Models.UserGroup", "Group")
.WithMany("Users")
.HasForeignKey("GroupId");
});
#pragma warning restore 612, 618
}
}
}
@@ -0,0 +1,35 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
namespace Tgstation.Server.Host.Database.Migrations
{
/// <summary>
/// Add the Timestamp column to RevisionInformations for PostgresSQL.
/// </summary>
public partial class PGAddRevInfoTimestamp : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
if (migrationBuilder == null)
throw new ArgumentNullException(nameof(migrationBuilder));
migrationBuilder.AddColumn<DateTimeOffset>(
name: "Timestamp",
table: "RevisionInformations",
nullable: false,
defaultValue: new DateTimeOffset(new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)));
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
if (migrationBuilder == null)
throw new ArgumentNullException(nameof(migrationBuilder));
migrationBuilder.DropColumn(
name: "Timestamp",
table: "RevisionInformations");
}
}
}
@@ -0,0 +1,885 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
namespace Tgstation.Server.Host.Database.Migrations
{
[DbContext(typeof(SqliteDatabaseContext))]
[Migration("20210112154243_SLAddRevInfoTimestamp")]
partial class SLAddRevInfoTimestamp
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "3.1.10");
modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<ushort?>("ChannelLimit")
.IsRequired()
.HasColumnType("INTEGER");
b.Property<string>("ConnectionString")
.IsRequired()
.HasColumnType("TEXT")
.HasMaxLength(10000);
b.Property<bool?>("Enabled")
.HasColumnType("INTEGER");
b.Property<long>("InstanceId")
.HasColumnType("INTEGER");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT")
.HasMaxLength(100);
b.Property<int>("Provider")
.HasColumnType("INTEGER");
b.Property<uint?>("ReconnectionInterval")
.IsRequired()
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("InstanceId", "Name")
.IsUnique();
b.ToTable("ChatBots");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<long>("ChatSettingsId")
.HasColumnType("INTEGER");
b.Property<ulong?>("DiscordChannelId")
.HasColumnType("INTEGER");
b.Property<string>("IrcChannel")
.HasColumnType("TEXT")
.HasMaxLength(100);
b.Property<bool?>("IsAdminChannel")
.IsRequired()
.HasColumnType("INTEGER");
b.Property<bool?>("IsUpdatesChannel")
.IsRequired()
.HasColumnType("INTEGER");
b.Property<bool?>("IsWatchdogChannel")
.IsRequired()
.HasColumnType("INTEGER");
b.Property<string>("Tag")
.HasColumnType("TEXT")
.HasMaxLength(10000);
b.HasKey("Id");
b.HasIndex("ChatSettingsId", "DiscordChannelId")
.IsUnique();
b.HasIndex("ChatSettingsId", "IrcChannel")
.IsUnique();
b.ToTable("ChatChannels");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ByondVersion")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int?>("DMApiMajorVersion")
.HasColumnType("INTEGER");
b.Property<int?>("DMApiMinorVersion")
.HasColumnType("INTEGER");
b.Property<int?>("DMApiPatchVersion")
.HasColumnType("INTEGER");
b.Property<Guid?>("DirectoryName")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("DmeName")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int?>("GitHubDeploymentId")
.HasColumnType("INTEGER");
b.Property<long?>("GitHubRepoId")
.HasColumnType("INTEGER");
b.Property<long>("JobId")
.HasColumnType("INTEGER");
b.Property<int?>("MinimumSecurityLevel")
.HasColumnType("INTEGER");
b.Property<string>("Output")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("RepositoryOrigin")
.HasColumnType("TEXT");
b.Property<long>("RevisionInformationId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("DirectoryName");
b.HasIndex("JobId")
.IsUnique();
b.HasIndex("RevisionInformationId");
b.ToTable("CompileJobs");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("AdditionalParameters")
.IsRequired()
.HasColumnType("TEXT")
.HasMaxLength(10000);
b.Property<bool?>("AllowWebClient")
.IsRequired()
.HasColumnType("INTEGER");
b.Property<bool?>("AutoStart")
.IsRequired()
.HasColumnType("INTEGER");
b.Property<uint?>("HeartbeatSeconds")
.IsRequired()
.HasColumnType("INTEGER");
b.Property<long>("InstanceId")
.HasColumnType("INTEGER");
b.Property<ushort?>("Port")
.IsRequired()
.HasColumnType("INTEGER");
b.Property<int>("SecurityLevel")
.HasColumnType("INTEGER");
b.Property<uint?>("StartupTimeout")
.IsRequired()
.HasColumnType("INTEGER");
b.Property<uint?>("TopicRequestTimeout")
.IsRequired()
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("InstanceId")
.IsUnique();
b.ToTable("DreamDaemonSettings");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<ushort?>("ApiValidationPort")
.IsRequired()
.HasColumnType("INTEGER");
b.Property<int>("ApiValidationSecurityLevel")
.HasColumnType("INTEGER");
b.Property<long>("InstanceId")
.HasColumnType("INTEGER");
b.Property<string>("ProjectName")
.HasColumnType("TEXT")
.HasMaxLength(10000);
b.Property<bool?>("RequireDMApiValidation")
.IsRequired()
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("InstanceId")
.IsUnique();
b.ToTable("DreamMakerSettings");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<uint?>("AutoUpdateInterval")
.IsRequired()
.HasColumnType("INTEGER");
b.Property<ushort?>("ChatBotLimit")
.IsRequired()
.HasColumnType("INTEGER");
b.Property<int>("ConfigurationType")
.HasColumnType("INTEGER");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT")
.HasMaxLength(10000);
b.Property<bool?>("Online")
.IsRequired()
.HasColumnType("INTEGER");
b.Property<string>("Path")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("SwarmIdentifer")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("Path", "SwarmIdentifer")
.IsUnique();
b.ToTable("Instances");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.InstancePermissionSet", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<ulong>("ByondRights")
.HasColumnType("INTEGER");
b.Property<ulong>("ChatBotRights")
.HasColumnType("INTEGER");
b.Property<ulong>("ConfigurationRights")
.HasColumnType("INTEGER");
b.Property<ulong>("DreamDaemonRights")
.HasColumnType("INTEGER");
b.Property<ulong>("DreamMakerRights")
.HasColumnType("INTEGER");
b.Property<long>("InstanceId")
.HasColumnType("INTEGER");
b.Property<ulong>("InstancePermissionSetRights")
.HasColumnType("INTEGER");
b.Property<long>("PermissionSetId")
.HasColumnType("INTEGER");
b.Property<ulong>("RepositoryRights")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("InstanceId");
b.HasIndex("PermissionSetId", "InstanceId")
.IsUnique();
b.ToTable("InstancePermissionSets");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<ulong?>("CancelRight")
.HasColumnType("INTEGER");
b.Property<ulong?>("CancelRightsType")
.HasColumnType("INTEGER");
b.Property<bool?>("Cancelled")
.IsRequired()
.HasColumnType("INTEGER");
b.Property<long?>("CancelledById")
.HasColumnType("INTEGER");
b.Property<string>("Description")
.IsRequired()
.HasColumnType("TEXT");
b.Property<uint?>("ErrorCode")
.HasColumnType("INTEGER");
b.Property<string>("ExceptionDetails")
.HasColumnType("TEXT");
b.Property<long>("InstanceId")
.HasColumnType("INTEGER");
b.Property<DateTimeOffset?>("StartedAt")
.IsRequired()
.HasColumnType("TEXT");
b.Property<long>("StartedById")
.HasColumnType("INTEGER");
b.Property<DateTimeOffset?>("StoppedAt")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("CancelledById");
b.HasIndex("InstanceId");
b.HasIndex("StartedById");
b.ToTable("Jobs");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.OAuthConnection", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ExternalUserId")
.IsRequired()
.HasColumnType("TEXT")
.HasMaxLength(100);
b.Property<int>("Provider")
.HasColumnType("INTEGER");
b.Property<long?>("UserId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("UserId");
b.HasIndex("Provider", "ExternalUserId")
.IsUnique();
b.ToTable("OAuthConnections");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.PermissionSet", b =>
{
b.Property<long?>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<ulong>("AdministrationRights")
.HasColumnType("INTEGER");
b.Property<long?>("GroupId")
.HasColumnType("INTEGER");
b.Property<ulong>("InstanceManagerRights")
.HasColumnType("INTEGER");
b.Property<long?>("UserId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("GroupId")
.IsUnique();
b.HasIndex("UserId")
.IsUnique();
b.ToTable("PermissionSets");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("AccessIdentifier")
.IsRequired()
.HasColumnType("TEXT");
b.Property<long>("CompileJobId")
.HasColumnType("INTEGER");
b.Property<int>("LaunchSecurityLevel")
.HasColumnType("INTEGER");
b.Property<ushort>("Port")
.HasColumnType("INTEGER");
b.Property<int>("ProcessId")
.HasColumnType("INTEGER");
b.Property<int>("RebootState")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("CompileJobId");
b.ToTable("ReattachInformations");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("AccessToken")
.HasColumnType("TEXT")
.HasMaxLength(10000);
b.Property<string>("AccessUser")
.HasColumnType("TEXT")
.HasMaxLength(10000);
b.Property<bool?>("AutoUpdatesKeepTestMerges")
.IsRequired()
.HasColumnType("INTEGER");
b.Property<bool?>("AutoUpdatesSynchronize")
.IsRequired()
.HasColumnType("INTEGER");
b.Property<string>("CommitterEmail")
.IsRequired()
.HasColumnType("TEXT")
.HasMaxLength(10000);
b.Property<string>("CommitterName")
.IsRequired()
.HasColumnType("TEXT")
.HasMaxLength(10000);
b.Property<bool?>("CreateGitHubDeployments")
.IsRequired()
.HasColumnType("INTEGER");
b.Property<long>("InstanceId")
.HasColumnType("INTEGER");
b.Property<bool?>("PostTestMergeComment")
.IsRequired()
.HasColumnType("INTEGER");
b.Property<bool?>("PushTestMergeCommits")
.IsRequired()
.HasColumnType("INTEGER");
b.Property<bool?>("ShowTestMergeCommitters")
.IsRequired()
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("InstanceId")
.IsUnique();
b.ToTable("RepositorySettings");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<long>("RevisionInformationId")
.HasColumnType("INTEGER");
b.Property<long>("TestMergeId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("RevisionInformationId");
b.HasIndex("TestMergeId");
b.ToTable("RevInfoTestMerges");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("CommitSha")
.IsRequired()
.HasColumnType("TEXT")
.HasMaxLength(40);
b.Property<long>("InstanceId")
.HasColumnType("INTEGER");
b.Property<string>("OriginCommitSha")
.IsRequired()
.HasColumnType("TEXT")
.HasMaxLength(40);
b.Property<DateTimeOffset>("Timestamp")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("InstanceId", "CommitSha")
.IsUnique();
b.ToTable("RevisionInformations");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Author")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("BodyAtMerge")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Comment")
.HasColumnType("TEXT")
.HasMaxLength(10000);
b.Property<DateTimeOffset>("MergedAt")
.HasColumnType("TEXT");
b.Property<long>("MergedById")
.HasColumnType("INTEGER");
b.Property<int>("Number")
.HasColumnType("INTEGER");
b.Property<long?>("PrimaryRevisionInformationId")
.IsRequired()
.HasColumnType("INTEGER");
b.Property<string>("TargetCommitSha")
.IsRequired()
.HasColumnType("TEXT")
.HasMaxLength(40);
b.Property<string>("TitleAtMerge")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Url")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("MergedById");
b.HasIndex("PrimaryRevisionInformationId")
.IsUnique();
b.ToTable("TestMerges");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
{
b.Property<long?>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("CanonicalName")
.IsRequired()
.HasColumnType("TEXT")
.HasMaxLength(100);
b.Property<DateTimeOffset?>("CreatedAt")
.IsRequired()
.HasColumnType("TEXT");
b.Property<long?>("CreatedById")
.HasColumnType("INTEGER");
b.Property<bool?>("Enabled")
.IsRequired()
.HasColumnType("INTEGER");
b.Property<long?>("GroupId")
.HasColumnType("INTEGER");
b.Property<DateTimeOffset?>("LastPasswordUpdate")
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT")
.HasMaxLength(100);
b.Property<string>("PasswordHash")
.HasColumnType("TEXT");
b.Property<string>("SystemIdentifier")
.HasColumnType("TEXT")
.HasMaxLength(100);
b.HasKey("Id");
b.HasIndex("CanonicalName")
.IsUnique();
b.HasIndex("CreatedById");
b.HasIndex("GroupId");
b.HasIndex("SystemIdentifier")
.IsUnique();
b.ToTable("Users");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.UserGroup", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT")
.HasMaxLength(100);
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("Groups");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
{
b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
.WithMany("ChatSettings")
.HasForeignKey("InstanceId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
{
b.HasOne("Tgstation.Server.Host.Models.ChatBot", "ChatSettings")
.WithMany("Channels")
.HasForeignKey("ChatSettingsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
{
b.HasOne("Tgstation.Server.Host.Models.Job", "Job")
.WithOne()
.HasForeignKey("Tgstation.Server.Host.Models.CompileJob", "JobId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
.WithMany("CompileJobs")
.HasForeignKey("RevisionInformationId")
.OnDelete(DeleteBehavior.ClientNoAction)
.IsRequired();
});
modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
{
b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
.WithOne("DreamDaemonSettings")
.HasForeignKey("Tgstation.Server.Host.Models.DreamDaemonSettings", "InstanceId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
{
b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
.WithOne("DreamMakerSettings")
.HasForeignKey("Tgstation.Server.Host.Models.DreamMakerSettings", "InstanceId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Tgstation.Server.Host.Models.InstancePermissionSet", b =>
{
b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
.WithMany("InstancePermissionSets")
.HasForeignKey("InstanceId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Tgstation.Server.Host.Models.PermissionSet", "PermissionSet")
.WithMany("InstancePermissionSets")
.HasForeignKey("PermissionSetId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
{
b.HasOne("Tgstation.Server.Host.Models.User", "CancelledBy")
.WithMany()
.HasForeignKey("CancelledById");
b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
.WithMany("Jobs")
.HasForeignKey("InstanceId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Tgstation.Server.Host.Models.User", "StartedBy")
.WithMany()
.HasForeignKey("StartedById")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Tgstation.Server.Host.Models.OAuthConnection", b =>
{
b.HasOne("Tgstation.Server.Host.Models.User", "User")
.WithMany("OAuthConnections")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Tgstation.Server.Host.Models.PermissionSet", b =>
{
b.HasOne("Tgstation.Server.Host.Models.UserGroup", "Group")
.WithOne("PermissionSet")
.HasForeignKey("Tgstation.Server.Host.Models.PermissionSet", "GroupId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("Tgstation.Server.Host.Models.User", "User")
.WithOne("PermissionSet")
.HasForeignKey("Tgstation.Server.Host.Models.PermissionSet", "UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
{
b.HasOne("Tgstation.Server.Host.Models.CompileJob", "CompileJob")
.WithMany()
.HasForeignKey("CompileJobId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
{
b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
.WithOne("RepositorySettings")
.HasForeignKey("Tgstation.Server.Host.Models.RepositorySettings", "InstanceId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
{
b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
.WithMany("ActiveTestMerges")
.HasForeignKey("RevisionInformationId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Tgstation.Server.Host.Models.TestMerge", "TestMerge")
.WithMany("RevisonInformations")
.HasForeignKey("TestMergeId")
.OnDelete(DeleteBehavior.ClientNoAction)
.IsRequired();
});
modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
{
b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
.WithMany("RevisionInformations")
.HasForeignKey("InstanceId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
{
b.HasOne("Tgstation.Server.Host.Models.User", "MergedBy")
.WithMany("TestMerges")
.HasForeignKey("MergedById")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "PrimaryRevisionInformation")
.WithOne("PrimaryTestMerge")
.HasForeignKey("Tgstation.Server.Host.Models.TestMerge", "PrimaryRevisionInformationId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
{
b.HasOne("Tgstation.Server.Host.Models.User", "CreatedBy")
.WithMany("CreatedUsers")
.HasForeignKey("CreatedById");
b.HasOne("Tgstation.Server.Host.Models.UserGroup", "Group")
.WithMany("Users")
.HasForeignKey("GroupId");
});
#pragma warning restore 612, 618
}
}
}
@@ -0,0 +1,69 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
namespace Tgstation.Server.Host.Database.Migrations
{
/// <summary>
/// Add the Timestamp column to RevisionInformations for SQLite.
/// </summary>
public partial class SLAddRevInfoTimestamp : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
if (migrationBuilder == null)
throw new ArgumentNullException(nameof(migrationBuilder));
migrationBuilder.AddColumn<DateTimeOffset>(
name: "Timestamp",
table: "RevisionInformations",
nullable: false,
defaultValue: new DateTimeOffset(new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)));
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
if (migrationBuilder == null)
throw new ArgumentNullException(nameof(migrationBuilder));
migrationBuilder.RenameTable(
name: "RevisionInformations",
newName: "RevisionInformations_down");
migrationBuilder.CreateTable(
name: "RevisionInformations",
columns: table => new
{
Id = table.Column<long>(nullable: false)
.Annotation("Sqlite:Autoincrement", true),
CommitSha = table.Column<string>(maxLength: 40, nullable: false),
OriginCommitSha = table.Column<string>(maxLength: 40, nullable: false),
InstanceId = table.Column<long>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_RevisionInformations", x => x.Id);
table.ForeignKey(
name: "FK_RevisionInformations_Instances_InstanceId",
column: x => x.InstanceId,
principalTable: "Instances",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.Sql("INSERT INTO RevisionInformations (Id, CommitSha, OriginCommitSha, InstanceId) SELECT Id, CommitSha, OriginCommitSha, InstanceId FROM RevisionInformations_down");
migrationBuilder.DropTable(
name: "RevisionInformations_down");
migrationBuilder.RenameTable(
name: "RevisionInformations",
newName: "RevisionInformations_down");
migrationBuilder.RenameTable(
name: "RevisionInformations_down",
newName: "RevisionInformations");
}
}
}
@@ -566,6 +566,9 @@ namespace Tgstation.Server.Host.Database.Migrations
.HasColumnType("varchar(40) CHARACTER SET utf8mb4")
.HasMaxLength(40);
b.Property<DateTimeOffset>("Timestamp")
.HasColumnType("datetime(6)");
b.HasKey("Id");
b.HasIndex("InstanceId", "CommitSha")
@@ -573,6 +573,9 @@ namespace Tgstation.Server.Host.Database.Migrations
.HasColumnType("character varying(40)")
.HasMaxLength(40);
b.Property<DateTimeOffset>("Timestamp")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.HasIndex("InstanceId", "CommitSha")
@@ -578,6 +578,9 @@ namespace Tgstation.Server.Host.Database.Migrations
.HasColumnType("nvarchar(40)")
.HasMaxLength(40);
b.Property<DateTimeOffset>("Timestamp")
.HasColumnType("datetimeoffset");
b.HasKey("Id");
b.HasIndex("InstanceId", "CommitSha")
@@ -565,6 +565,9 @@ namespace Tgstation.Server.Host.Database.Migrations
.HasColumnType("TEXT")
.HasMaxLength(40);
b.Property<DateTimeOffset>("Timestamp")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("InstanceId", "CommitSha")
@@ -1,4 +1,4 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Pomelo.EntityFrameworkCore.MySql.Infrastructure;
using System;
using Tgstation.Server.Host.Configuration;
@@ -1,14 +1,35 @@
using Newtonsoft.Json;
using System;
using Tgstation.Server.Api;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
namespace Tgstation.Server.Host.Extensions.Converters
{
/// <summary>
/// <see cref="JsonConverter"/> for serializing <see cref="Version"/>s for BYOND.
/// <see cref="JsonConverter"/> and <see cref="IYamlTypeConverter"/> for serializing <see cref="global::System.Version"/>s for BYOND.
/// </summary>
sealed class VersionConverter : JsonConverter
sealed class VersionConverter : JsonConverter, IYamlTypeConverter
{
/// <summary>
/// Check if the <see cref="VersionConverter"/> supports (de)serializing a given <paramref name="type"/>.
/// </summary>
/// <param name="type">The <see cref="Type"/> to check.</param>
/// <param name="validate">If the method should <see langword="throw"/> if validation fails.</param>
/// <returns><see langword="true"/> if <paramref name="type"/> is a <see cref="global::System.Version"/>, <see langword="false"/> otherwise.</returns>
static bool CheckSupportsType(Type type, bool validate)
{
if (type == null)
throw new ArgumentNullException(nameof(type));
var supported = type == typeof(global::System.Version);
if (!supported && validate)
throw new NotSupportedException($"{nameof(VersionConverter)} does not convert {type}s!");
return supported;
}
/// <inheritdoc />
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
@@ -16,7 +37,7 @@ namespace Tgstation.Server.Host.Extensions.Converters
{
writer.WriteNull();
}
else if (value is Version version)
else if (value is global::System.Version version)
{
writer.WriteValue(version.Semver().ToString());
}
@@ -29,6 +50,11 @@ namespace Tgstation.Server.Host.Extensions.Converters
/// <inheritdoc />
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader == null)
throw new ArgumentNullException(nameof(reader));
CheckSupportsType(objectType, true);
if (reader.TokenType == JsonToken.Null)
return null;
@@ -36,7 +62,7 @@ namespace Tgstation.Server.Host.Extensions.Converters
{
try
{
Version v = new Version((string)reader.Value);
var v = global::System.Version.Parse((string)reader.Value);
return v.Semver();
}
catch (Exception ex)
@@ -50,6 +76,40 @@ namespace Tgstation.Server.Host.Extensions.Converters
}
/// <inheritdoc />
public override bool CanConvert(Type objectType) => objectType == typeof(Version);
public override bool CanConvert(Type objectType) => CheckSupportsType(objectType, false);
/// <inheritdoc />
public bool Accepts(Type type) => CheckSupportsType(type, false);
/// <inheritdoc />
public object ReadYaml(IParser parser, Type type)
{
if (parser == null)
throw new ArgumentNullException(nameof(parser));
CheckSupportsType(type, true);
var scalar = parser.Consume<Scalar>();
if (scalar == null)
return null;
return global::System.Version.Parse(scalar.Value);
}
/// <inheritdoc />
public void WriteYaml(IEmitter emitter, object value, Type type)
{
if (emitter == null)
throw new ArgumentNullException(nameof(emitter));
CheckSupportsType(type, true);
var version = (global::System.Version)value;
emitter.Emit(
new Scalar(
version
?.Semver()
.ToString()));
}
}
}
@@ -42,6 +42,7 @@ namespace Tgstation.Server.Host.Models
public Api.Models.RevisionInformation ToApi() => new Api.Models.RevisionInformation
{
CommitSha = CommitSha,
Timestamp = Timestamp,
OriginCommitSha = OriginCommitSha,
PrimaryTestMerge = PrimaryTestMerge?.ToApi(),
ActiveTestMerges = ActiveTestMerges.Select(x => x.TestMerge.ToApi()).ToList(),
+2 -1
View File
@@ -5,6 +5,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Tgstation.Server.Host.Core;
using Tgstation.Server.Host.Properties;
using Tgstation.Server.Host.System;
namespace Tgstation.Server.Host
@@ -17,7 +18,7 @@ namespace Tgstation.Server.Host
/// <summary>
/// The expected host watchdog <see cref="Version"/>.
/// </summary>
internal static readonly Version HostWatchdogVersion = new Version(1, 1, 0);
internal static Version HostWatchdogVersion => Version.Parse(MasterVersionsAttribute.Instance.RawHostWatchdogVersion);
/// <summary>
/// The <see cref="IServerFactory"/> to use.
@@ -22,29 +22,37 @@ namespace Tgstation.Server.Host.Properties
public string RawConfigurationVersion { get; }
/// <summary>
/// The <see cref="Version"/> <see cref="string"/> of the DMAPI version built.
/// The <see cref="Version"/> <see cref="string"/> of the DMAPI interop version used.
/// </summary>
public string RawDMApiVersion { get; }
public string RawInteropVersion { get; }
/// <summary>
/// The <see cref="Version"/> <see cref="string"/> of the control panel version built.
/// </summary>
public string RawControlPanelVersion { get; }
/// <summary>
/// The <see cref="Version"/> <see cref="string"/> of the control panel version built.
/// </summary>
public string RawHostWatchdogVersion { get; }
/// <summary>
/// Initializes a new instance of the <see cref="MasterVersionsAttribute"/> <see langword="class"/>.
/// </summary>
/// <param name="rawConfigurationVersion">The value of <see cref="RawConfigurationVersion"/>.</param>
/// <param name="rawDMApiVersion">The value of <see cref="RawDMApiVersion"/>.</param>
/// <param name="rawInteropVersion">The value of <see cref="RawInteropVersion"/>.</param>
/// <param name="rawControlPanelVersion">The value of <see cref="RawControlPanelVersion"/>.</param>
/// <param name="rawHostWatchdogVersion">The value of <see cref="RawHostWatchdogVersion"/>.</param>
public MasterVersionsAttribute(
string rawConfigurationVersion,
string rawDMApiVersion,
string rawControlPanelVersion)
string rawInteropVersion,
string rawControlPanelVersion,
string rawHostWatchdogVersion)
{
RawConfigurationVersion = rawConfigurationVersion ?? throw new ArgumentNullException(nameof(rawConfigurationVersion));
RawDMApiVersion = rawDMApiVersion ?? throw new ArgumentNullException(nameof(rawDMApiVersion));
RawInteropVersion = rawInteropVersion ?? throw new ArgumentNullException(nameof(rawInteropVersion));
RawControlPanelVersion = rawControlPanelVersion ?? throw new ArgumentNullException(nameof(rawControlPanelVersion));
RawHostWatchdogVersion = rawHostWatchdogVersion ?? throw new ArgumentNullException(nameof(rawHostWatchdogVersion));
}
}
}
+1 -1
View File
@@ -25,7 +25,7 @@ Here's a breakdown of things in this directory
- [.config](./.config) contains the dotnet-tools.json. At the time of writing, this is only used to set the version of the [dotnet ef tools](https://docs.microsoft.com/en-us/ef/core/miscellaneous/cli/) used to create database migrations.
- [ClientApp](./ClientApp) contains scripts to build and deploy the web control panel with TGS.
- [Components](./Components) is where the bulk of the TGS implementation lives.
- [Configuration](./Configuration) contains classes that partly make up the configuration json files (i.e. [appsettings.json](./appsettings.json)).
- [Configuration](./Configuration) contains classes that partly make up the configuration yaml files (i.e. [appsettings.yml](./appsettings.yml)).
- [Controllers](./Controllers) is where HTTP API code lives and bridges it with component code.
- [Core](./Core) contains [Application.cs](./Core/Application.cs) and other various helpers that don't belong anywhere else.
- [Database](./Database) contains all database related code.
+19 -1
View File
@@ -49,7 +49,25 @@ namespace Tgstation.Server.Host
var basePath = IOManager.ResolvePath();
IHostBuilder CreateDefaultBuilder() => Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((context, configuration) => configuration.SetBasePath(basePath));
.ConfigureAppConfiguration((context, builder) =>
{
builder.SetBasePath(basePath);
builder.AddYamlFile("appsettings.yml", optional: true, reloadOnChange: true)
.AddYamlFile($"appsettings.{context.HostingEnvironment.EnvironmentName}.yml", optional: true, reloadOnChange: true);
// reorganize the builder so our yaml configs don't override the env/cmdline configs
// values obtained via debugger
var environmentJsonConfig = builder.Sources[2];
var envConfig = builder.Sources[3];
var cmdLineConfig = builder.Sources[4];
var baseYmlConfig = builder.Sources[5];
var environmentYmlConfig = builder.Sources[6];
builder.Sources[2] = baseYmlConfig;
builder.Sources[3] = environmentJsonConfig;
builder.Sources[4] = environmentYmlConfig;
builder.Sources[5] = envConfig;
builder.Sources[6] = cmdLineConfig;
});
var setupWizardHostBuilder = CreateDefaultBuilder()
.UseSetupApplication();
+12 -6
View File
@@ -4,7 +4,6 @@ using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using MySql.Data.MySqlClient;
using Newtonsoft.Json;
using Npgsql;
using System;
using System.Collections.Generic;
@@ -20,8 +19,10 @@ using System.Threading.Tasks;
using Tgstation.Server.Host.Configuration;
using Tgstation.Server.Host.Core;
using Tgstation.Server.Host.Database;
using Tgstation.Server.Host.Extensions.Converters;
using Tgstation.Server.Host.IO;
using Tgstation.Server.Host.System;
using YamlDotNet.Serialization;
namespace Tgstation.Server.Host.Setup
{
@@ -841,8 +842,13 @@ namespace Tgstation.Server.Host.Setup
{ SwarmConfiguration.Section, swarmConfiguration },
};
var json = JsonConvert.SerializeObject(map, Formatting.Indented);
var configBytes = Encoding.UTF8.GetBytes(json);
var serializer = new SerializerBuilder()
.WithTypeConverter(new VersionConverter())
.Build();
var serializedYaml = serializer.Serialize(map);
var configBytes = Encoding.UTF8.GetBytes(serializedYaml);
reloadTcs = new TaskCompletionSource<object>();
@@ -863,9 +869,9 @@ namespace Tgstation.Server.Host.Setup
{
await console.WriteAsync(e.Message, true, cancellationToken).ConfigureAwait(false);
await console.WriteAsync(null, true, cancellationToken).ConfigureAwait(false);
await console.WriteAsync("For your convienence, here's the json we tried to write out:", true, cancellationToken).ConfigureAwait(false);
await console.WriteAsync("For your convienence, here's the yaml we tried to write out:", true, cancellationToken).ConfigureAwait(false);
await console.WriteAsync(null, true, cancellationToken).ConfigureAwait(false);
await console.WriteAsync(json, true, cancellationToken).ConfigureAwait(false);
await console.WriteAsync(serializedYaml, true, cancellationToken).ConfigureAwait(false);
await console.WriteAsync(null, true, cancellationToken).ConfigureAwait(false);
await console.WriteAsync("Press any key to exit...", true, cancellationToken).ConfigureAwait(false);
await console.PressAnyKeyAsync(cancellationToken).ConfigureAwait(false);
@@ -930,7 +936,7 @@ namespace Tgstation.Server.Host.Setup
return;
}
var userConfigFileName = String.Format(CultureInfo.InvariantCulture, "appsettings.{0}.json", hostingEnvironment.EnvironmentName);
var userConfigFileName = String.Format(CultureInfo.InvariantCulture, "appsettings.{0}.yml", hostingEnvironment.EnvironmentName);
async Task HandleSetupCancel()
{
@@ -6,7 +6,7 @@ namespace Tgstation.Server.Host.Setup
public enum SetupWizardMode
{
/// <summary>
/// Run the wizard if the appsettings.{Environment}.json is not present or empty.
/// Run the wizard if the appsettings.{Environment}.yml is not present or empty.
/// </summary>
Autodetect,
@@ -49,8 +49,9 @@
<ItemGroup>
<AssemblyAttributes Include="Tgstation.Server.Host.Properties.MasterVersionsAttribute">
<_Parameter1>$(TgsConfigVersion)</_Parameter1>
<_Parameter2>$(TgsDmapiVersion)</_Parameter2>
<_Parameter2>$(TgsInteropVersion)</_Parameter2>
<_Parameter3>$(TgsControlPanelVersion)</_Parameter3>
<_Parameter4>$(TgsHostWatchdogVersion)</_Parameter4>
</AssemblyAttributes>
</ItemGroup>
@@ -83,6 +84,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.10" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.10.8" />
<PackageReference Include="Mono.Posix.NETStandard" Version="1.0.0" />
<PackageReference Include="NetEscapades.Configuration.Yaml" Version="2.1.0" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="3.1.4" />
<PackageReference Include="Octokit" Version="0.48.0" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="3.2.4" />
@@ -118,7 +120,11 @@
</ItemGroup>
<ItemGroup>
<Content Update="appsettings.json">
<Content Include="appsettings.yml" />
</ItemGroup>
<ItemGroup>
<Content Update="appsettings.yml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
@@ -1,66 +0,0 @@
{
"General": {
"MinimumPasswordLength": 15,
"GitHubAccessToken": null,
"SetupWizardMode": "AutoDetect",
"ByondTopicTimeout": 5000,
"RestartTimeout": 60000,
"ApiPort": 5000,
"UseBasicWatchdog": false,
"UserLimit": 100,
"UserGroupLimit": 25,
"InstanceLimit": 10,
"ValidInstancePaths": null,
"HostApiDocumentation": false
},
"FileLogging": {
"Directory": null,
"Disable": false,
"LogLevel": "Debug",
"MicrosoftLogLevel": "Warning"
},
"Logging": {
"IncludeScopes": false,
"Debug": {
"LogLevel": {
"Default": "Debug",
"Microsoft": "Information"
}
},
"Console": {
"LogLevel": {
"Default": "Trace",
"Microsoft": "Warning"
}
}
},
"ControlPanel": {
"Enable": false,
"AllowAnyOrigin": false,
"AllowedOrigins": []
},
"Updates": {
"GitHubRepositoryId": 92952846,
"GitTagPrefix": "tgstation-server-v",
"UpdatePackageAssetName": "ServerUpdatePackage.zip"
},
"Database": {
"DropDatabase": false,
"DatabaseType": "SqlServer",
"ResetAdminPassword": false,
"ServerVersion": null,
"ConnectionString": "Data Source=(local);Initial Catalog=TGS;Integrated Security=True"
},
"Security": {
"TokenExpiryMinutes": 15,
"TokenClockSkewMinutes": 1,
"TokenSigningKeyByteAmount": 256,
"CustomTokenSigningKeyBase64": null,
"OAuth": {
"GitHub": null,
"Discord": null,
"TGForums": null,
"Keycloak": null
}
}
}
+52
View File
@@ -0,0 +1,52 @@
General:
MinimumPasswordLength: 15
GitHubAccessToken:
SetupWizardMode: AutoDetect
ByondTopicTimeout: 5000
RestartTimeout: 60000
ApiPort: 5000
UseBasicWatchdog: false
UserLimit: 100
UserGroupLimit: 25
InstanceLimit: 10
ValidInstancePaths:
HostApiDocumentation: false
FileLogging:
Directory:
Disable: false
LogLevel: Debug
MicrosoftLogLevel: Warning
Logging:
IncludeScopes: false
Debug:
LogLevel:
Default: Debug
Microsoft: Information
Console:
LogLevel:
Default: Trace
Microsoft: Warning
ControlPanel:
Enable: false
AllowAnyOrigin: false
AllowedOrigins: []
Updates:
GitHubRepositoryId: 92952846
GitTagPrefix: tgstation-server-v
UpdatePackageAssetName: ServerUpdatePackage.zip
Database:
DropDatabase: false
DatabaseType: SqlServer
ResetAdminPassword: false
ServerVersion:
ConnectionString: Data Source=(local);Initial Catalog=TGS;Integrated Security=True
Security:
TokenExpiryMinutes: 15
TokenClockSkewMinutes: 1
TokenSigningKeyByteAmount: 256
CustomTokenSigningKeyBase64:
OAuth:
GitHub:
Discord:
TGForums:
Keycloak:
-7
View File
@@ -14,13 +14,6 @@
sleep(50)
world.TgsTargetedChatBroadcast("Sample admin-only message", TRUE)
world.log << "Validating API sleep"
// Validate TGS_DMAPI_VERSION against DMAPI version used
var/datum/tgs_version/active_version = world.TgsApiVersion()
var/datum/tgs_version/dmapi_version = new /datum/tgs_version(TGS_DMAPI_VERSION)
if(!active_version.Equals(dmapi_version))
text2file("DMAPI version [TGS_DMAPI_VERSION] does not match active API version [active_version.raw_parameter]", "test_fail_reason.txt")
var/list/world_params = params2list(world.params)
if(!("test" in world_params) || world_params["test"] != "bababooey")
text2file("Expected parameter test=bababooey but did not receive", "test_fail_reason.txt")
@@ -0,0 +1,52 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using Tgstation.Server.Api;
using YamlDotNet.Serialization;
namespace Tgstation.Server.Host.Extensions.Converters.Tests
{
[TestClass]
public sealed class TestVersionConverter
{
class TestObject
{
public Version Version { get; set; }
}
readonly Version testVersion;
readonly string testYaml;
public TestVersionConverter()
{
testVersion = new Version(1, 2, 3);
testYaml = $@"{nameof(TestObject.Version)}: {testVersion.Semver()}";
}
[TestMethod]
public void TestYamlSerialization()
{
var testObj = new TestObject
{
Version = testVersion
};
var serializedString = new SerializerBuilder()
.WithTypeConverter(new VersionConverter())
.Build()
.Serialize(testObj);
Assert.AreEqual(testYaml, serializedString.Trim());
}
[TestMethod]
public void TestYamlDeserialization()
{
var deserialized = new DeserializerBuilder()
.WithTypeConverter(new VersionConverter())
.Build()
.Deserialize<TestObject>(testYaml);
Assert.AreEqual(testVersion, deserialized.Version);
}
}
}
@@ -1,4 +1,4 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
@@ -16,7 +16,9 @@ namespace Tgstation.Server.Host.Extensions.Tests
class BadConfig1
{
#pragma warning disable IDE0051 // Remove unused private members
const string Section = "asdf";
#pragma warning restore IDE0051 // Remove unused private members
}
class BadConfig2
@@ -64,7 +64,7 @@ namespace Tgstation.Server.Tests.Instance
clone = await repositoryClient.Clone(initalRepo, cancellationToken).ConfigureAwait(false);
await WaitForJob(clone.ActiveJob, 900, false, null, cancellationToken).ConfigureAwait(false);
await WaitForJob(clone.ActiveJob, 9000, false, null, cancellationToken).ConfigureAwait(false);
var readAfterClone = await repositoryClient.Read(cancellationToken);
Assert.AreEqual(initalRepo.Origin, readAfterClone.Origin);
@@ -79,6 +79,7 @@ namespace Tgstation.Server.Tests.Instance
Assert.IsNotNull(readAfterClone.RevisionInformation.OriginCommitSha);
Assert.IsNull(readAfterClone.RevisionInformation.PrimaryTestMerge);
Assert.AreEqual(readAfterClone.RevisionInformation.CommitSha, readAfterClone.RevisionInformation.OriginCommitSha);
Assert.AreNotEqual(default, readAfterClone.RevisionInformation.Timestamp);
readAfterClone.Origin = new Uri("https://github.com/tgstation/tgstation");
await ApiAssert.ThrowsException<ApiConflictException>(() => repositoryClient.Update(readAfterClone, cancellationToken), ErrorCode.RepoCantChangeOrigin);
@@ -144,7 +144,7 @@ namespace Tgstation.Server.Tests.Instance
Assert.IsNotNull(newerCompileJob);
Assert.AreNotEqual(initialCompileJob.Id, newerCompileJob.Id);
Assert.AreEqual(DreamDaemonSecurity.Safe, newerCompileJob.MinimumSecurityLevel);
Assert.AreEqual(DMApiConstants.Version, daemonStatus.StagedCompileJob.DMApiVersion);
Assert.AreEqual(DMApiConstants.InteropVersion, daemonStatus.StagedCompileJob.DMApiVersion);
await instanceClient.DreamDaemon.Shutdown(cancellationToken);
}
@@ -162,7 +162,7 @@ namespace Tgstation.Server.Tests.Instance
Assert.AreEqual(WatchdogStatus.Offline, daemonStatus.Status.Value);
Assert.IsNotNull(daemonStatus.ActiveCompileJob);
Assert.IsNull(daemonStatus.StagedCompileJob);
Assert.AreEqual(DMApiConstants.Version, daemonStatus.ActiveCompileJob.DMApiVersion);
Assert.AreEqual(DMApiConstants.InteropVersion, daemonStatus.ActiveCompileJob.DMApiVersion);
Assert.AreEqual(DreamDaemonSecurity.Safe, daemonStatus.ActiveCompileJob.MinimumSecurityLevel);
Job startJob;
@@ -307,7 +307,7 @@ namespace Tgstation.Server.Tests.Instance
Assert.AreEqual(WatchdogStatus.Offline, daemonStatus.Status.Value);
Assert.IsNotNull(daemonStatus.ActiveCompileJob);
Assert.IsNull(daemonStatus.StagedCompileJob);
Assert.AreEqual(DMApiConstants.Version, daemonStatus.ActiveCompileJob.DMApiVersion);
Assert.AreEqual(DMApiConstants.InteropVersion, daemonStatus.ActiveCompileJob.DMApiVersion);
Assert.AreEqual(DreamDaemonSecurity.Ultrasafe, daemonStatus.ActiveCompileJob.MinimumSecurityLevel);
var startJob = await StartDD(cancellationToken).ConfigureAwait(false);
@@ -347,7 +347,7 @@ namespace Tgstation.Server.Tests.Instance
Assert.AreEqual(WatchdogStatus.Offline, daemonStatus.Status.Value);
Assert.IsNotNull(daemonStatus.ActiveCompileJob);
Assert.IsNull(daemonStatus.StagedCompileJob);
Assert.AreEqual(DMApiConstants.Version, daemonStatus.ActiveCompileJob.DMApiVersion);
Assert.AreEqual(DMApiConstants.InteropVersion, daemonStatus.ActiveCompileJob.DMApiVersion);
Assert.AreEqual(DreamDaemonSecurity.Ultrasafe, daemonStatus.ActiveCompileJob.MinimumSecurityLevel);
var startJob = await StartDD(cancellationToken).ConfigureAwait(false);
@@ -110,7 +110,7 @@ namespace Tgstation.Server.Tests
args.Add($"Security:OAuth=null");
// SPECIFICALLY DELETE THE DEV APPSETTINGS, WE DON'T WANT IT IN THE WAY
File.Delete("appsettings.Development.json");
File.Delete("appsettings.Development.yml");
if (!String.IsNullOrEmpty(gitHubAccessToken))
args.Add(String.Format(CultureInfo.InvariantCulture, "General:GitHubAccessToken={0}", gitHubAccessToken));
+9 -1
View File
@@ -99,7 +99,15 @@ namespace Tgstation.Server.Tests
Assert.IsTrue(Version.TryParse(versionLine, out var actual));
Assert.AreEqual(expected, actual);
Assert.AreEqual(expected, DMApiConstants.Version);
}
[TestMethod]
public void TestInteropVersion()
{
var versionString = versionsPropertyGroup.Element(xmlNamespace + "TgsInteropVersion").Value;
Assert.IsNotNull(versionString);
Assert.IsTrue(Version.TryParse(versionString, out var expected));
Assert.AreEqual(expected, DMApiConstants.InteropVersion);
}
[TestMethod]
+2
View File
@@ -65,6 +65,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tgs", "tgs", "{F7765A4B-021
src\DMAPI\tgs\includes.dm = src\DMAPI\tgs\includes.dm
src\DMAPI\tgs\LICENSE = src\DMAPI\tgs\LICENSE
src\DMAPI\tgs\README.md = src\DMAPI\tgs\README.md
src\DMAPI\tgs.dm = src\DMAPI\tgs.dm
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "core", "core", "{DCCBA9DA-47BA-4C70-823B-E99A3ACA0377}"
@@ -134,6 +135,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "v5", "v5", "{FAEAD3B5-2EAB-
src\DMAPI\tgs\v5\_defines.dm = src\DMAPI\tgs\v5\_defines.dm
src\DMAPI\tgs\v5\api.dm = src\DMAPI\tgs\v5\api.dm
src\DMAPI\tgs\v5\commands.dm = src\DMAPI\tgs\v5\commands.dm
src\DMAPI\tgs\v5\interop_version.dm = src\DMAPI\tgs\v5\interop_version.dm
src\DMAPI\tgs\v5\README.md = src\DMAPI\tgs\v5\README.md
src\DMAPI\tgs\v5\undefs.dm = src\DMAPI\tgs\v5\undefs.dm
EndProjectSection
+7 -3
View File
@@ -72,7 +72,7 @@ namespace ReleaseNotes
Task<Milestone> milestoneTask = null;
var milestoneTaskLock = new object();
var releaseDictionary = new Dictionary<string, List<Tuple<string, int>>>(StringComparer.OrdinalIgnoreCase);
var releaseDictionary = new Dictionary<string, List<Tuple<string, int, string>>>(StringComparer.OrdinalIgnoreCase);
var authorizedUsers = new Dictionary<long, Task<bool>>();
bool postControlPanelMessage = false;
@@ -136,7 +136,7 @@ namespace ReleaseNotes
foreach (var I in notes)
Console.WriteLine(component + " #" + fullPR.Number + " - " + I + " (@" + user.Login + ")");
var tupleSelector = notes.Select(note => Tuple.Create(note, fullPR.Number));
var tupleSelector = notes.Select(note => Tuple.Create(note, fullPR.Number, user.Login));
if (releaseDictionary.TryGetValue(component, out var currentValues))
currentValues.AddRange(tupleSelector);
else
@@ -169,6 +169,7 @@ namespace ReleaseNotes
}
if (trimmedLine.Length == 0)
continue;
notes.Add(trimmedLine);
}
}
@@ -255,13 +256,14 @@ namespace ReleaseNotes
var apiVersion = Version.Parse(versionsPropertyGroup.Element(xmlNamespace + "TgsApiVersion").Value);
var configVersion = Version.Parse(versionsPropertyGroup.Element(xmlNamespace + "TgsConfigVersion").Value);
var dmApiVersion = Version.Parse(versionsPropertyGroup.Element(xmlNamespace + "TgsDmapiVersion").Value);
var interopVersion = Version.Parse(versionsPropertyGroup.Element(xmlNamespace + "TgsInteropVersion").Value);
var webControlVersion = Version.Parse(controlPanelVersionsPropertyGroup.Element(controlPanelXmlNamespace + "TgsControlPanelVersion").Value);
var hostWatchdogVersion = Version.Parse(versionsPropertyGroup.Element(xmlNamespace + "TgsHostWatchdogVersion").Value);
if (webControlVersion.Major == 0)
postControlPanelMessage = true;
prefix = $"Please refer to the [README](https://github.com/tgstation/tgstation-server#setup) for setup instructions.{Environment.NewLine}{Environment.NewLine}#### Component Versions\nCore: {coreVersion}\nConfiguration: {configVersion}\nHTTP API: {apiVersion}\nDreamMaker API: {dmApiVersion}\n[Web Control Panel](https://github.com/tgstation/tgstation-server-control-panel): {webControlVersion}\nHost Watchdog: {hostWatchdogVersion}";
prefix = $"Please refer to the [README](https://github.com/tgstation/tgstation-server#setup) for setup instructions.{Environment.NewLine}{Environment.NewLine}#### Component Versions\nCore: {coreVersion}\nConfiguration: {configVersion}\nHTTP API: {apiVersion}\nDreamMaker API: {dmApiVersion} (Interop: {interopVersion})\n[Web Control Panel](https://github.com/tgstation/tgstation-server-control-panel): {webControlVersion}\nHost Watchdog: {hostWatchdogVersion}";
break;
case 3:
prefix = "The /tg/station server suite";
@@ -325,6 +327,8 @@ namespace ReleaseNotes
newNotes.Append(noteTuple.Item1);
newNotes.Append(" (#");
newNotes.Append(noteTuple.Item2);
newNotes.Append(" @");
newNotes.Append(noteTuple.Item3);
newNotes.Append(')');
}