mirror of
https://github.com/tgstation/tgstation-server.git
synced 2026-08-22 12:37:24 +01:00
Merge pull request #1824 from tgstation/WowForgorTheRead [TGSDeploy]
v6.6.0: Fix cron schedule handling in API. Update Webpanel to 5.8.0
This commit is contained in:
@@ -16,7 +16,7 @@ async function getFailedJobsForRun(github, context, workflowRunId, runAttempt) {
|
||||
attempt_number: runAttempt
|
||||
},
|
||||
response => {
|
||||
return response.data.jobs;
|
||||
return response.data;
|
||||
});
|
||||
|
||||
return jobs
|
||||
|
||||
@@ -12,7 +12,11 @@ This is a toolset to manage production DreamMaker servers. It includes the abili
|
||||
|
||||
### Pre-Requisites
|
||||
|
||||
_Note: If you opt to use the Windows installer, all pre-requisites for running BYOND servers (including MariaDB) are provided out of the box. If you wish to use OpenDream you will need to install the required dotnet SDK manually._
|
||||
_Note: If you opt to use the Windows installer, most pre-requisites for running BYOND servers (including MariaDB) are provided out of the box._
|
||||
|
||||
_If you are running on a Windows Server OS. You **might** need to install the [x86 Visual C++ 2015 Runtime](https://aka.ms/vs/17/release/vc_redist.x86.exe) to run BYOND._
|
||||
|
||||
_If you wish to use OpenDream you will need to install the required dotnet SDK manually._
|
||||
|
||||
tgstation-server needs a relational database to store it's data.
|
||||
|
||||
|
||||
@@ -8,19 +8,19 @@
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<!-- Usage: Logging specific for GitHub actions -->
|
||||
<PackageReference Include="GitHubActionsTestLogger" Version="2.3.3">
|
||||
<PackageReference Include="GitHubActionsTestLogger" Version="2.4.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<!-- Usage: Hard to say what exactly this is for, but not including it removes the test icon and breaks vstest.console.exe for some reason -->
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" Condition="'$(TgsTestNoSdk)' != 'true'" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" Condition="'$(TgsTestNoSdk)' != 'true'" />
|
||||
<!-- Usage: Dependency mocking for tests -->
|
||||
<!-- Pinned: Be VERY careful about updating https://github.com/moq/moq/issues/1372 -->
|
||||
<PackageReference Include="Moq" Version="4.20.70" />
|
||||
<!-- Usage: MSTest execution -->
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="3.3.1" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="3.4.3" />
|
||||
<!-- Usage: MSTest asserts etc... -->
|
||||
<PackageReference Include="MSTest.TestFramework" Version="3.3.1" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="3.4.3" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
+8
-6
@@ -3,12 +3,12 @@
|
||||
<!-- Integration tests will ensure they match across the board -->
|
||||
<Import Project="WebpanelVersion.props" />
|
||||
<PropertyGroup>
|
||||
<TgsCoreVersion>6.5.0</TgsCoreVersion>
|
||||
<TgsCoreVersion>6.6.0</TgsCoreVersion>
|
||||
<TgsConfigVersion>5.1.0</TgsConfigVersion>
|
||||
<TgsApiVersion>10.3.0</TgsApiVersion>
|
||||
<TgsApiVersion>10.4.0</TgsApiVersion>
|
||||
<TgsCommonLibraryVersion>7.0.0</TgsCommonLibraryVersion>
|
||||
<TgsApiLibraryVersion>13.3.0</TgsApiLibraryVersion>
|
||||
<TgsClientVersion>15.3.0</TgsClientVersion>
|
||||
<TgsApiLibraryVersion>13.4.0</TgsApiLibraryVersion>
|
||||
<TgsClientVersion>15.4.0</TgsClientVersion>
|
||||
<TgsDmapiVersion>7.1.2</TgsDmapiVersion>
|
||||
<TgsInteropVersion>5.9.0</TgsInteropVersion>
|
||||
<TgsHostWatchdogVersion>1.4.1</TgsHostWatchdogVersion>
|
||||
@@ -17,8 +17,10 @@
|
||||
<TgsNugetNetFramework>netstandard2.0</TgsNugetNetFramework>
|
||||
<TgsNetMajorVersion>8</TgsNetMajorVersion>
|
||||
<!-- Update this frequently with dotnet runtime patches. MAJOR MUST MATCH ABOVE! -->
|
||||
<TgsDotnetRedistUrl>https://download.visualstudio.microsoft.com/download/pr/00397fee-1bd9-44ef-899b-4504b26e6e96/ab9c73409659f3238d33faee304a8b7c/dotnet-hosting-8.0.4-win.exe</TgsDotnetRedistUrl>
|
||||
<TgsMariaDBRedistVersion>10.11.6</TgsMariaDBRedistVersion>
|
||||
<TgsDotnetRedistUrl>https://download.visualstudio.microsoft.com/download/pr/751d3fcd-72db-4da2-b8d0-709c19442225/33cc492bde704bfd6d70a2b9109005a0/dotnet-hosting-8.0.6-win.exe</TgsDotnetRedistUrl>
|
||||
<TgsMariaDBRedistVersion>10.11.8</TgsMariaDBRedistVersion>
|
||||
<!-- Only have this uncommented if the mariadb servers are shitting the bed, update if the version updates -->
|
||||
<TgsMariaDBFallbackRedist>https://mirror.its.dal.ca/mariadb//mariadb-10.11.8/winx64-packages/mariadb-10.11.8-winx64.msi</TgsMariaDBFallbackRedist>
|
||||
<TgsYarnVersion>1.22.21</TgsYarnVersion>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<!-- This is in it's own file to help incremental building, changing it causes a complete rebuild of the web panel -->
|
||||
<TgsWebpanelVersion>5.7.1</TgsWebpanelVersion>
|
||||
<TgsWebpanelVersion>5.8.0</TgsWebpanelVersion>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
||||
@@ -35,7 +35,15 @@ try
|
||||
try
|
||||
{
|
||||
Invoke-WebRequest -Uri $redistUrl -OutFile artifacts/hosting-bundle.exe
|
||||
Invoke-WebRequest -Uri $dbRedistUrl -OutFile artifacts/mariadb.msi
|
||||
try
|
||||
{
|
||||
Invoke-WebRequest -Uri $dbRedistUrl -OutFile artifacts/mariadb.msi
|
||||
}
|
||||
catch
|
||||
{
|
||||
$dbRedistUrl = $versionXML.Project.PropertyGroup.TgsMariaDBFallbackRedist
|
||||
Invoke-WebRequest -Uri $dbRedistUrl -OutFile artifacts/mariadb.msi
|
||||
}
|
||||
} finally {
|
||||
$ProgressPreference = $previousProgressPreference
|
||||
}
|
||||
|
||||
@@ -31,9 +31,18 @@ namespace Tgstation.Server.Api.Models
|
||||
/// <summary>
|
||||
/// The time interval in minutes the repository is automatically pulled and compiles. 0 disables.
|
||||
/// </summary>
|
||||
/// <remarks>Auto-updates intervals start counting when set, TGS is started, or from the completion of the previous update. Incompatible with <see cref="AutoUpdateCron"/>.</remarks>
|
||||
[Required]
|
||||
public uint? AutoUpdateInterval { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A cron expression indicating when auto-updates should trigger. Must be a valid 6 part cron schedule (SECONDS MINUTES HOURS DAY/MONTH MONTH DAY/WEEK). Empty <see cref="string"/> disables.
|
||||
/// </summary>
|
||||
/// <remarks>Updates will not be triggered if the previous update is still running. Incompatible with <see cref="AutoUpdateInterval"/>.</remarks>
|
||||
[Required]
|
||||
[StringLength(Limits.MaximumStringLength)]
|
||||
public string? AutoUpdateCron { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The maximum number of chat bots the <see cref="Instance"/> may contain.
|
||||
/// </summary>
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
<!-- Usage: HTTP constants reference -->
|
||||
<PackageReference Include="Microsoft.AspNetCore.Http.Extensions" Version="2.2.0" />
|
||||
<!-- Usage: Decoding the 'nbf' property of JWTs -->
|
||||
<PackageReference Include="Microsoft.IdentityModel.JsonWebTokens" Version="7.5.1" />
|
||||
<PackageReference Include="Microsoft.IdentityModel.JsonWebTokens" Version="7.6.2" />
|
||||
<!-- Usage: Primary JSON library -->
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<!-- Usage: Data model annotating -->
|
||||
|
||||
@@ -11,9 +11,9 @@
|
||||
|
||||
<ItemGroup>
|
||||
<!-- Usage: Connecting to SignalR hubs in API -->
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="8.0.4" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="8.0.6" />
|
||||
<!-- Usage: Using target JSON serializer for API -->
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" Version="8.0.4" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" Version="8.0.6" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"isRoot": true,
|
||||
"tools": {
|
||||
"dotnet-ef": {
|
||||
"version": "8.0.4",
|
||||
"version": "8.0.6",
|
||||
"commands": [
|
||||
"dotnet-ef"
|
||||
]
|
||||
|
||||
@@ -45,10 +45,11 @@ namespace Tgstation.Server.Host.Components
|
||||
IConfiguration Configuration { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Change the <see cref="Api.Models.Instance.AutoUpdateInterval"/> for the <see cref="IInstanceCore"/>.
|
||||
/// Change the auto-update timing for the <see cref="IInstanceCore"/>.
|
||||
/// </summary>
|
||||
/// <param name="newInterval">The new auto update inteval.</param>
|
||||
/// <param name="newInterval">The new auto-update inteval.</param>
|
||||
/// <param name="newCron">The new auto-update cron schedule.</param>
|
||||
/// <returns>A <see cref="ValueTask"/> representing the running operation.</returns>
|
||||
ValueTask SetAutoUpdateInterval(uint newInterval);
|
||||
ValueTask ScheduleAutoUpdate(uint newInterval, string? newCron);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using NCrontab;
|
||||
|
||||
using Serilog.Context;
|
||||
|
||||
using Tgstation.Server.Api.Rights;
|
||||
@@ -183,7 +187,7 @@ namespace Tgstation.Server.Host.Components
|
||||
using (LogContext.PushProperty(SerilogContextHelper.InstanceIdContextProperty, metadata.Id))
|
||||
{
|
||||
await Task.WhenAll(
|
||||
SetAutoUpdateInterval(metadata.Require(x => x.AutoUpdateInterval)).AsTask(),
|
||||
ScheduleAutoUpdate(metadata.Require(x => x.AutoUpdateInterval), metadata.AutoUpdateCron).AsTask(),
|
||||
Configuration.StartAsync(cancellationToken),
|
||||
EngineManager.StartAsync(cancellationToken),
|
||||
Chat.StartAsync(cancellationToken),
|
||||
@@ -202,7 +206,7 @@ namespace Tgstation.Server.Host.Components
|
||||
using (LogContext.PushProperty(SerilogContextHelper.InstanceIdContextProperty, metadata.Id))
|
||||
{
|
||||
logger.LogDebug("Stopping instance...");
|
||||
await SetAutoUpdateInterval(0);
|
||||
await ScheduleAutoUpdate(0, null);
|
||||
await Watchdog.StopAsync(cancellationToken);
|
||||
await Task.WhenAll(
|
||||
Configuration.StopAsync(cancellationToken),
|
||||
@@ -213,11 +217,13 @@ namespace Tgstation.Server.Host.Components
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async ValueTask SetAutoUpdateInterval(uint newInterval)
|
||||
public async ValueTask ScheduleAutoUpdate(uint newInterval, string? newCron)
|
||||
{
|
||||
if (newInterval > 0 && !String.IsNullOrWhiteSpace(newCron))
|
||||
throw new ArgumentException("Only one of newInterval and newCron may be set!");
|
||||
|
||||
Task toWait;
|
||||
lock (timerLock)
|
||||
{
|
||||
if (timerTask != null)
|
||||
{
|
||||
logger.LogTrace("Cancelling auto-update task");
|
||||
@@ -229,12 +235,11 @@ namespace Tgstation.Server.Host.Components
|
||||
}
|
||||
else
|
||||
toWait = Task.CompletedTask;
|
||||
}
|
||||
|
||||
await toWait;
|
||||
if (newInterval == 0)
|
||||
if (newInterval == 0 && String.IsNullOrWhiteSpace(newCron))
|
||||
{
|
||||
logger.LogTrace("New auto-update interval is 0. Not starting task.");
|
||||
logger.LogTrace("Auto-update disabled 0. Not starting task.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -243,12 +248,12 @@ namespace Tgstation.Server.Host.Components
|
||||
// race condition, just quit
|
||||
if (timerTask != null)
|
||||
{
|
||||
logger.LogWarning("Aborting auto update interval change due to race condition!");
|
||||
logger.LogWarning("Aborting auto-update scheduling change due to race condition!");
|
||||
return;
|
||||
}
|
||||
|
||||
timerCts = new CancellationTokenSource();
|
||||
timerTask = TimerLoop(newInterval, timerCts.Token);
|
||||
timerTask = TimerLoop(newInterval, newCron, timerCts.Token);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -484,82 +489,128 @@ namespace Tgstation.Server.Host.Components
|
||||
/// Pull the repository and compile for every set of given <paramref name="minutes"/>.
|
||||
/// </summary>
|
||||
/// <param name="minutes">How many minutes the operation should repeat. Does not include running time.</param>
|
||||
/// <param name="cron">Alternative cron schedule.</param>
|
||||
/// <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 // TODO: Decomplexify
|
||||
async Task TimerLoop(uint minutes, CancellationToken cancellationToken)
|
||||
async Task TimerLoop(uint minutes, string? cron, CancellationToken cancellationToken)
|
||||
{
|
||||
logger.LogDebug("Entering auto-update loop");
|
||||
while (true)
|
||||
try
|
||||
{
|
||||
await asyncDelayer.Delay(TimeSpan.FromMinutes(minutes > Int32.MaxValue ? Int32.MaxValue : minutes), cancellationToken);
|
||||
TimeSpan delay;
|
||||
if (!String.IsNullOrWhiteSpace(cron))
|
||||
{
|
||||
logger.LogTrace("Using cron schedule: {cron}", cron);
|
||||
var schedule = CrontabSchedule.Parse(
|
||||
cron,
|
||||
new CrontabSchedule.ParseOptions
|
||||
{
|
||||
IncludingSeconds = true,
|
||||
});
|
||||
var now = DateTime.UtcNow;
|
||||
var nextOccurrence = schedule.GetNextOccurrence(now);
|
||||
delay = nextOccurrence - now;
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.LogTrace("Using interval: {interval}m", minutes);
|
||||
|
||||
delay = TimeSpan.FromMinutes(minutes);
|
||||
}
|
||||
|
||||
logger.LogInformation("Next auto-update will occur at {time}", DateTimeOffset.UtcNow + delay);
|
||||
|
||||
// https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task.delay?view=net-8.0#system-threading-tasks-task-delay(system-timespan)
|
||||
const uint DelayMinutesLimit = UInt32.MaxValue - 1;
|
||||
Debug.Assert(DelayMinutesLimit == 4294967294, "Delay limit assertion failure!");
|
||||
|
||||
var maxDelayIterations = 0UL;
|
||||
if (delay.TotalMilliseconds >= UInt32.MaxValue)
|
||||
{
|
||||
maxDelayIterations = (ulong)Math.Floor(delay.TotalMilliseconds / DelayMinutesLimit);
|
||||
logger.LogDebug("Breaking interval into {iterationCount} iterations", maxDelayIterations + 1);
|
||||
delay = TimeSpan.FromMilliseconds(delay.TotalMilliseconds - (maxDelayIterations * DelayMinutesLimit));
|
||||
}
|
||||
|
||||
if (maxDelayIterations > 0)
|
||||
{
|
||||
var longDelayTimeSpan = TimeSpan.FromMilliseconds(DelayMinutesLimit);
|
||||
for (var i = 0UL; i < maxDelayIterations; ++i)
|
||||
{
|
||||
logger.LogTrace("Long delay #{iteration}...", i + 1);
|
||||
await asyncDelayer.Delay(longDelayTimeSpan, cancellationToken);
|
||||
}
|
||||
|
||||
logger.LogTrace("Final delay iteration #{iteration}...", maxDelayIterations + 1);
|
||||
}
|
||||
|
||||
await asyncDelayer.Delay(delay, cancellationToken);
|
||||
logger.LogInformation("Beginning auto update...");
|
||||
await eventConsumer.HandleEvent(EventType.InstanceAutoUpdateStart, Enumerable.Empty<string>(), true, cancellationToken);
|
||||
try
|
||||
{
|
||||
var repositoryUpdateJob = Job.Create(Api.Models.JobCode.RepositoryAutoUpdate, null, metadata, RepositoryRights.CancelPendingChanges);
|
||||
await jobManager.RegisterOperation(
|
||||
repositoryUpdateJob,
|
||||
RepositoryAutoUpdateJob,
|
||||
cancellationToken);
|
||||
|
||||
var repoUpdateJobResult = await jobManager.WaitForJobCompletion(repositoryUpdateJob, null, cancellationToken, cancellationToken);
|
||||
if (repoUpdateJobResult == false)
|
||||
var repositoryUpdateJob = Job.Create(Api.Models.JobCode.RepositoryAutoUpdate, null, metadata, RepositoryRights.CancelPendingChanges);
|
||||
await jobManager.RegisterOperation(
|
||||
repositoryUpdateJob,
|
||||
RepositoryAutoUpdateJob,
|
||||
cancellationToken);
|
||||
|
||||
var repoUpdateJobResult = await jobManager.WaitForJobCompletion(repositoryUpdateJob, null, cancellationToken, cancellationToken);
|
||||
if (repoUpdateJobResult == false)
|
||||
{
|
||||
logger.LogWarning("Aborting auto-update due to repository update error!");
|
||||
continue;
|
||||
}
|
||||
|
||||
Job compileProcessJob;
|
||||
using (var repo = await RepositoryManager.LoadRepository(cancellationToken))
|
||||
{
|
||||
if (repo == null)
|
||||
throw new JobException(Api.Models.ErrorCode.RepoMissing);
|
||||
|
||||
var deploySha = repo.Head;
|
||||
if (deploySha == null)
|
||||
{
|
||||
logger.LogWarning("Aborting auto-update due to repository update error!");
|
||||
logger.LogTrace("Aborting auto update, repository error!");
|
||||
continue;
|
||||
}
|
||||
|
||||
Job compileProcessJob;
|
||||
using (var repo = await RepositoryManager.LoadRepository(cancellationToken))
|
||||
if (deploySha == LatestCompileJob()?.RevisionInformation.CommitSha)
|
||||
{
|
||||
if (repo == null)
|
||||
throw new JobException(Api.Models.ErrorCode.RepoMissing);
|
||||
|
||||
var deploySha = repo.Head;
|
||||
if (deploySha == null)
|
||||
{
|
||||
logger.LogTrace("Aborting auto update, repository error!");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (deploySha == LatestCompileJob()?.RevisionInformation.CommitSha)
|
||||
{
|
||||
logger.LogTrace("Aborting auto update, same revision as latest CompileJob");
|
||||
continue;
|
||||
}
|
||||
|
||||
// finally set up the job
|
||||
compileProcessJob = Job.Create(Api.Models.JobCode.AutomaticDeployment, null, metadata, DreamMakerRights.CancelCompile);
|
||||
await jobManager.RegisterOperation(
|
||||
compileProcessJob,
|
||||
(core, databaseContextFactory, job, progressReporter, jobCancellationToken) =>
|
||||
{
|
||||
if (core != this)
|
||||
throw new InvalidOperationException(DifferentCoreExceptionMessage);
|
||||
return DreamMaker.DeploymentProcess(
|
||||
job,
|
||||
databaseContextFactory,
|
||||
progressReporter,
|
||||
jobCancellationToken);
|
||||
},
|
||||
cancellationToken);
|
||||
logger.LogTrace("Aborting auto update, same revision as latest CompileJob");
|
||||
continue;
|
||||
}
|
||||
|
||||
await jobManager.WaitForJobCompletion(compileProcessJob, null, default, cancellationToken);
|
||||
}
|
||||
catch (Exception e) when (e is not OperationCanceledException)
|
||||
{
|
||||
logger.LogWarning(e, "Error in auto update loop!");
|
||||
continue;
|
||||
// finally set up the job
|
||||
compileProcessJob = Job.Create(Api.Models.JobCode.AutomaticDeployment, null, metadata, DreamMakerRights.CancelCompile);
|
||||
await jobManager.RegisterOperation(
|
||||
compileProcessJob,
|
||||
(core, databaseContextFactory, job, progressReporter, jobCancellationToken) =>
|
||||
{
|
||||
if (core != this)
|
||||
throw new InvalidOperationException(DifferentCoreExceptionMessage);
|
||||
return DreamMaker.DeploymentProcess(
|
||||
job,
|
||||
databaseContextFactory,
|
||||
progressReporter,
|
||||
jobCancellationToken);
|
||||
},
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
await jobManager.WaitForJobCompletion(compileProcessJob, null, default, cancellationToken);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
logger.LogDebug("Cancelled auto update loop!");
|
||||
break;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.LogError(e, "Error in auto update loop!");
|
||||
continue;
|
||||
}
|
||||
|
||||
logger.LogTrace("Leaving auto update loop...");
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ namespace Tgstation.Server.Host.Components
|
||||
public ValueTask InstanceRenamed(string newInstanceName, CancellationToken cancellationToken) => Instance.InstanceRenamed(newInstanceName, cancellationToken);
|
||||
|
||||
/// <inheritdoc />
|
||||
public ValueTask SetAutoUpdateInterval(uint newInterval) => Instance.SetAutoUpdateInterval(newInterval);
|
||||
public ValueTask ScheduleAutoUpdate(uint newInterval, string? newCron) => Instance.ScheduleAutoUpdate(newInterval, newCron);
|
||||
|
||||
/// <inheritdoc />
|
||||
public CompileJob? LatestCompileJob() => Instance.LatestCompileJob();
|
||||
|
||||
@@ -13,6 +13,8 @@ using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
using NCrontab;
|
||||
|
||||
using Tgstation.Server.Api;
|
||||
using Tgstation.Server.Api.Models;
|
||||
using Tgstation.Server.Api.Models.Request;
|
||||
@@ -144,6 +146,10 @@ namespace Tgstation.Server.Host.Controllers
|
||||
if (String.IsNullOrWhiteSpace(model.Name) || String.IsNullOrWhiteSpace(model.Path))
|
||||
return BadRequest(new ErrorMessageResponse(ErrorCode.InstanceWhitespaceNameOrPath));
|
||||
|
||||
IActionResult? earlyOut = ValidateCronSetting(model);
|
||||
if (earlyOut != null)
|
||||
return earlyOut;
|
||||
|
||||
var unNormalizedPath = model.Path;
|
||||
var targetInstancePath = NormalizePath(unNormalizedPath);
|
||||
model.Path = targetInstancePath;
|
||||
@@ -166,7 +172,6 @@ namespace Tgstation.Server.Host.Controllers
|
||||
return Conflict(new ErrorMessageResponse(ErrorCode.InstanceAtConflictingPath));
|
||||
|
||||
// Validate it's not a child of any other instance
|
||||
IActionResult? earlyOut = null;
|
||||
ulong countOfOtherInstances = 0;
|
||||
using (var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken))
|
||||
{
|
||||
@@ -415,9 +420,19 @@ namespace Tgstation.Server.Host.Controllers
|
||||
}
|
||||
|
||||
var oldAutoUpdateInterval = originalModel.AutoUpdateInterval!.Value;
|
||||
var oldAutoUpdateCron = originalModel.AutoUpdateCron;
|
||||
|
||||
var earlyOut = ValidateCronSetting(model);
|
||||
if (earlyOut != null)
|
||||
return earlyOut;
|
||||
|
||||
var changedAutoInterval = model.AutoUpdateInterval.HasValue && oldAutoUpdateInterval != model.AutoUpdateInterval;
|
||||
var changedAutoCron = model.AutoUpdateCron != null && oldAutoUpdateCron != model.AutoUpdateCron;
|
||||
|
||||
var renamed = model.Name != null && originalModel.Name != model.Name;
|
||||
|
||||
if (CheckModified(x => x.AutoUpdateInterval, InstanceManagerRights.SetAutoUpdate)
|
||||
|| CheckModified(x => x.AutoUpdateCron, InstanceManagerRights.SetAutoUpdate)
|
||||
|| CheckModified(x => x.ConfigurationType, InstanceManagerRights.SetConfiguration)
|
||||
|| CheckModified(x => x.Name, InstanceManagerRights.Rename)
|
||||
|| CheckModified(x => x.Online, InstanceManagerRights.SetOnline)
|
||||
@@ -436,6 +451,11 @@ namespace Tgstation.Server.Host.Controllers
|
||||
return Conflict(new ErrorMessageResponse(ErrorCode.ChatBotMax));
|
||||
}
|
||||
|
||||
if (changedAutoCron)
|
||||
model.AutoUpdateInterval = 0;
|
||||
else if (changedAutoInterval)
|
||||
model.AutoUpdateCron = String.Empty;
|
||||
|
||||
await DatabaseContext.Save(cancellationToken);
|
||||
|
||||
if (renamed)
|
||||
@@ -497,13 +517,13 @@ namespace Tgstation.Server.Host.Controllers
|
||||
api.MoveJob = job.ToApi();
|
||||
}
|
||||
|
||||
if (model.AutoUpdateInterval.HasValue && oldAutoUpdateInterval != model.AutoUpdateInterval)
|
||||
if (changedAutoInterval || changedAutoCron)
|
||||
{
|
||||
// ignoring retval because we don't care if it's offline
|
||||
await WithComponentInstanceNullable(
|
||||
async componentInstance =>
|
||||
{
|
||||
await componentInstance.SetAutoUpdateInterval(model.AutoUpdateInterval.Value);
|
||||
await componentInstance.ScheduleAutoUpdate(model.AutoUpdateInterval!.Value, model.AutoUpdateCron);
|
||||
return null;
|
||||
},
|
||||
originalModel);
|
||||
@@ -746,6 +766,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
Online = false,
|
||||
Path = initialSettings.Path,
|
||||
AutoUpdateInterval = initialSettings.AutoUpdateInterval ?? 0,
|
||||
AutoUpdateCron = initialSettings.AutoUpdateCron ?? String.Empty,
|
||||
ChatBotLimit = initialSettings.ChatBotLimit ?? Models.Instance.DefaultChatBotLimit,
|
||||
RepositorySettings = new RepositorySettings
|
||||
{
|
||||
@@ -821,5 +842,31 @@ namespace Tgstation.Server.Host.Controllers
|
||||
.Where(x => x.InstanceId == instanceResponse.Id && x.PermissionSetId == AuthenticationContext.PermissionSet.Id)
|
||||
.AnyAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates a given <paramref name="instance"/>'s <see cref="Api.Models.Instance.AutoUpdateCron"/> setting.
|
||||
/// </summary>
|
||||
/// <param name="instance">The <see cref="Api.Models.Instance"/> to validate.</param>
|
||||
/// <returns><see langword="null"/> if <paramref name="instance"/> has a valid <see cref="Api.Models.Instance.AutoUpdateCron"/> setting, a <see cref="BadRequestObjectResult"/> otherwise.</returns>
|
||||
BadRequestObjectResult? ValidateCronSetting(Api.Models.Instance instance)
|
||||
{
|
||||
if (!String.IsNullOrWhiteSpace(instance.AutoUpdateCron))
|
||||
{
|
||||
if ((instance.AutoUpdateInterval.HasValue && instance.AutoUpdateInterval.Value != 0)
|
||||
|| (CrontabSchedule.TryParse(
|
||||
instance.AutoUpdateCron,
|
||||
new CrontabSchedule.ParseOptions
|
||||
{
|
||||
IncludingSeconds = true,
|
||||
}) == null))
|
||||
return BadRequest(new ErrorMessageResponse(ErrorCode.ModelValidationFailure));
|
||||
|
||||
instance.AutoUpdateInterval = 0;
|
||||
}
|
||||
else
|
||||
instance.AutoUpdateCron = String.Empty;
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -375,22 +375,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(MSAddCompilerAdditionalArguments);
|
||||
internal static readonly Type MSLatestMigration = typeof(MSAddCronAutoUpdates);
|
||||
|
||||
/// <summary>
|
||||
/// Used by unit tests to remind us to setup the correct MYSQL migration downgrades.
|
||||
/// </summary>
|
||||
internal static readonly Type MYLatestMigration = typeof(MYAddCompilerAdditionalArguments);
|
||||
internal static readonly Type MYLatestMigration = typeof(MYAddCronAutoUpdates);
|
||||
|
||||
/// <summary>
|
||||
/// Used by unit tests to remind us to setup the correct PostgresSQL migration downgrades.
|
||||
/// </summary>
|
||||
internal static readonly Type PGLatestMigration = typeof(PGAddCompilerAdditionalArguments);
|
||||
internal static readonly Type PGLatestMigration = typeof(PGAddCronAutoUpdates);
|
||||
|
||||
/// <summary>
|
||||
/// Used by unit tests to remind us to setup the correct SQLite migration downgrades.
|
||||
/// </summary>
|
||||
internal static readonly Type SLLatestMigration = typeof(SLAddCompilerAdditionalArguments);
|
||||
internal static readonly Type SLLatestMigration = typeof(SLAddCronAutoUpdates);
|
||||
|
||||
/// <inheritdoc />
|
||||
#pragma warning disable CA1502 // Cyclomatic complexity
|
||||
@@ -419,6 +419,16 @@ namespace Tgstation.Server.Host.Database
|
||||
|
||||
string BadDatabaseType() => throw new ArgumentException($"Invalid DatabaseType: {currentDatabaseType}", nameof(currentDatabaseType));
|
||||
|
||||
if (targetVersion < new Version(6, 6, 0))
|
||||
targetMigration = currentDatabaseType switch
|
||||
{
|
||||
DatabaseType.MySql => nameof(MYAddCompilerAdditionalArguments),
|
||||
DatabaseType.PostgresSql => nameof(PGAddCompilerAdditionalArguments),
|
||||
DatabaseType.SqlServer => nameof(MSAddCompilerAdditionalArguments),
|
||||
DatabaseType.Sqlite => nameof(SLAddCompilerAdditionalArguments),
|
||||
_ => BadDatabaseType(),
|
||||
};
|
||||
|
||||
if (targetVersion < new Version(6, 5, 0))
|
||||
targetMigration = currentDatabaseType switch
|
||||
{
|
||||
|
||||
+1084
File diff suppressed because it is too large
Load Diff
+40
@@ -0,0 +1,40 @@
|
||||
using System;
|
||||
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class MSSwitchTo64BitDeploymentIds : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(migrationBuilder);
|
||||
|
||||
migrationBuilder.AlterColumn<long>(
|
||||
name: "GitHubDeploymentId",
|
||||
table: "CompileJobs",
|
||||
type: "bigint",
|
||||
nullable: true,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int",
|
||||
oldNullable: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(migrationBuilder);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "GitHubDeploymentId",
|
||||
table: "CompileJobs",
|
||||
type: "int",
|
||||
nullable: true,
|
||||
oldClrType: typeof(long),
|
||||
oldType: "bigint",
|
||||
oldNullable: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
+1154
File diff suppressed because it is too large
Load Diff
+40
@@ -0,0 +1,40 @@
|
||||
using System;
|
||||
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class MYSwitchTo64BitDeploymentIds : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(migrationBuilder);
|
||||
|
||||
migrationBuilder.AlterColumn<long>(
|
||||
name: "GitHubDeploymentId",
|
||||
table: "CompileJobs",
|
||||
type: "bigint",
|
||||
nullable: true,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int",
|
||||
oldNullable: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(migrationBuilder);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "GitHubDeploymentId",
|
||||
table: "CompileJobs",
|
||||
type: "int",
|
||||
nullable: true,
|
||||
oldClrType: typeof(long),
|
||||
oldType: "bigint",
|
||||
oldNullable: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
+1078
File diff suppressed because it is too large
Load Diff
+40
@@ -0,0 +1,40 @@
|
||||
using System;
|
||||
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class PGSwitchTo64BitDeploymentIds : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(migrationBuilder);
|
||||
|
||||
migrationBuilder.AlterColumn<long>(
|
||||
name: "GitHubDeploymentId",
|
||||
table: "CompileJobs",
|
||||
type: "bigint",
|
||||
nullable: true,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "integer",
|
||||
oldNullable: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(migrationBuilder);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "GitHubDeploymentId",
|
||||
table: "CompileJobs",
|
||||
type: "integer",
|
||||
nullable: true,
|
||||
oldClrType: typeof(long),
|
||||
oldType: "bigint",
|
||||
oldNullable: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
+1089
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class MSAddCronAutoUpdates : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(migrationBuilder);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "AutoUpdateCron",
|
||||
table: "Instances",
|
||||
type: "nvarchar(max)",
|
||||
maxLength: 10000,
|
||||
nullable: false,
|
||||
defaultValue: String.Empty);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(migrationBuilder);
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "AutoUpdateCron",
|
||||
table: "Instances");
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
+1159
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class MYAddCronAutoUpdates : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(migrationBuilder);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "AutoUpdateCron",
|
||||
table: "Instances",
|
||||
type: "varchar(10000)",
|
||||
maxLength: 10000,
|
||||
nullable: false,
|
||||
defaultValue: String.Empty)
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(migrationBuilder);
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "AutoUpdateCron",
|
||||
table: "Instances");
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
+1083
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class PGAddCronAutoUpdates : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(migrationBuilder);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "AutoUpdateCron",
|
||||
table: "Instances",
|
||||
type: "character varying(10000)",
|
||||
maxLength: 10000,
|
||||
nullable: false,
|
||||
defaultValue: String.Empty);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(migrationBuilder);
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "AutoUpdateCron",
|
||||
table: "Instances");
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
+1055
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class SLAddCronAutoUpdates : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(migrationBuilder);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "AutoUpdateCron",
|
||||
table: "Instances",
|
||||
type: "TEXT",
|
||||
maxLength: 10000,
|
||||
nullable: false,
|
||||
defaultValue: String.Empty);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(migrationBuilder);
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "AutoUpdateCron",
|
||||
table: "Instances");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "8.0.4")
|
||||
.HasAnnotation("ProductVersion", "8.0.6")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
||||
|
||||
MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder);
|
||||
@@ -149,8 +149,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
|
||||
MySqlPropertyBuilderExtensions.HasCharSet(b.Property<string>("EngineVersion"), "utf8mb4");
|
||||
|
||||
b.Property<int?>("GitHubDeploymentId")
|
||||
.HasColumnType("int");
|
||||
b.Property<long?>("GitHubDeploymentId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<long?>("GitHubRepoId")
|
||||
.HasColumnType("bigint");
|
||||
@@ -315,6 +315,11 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<long?>("Id"));
|
||||
|
||||
b.Property<string>("AutoUpdateCron")
|
||||
.IsRequired()
|
||||
.HasMaxLength(10000)
|
||||
.HasColumnType("varchar(10000)");
|
||||
|
||||
b.Property<uint?>("AutoUpdateInterval")
|
||||
.IsRequired()
|
||||
.HasColumnType("int unsigned");
|
||||
|
||||
+8
-3
@@ -13,7 +13,7 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "8.0.4")
|
||||
.HasAnnotation("ProductVersion", "8.0.6")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
@@ -137,8 +137,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<int?>("GitHubDeploymentId")
|
||||
.HasColumnType("integer");
|
||||
b.Property<long?>("GitHubDeploymentId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<long?>("GitHubRepoId")
|
||||
.HasColumnType("bigint");
|
||||
@@ -289,6 +289,11 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long?>("Id"));
|
||||
|
||||
b.Property<string>("AutoUpdateCron")
|
||||
.IsRequired()
|
||||
.HasMaxLength(10000)
|
||||
.HasColumnType("character varying(10000)");
|
||||
|
||||
b.Property<long>("AutoUpdateInterval")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
|
||||
+8
-3
@@ -13,7 +13,7 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "8.0.4")
|
||||
.HasAnnotation("ProductVersion", "8.0.6")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||
@@ -139,8 +139,8 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int?>("GitHubDeploymentId")
|
||||
.HasColumnType("int");
|
||||
b.Property<long?>("GitHubDeploymentId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<long?>("GitHubRepoId")
|
||||
.HasColumnType("bigint");
|
||||
@@ -291,6 +291,11 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long?>("Id"));
|
||||
|
||||
b.Property<string>("AutoUpdateCron")
|
||||
.IsRequired()
|
||||
.HasMaxLength(10000)
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<long>("AutoUpdateInterval")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "8.0.4");
|
||||
modelBuilder.HasAnnotation("ProductVersion", "8.0.6");
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
|
||||
{
|
||||
@@ -129,7 +129,7 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int?>("GitHubDeploymentId")
|
||||
b.Property<long?>("GitHubDeploymentId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<long?>("GitHubRepoId")
|
||||
@@ -281,6 +281,11 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("AutoUpdateCron")
|
||||
.IsRequired()
|
||||
.HasMaxLength(10000)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<uint?>("AutoUpdateInterval")
|
||||
.IsRequired()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace Tgstation.Server.Host.Models
|
||||
/// <summary>
|
||||
/// The GitHub deployment ID associated with the <see cref="CompileJob"/> if any.
|
||||
/// </summary>
|
||||
public int? GitHubDeploymentId { get; set; }
|
||||
public long? GitHubDeploymentId { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Version? DMApiVersion
|
||||
|
||||
@@ -69,6 +69,7 @@ namespace Tgstation.Server.Host.Models
|
||||
public InstanceResponse ToApi() => new()
|
||||
{
|
||||
AutoUpdateInterval = AutoUpdateInterval,
|
||||
AutoUpdateCron = AutoUpdateCron,
|
||||
ConfigurationType = ConfigurationType,
|
||||
Id = Id,
|
||||
Name = Name,
|
||||
|
||||
@@ -70,57 +70,58 @@
|
||||
<!-- Usage: IRC interop -->
|
||||
<PackageReference Include="Cyberboss.SmartIrc4net.Standard" Version="0.4.7" />
|
||||
<!-- Usage: .env file parsing -->
|
||||
<PackageReference Include="DotEnv.Core" Version="3.0.0" />
|
||||
<PackageReference Include="DotEnv.Core" Version="3.1.0" />
|
||||
<!-- Usage: Text formatter for Elasticsearch logging plugin -->
|
||||
<PackageReference Include="Elastic.CommonSchema.Serilog" Version="8.11.0" />
|
||||
<PackageReference Include="Elastic.CommonSchema.Serilog" Version="8.11.1" />
|
||||
<!-- Usage: GitLab interop -->
|
||||
<PackageReference Include="GitLabApiClient" Version="1.8.0" />
|
||||
<!-- Usage: git interop -->
|
||||
<PackageReference Include="LibGit2Sharp" Version="0.30.0" />
|
||||
<!-- Usage: JWT injection into HTTP pipeline -->
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.4" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.6" />
|
||||
<!-- Usage: Support ""legacy"" Newotonsoft.Json in HTTP pipeline. The rest of our codebase uses Newtonsoft. -->
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.4" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.6" />
|
||||
<!-- Usage: Using target JSON serializer for API -->
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" Version="8.0.4" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" Version="8.0.6" />
|
||||
<!-- Usage: Generating dumps of dotnet engine processes -->
|
||||
<PackageReference Include="Microsoft.Diagnostics.NETCore.Client" Version="0.2.510501" />
|
||||
<PackageReference Include="Microsoft.Diagnostics.NETCore.Client" Version="0.2.532401" />
|
||||
<!-- Usage: Database ORM -->
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.4" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.6" />
|
||||
<!-- Usage: Automatic migration generation using command line -->
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.4">
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.6">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<!-- Usage: Sqlite ORM plugin -->
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.4" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.6" />
|
||||
<!-- Usage: MSSQL ORM plugin -->
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.4" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.6" />
|
||||
<!-- Usage: POSIX support for syscalls, signals, and symlinks -->
|
||||
<PackageReference Include="Mono.Posix.NETStandard" Version="1.0.0" />
|
||||
<PackageReference Include="NCrontab.Signed" Version="3.3.3" />
|
||||
<!-- Usage: YAML config plugin -->
|
||||
<PackageReference Include="NetEscapades.Configuration.Yaml" Version="3.1.0" />
|
||||
<!-- Usage: PostgresSQL ORM plugin -->
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.2" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.4" />
|
||||
<!-- Usage: GitHub.com interop -->
|
||||
<PackageReference Include="Octokit" Version="11.0.1" />
|
||||
<PackageReference Include="Octokit" Version="13.0.0" />
|
||||
<!-- Usage: MYSQL/MariaDB ORM plugin -->
|
||||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.2" />
|
||||
<!-- Usage: Discord interop -->
|
||||
<PackageReference Include="Remora.Discord" Version="2024.1.0" />
|
||||
<PackageReference Include="Remora.Discord" Version="2024.2.0" />
|
||||
<!-- Usage: Rich logger builder -->
|
||||
<PackageReference Include="Serilog.Extensions.Logging" Version="8.0.0" />
|
||||
<!-- Usage: Async logging plugin -->
|
||||
<PackageReference Include="Serilog.Sinks.Async" Version="1.5.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Async" Version="2.0.0" />
|
||||
<!-- Usage: Console logging plugin -->
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="5.0.1" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
|
||||
<!-- Usage: Elasticsearch logging plugin -->
|
||||
<PackageReference Include="Serilog.Sinks.Elasticsearch" Version="10.0.0" />
|
||||
<!-- Usage: File logging plugin -->
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
|
||||
<!-- Usage: OpenAPI spec generator -->
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
|
||||
<!-- Usage: Newtonsoft.Json plugin for OpenAPI spec generator -->
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.Newtonsoft" Version="6.5.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.Newtonsoft" Version="6.6.2" />
|
||||
<!-- Usage: Windows authentication plugin allowing searching for users by name -->
|
||||
<PackageReference Include="System.DirectoryServices.AccountManagement" Version="8.0.0" />
|
||||
<!-- Usage: Identifying owning user of Windows Process objects -->
|
||||
@@ -128,7 +129,7 @@
|
||||
<!-- Usage: Temporary resolution to compatibility issues with EFCore 7 and .NET 8 -->
|
||||
<PackageReference Include="System.Security.Permissions" Version="8.0.0" />
|
||||
<!-- Usage: .DeleteAsync() support for IQueryable<T>s -->
|
||||
<PackageReference Include="Z.EntityFramework.Plus.EFCore" Version="8.102.2.2" />
|
||||
<PackageReference Include="Z.EntityFramework.Plus.EFCore" Version="8.103.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -133,7 +133,7 @@ namespace Tgstation.Server.Host.Utils.GitHub
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async ValueTask<int> GetCurrentUserId(CancellationToken cancellationToken)
|
||||
public async ValueTask<long> GetCurrentUserId(CancellationToken cancellationToken)
|
||||
{
|
||||
logger.LogTrace("CreateOAuthAccessToken");
|
||||
|
||||
@@ -183,7 +183,7 @@ namespace Tgstation.Server.Host.Utils.GitHub
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async ValueTask<int> CreateDeployment(NewDeployment newDeployment, string repoOwner, string repoName, CancellationToken cancellationToken)
|
||||
public async ValueTask<long> CreateDeployment(NewDeployment newDeployment, string repoOwner, string repoName, CancellationToken cancellationToken)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(newDeployment);
|
||||
|
||||
@@ -206,7 +206,7 @@ namespace Tgstation.Server.Host.Utils.GitHub
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task CreateDeploymentStatus(NewDeploymentStatus newDeploymentStatus, string repoOwner, string repoName, int deploymentId, CancellationToken cancellationToken)
|
||||
public Task CreateDeploymentStatus(NewDeploymentStatus newDeploymentStatus, string repoOwner, string repoName, long deploymentId, CancellationToken cancellationToken)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(newDeploymentStatus);
|
||||
|
||||
@@ -228,7 +228,7 @@ namespace Tgstation.Server.Host.Utils.GitHub
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task CreateDeploymentStatus(NewDeploymentStatus newDeploymentStatus, long repoId, int deploymentId, CancellationToken cancellationToken)
|
||||
public Task CreateDeploymentStatus(NewDeploymentStatus newDeploymentStatus, long repoId, long deploymentId, CancellationToken cancellationToken)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(newDeploymentStatus);
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace Tgstation.Server.Host.Utils.GitHub
|
||||
/// <param name="repoName">The name of the target repository.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="ValueTask{TResult}"/> resulting in the new deployment's ID.</returns>
|
||||
ValueTask<int> CreateDeployment(NewDeployment newDeployment, string repoOwner, string repoName, CancellationToken cancellationToken);
|
||||
ValueTask<long> CreateDeployment(NewDeployment newDeployment, string repoOwner, string repoName, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Create a <paramref name="newDeploymentStatus"/> on a target deployment.
|
||||
@@ -40,7 +40,7 @@ namespace Tgstation.Server.Host.Utils.GitHub
|
||||
/// <param name="deploymentId">The ID of the parent deployment.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the running operation.</returns>
|
||||
Task CreateDeploymentStatus(NewDeploymentStatus newDeploymentStatus, string repoOwner, string repoName, int deploymentId, CancellationToken cancellationToken);
|
||||
Task CreateDeploymentStatus(NewDeploymentStatus newDeploymentStatus, string repoOwner, string repoName, long deploymentId, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Create a <paramref name="newDeploymentStatus"/> on a target deployment.
|
||||
@@ -50,6 +50,6 @@ namespace Tgstation.Server.Host.Utils.GitHub
|
||||
/// <param name="deploymentId">The ID of the parent deployment.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the running operation.</returns>
|
||||
Task CreateDeploymentStatus(NewDeploymentStatus newDeploymentStatus, long repoId, int deploymentId, CancellationToken cancellationToken);
|
||||
Task CreateDeploymentStatus(NewDeploymentStatus newDeploymentStatus, long repoId, long deploymentId, CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ namespace Tgstation.Server.Host.Utils.GitHub
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="ValueTask{TResult}"/> resulting in the current user's ID.</returns>
|
||||
ValueTask<int> GetCurrentUserId(CancellationToken cancellationToken);
|
||||
ValueTask<long> GetCurrentUserId(CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Get a given <paramref name="pullRequestNumber"/>.
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="8.0.4" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="8.0.6" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -162,13 +162,42 @@ namespace Tgstation.Server.Tests.Live
|
||||
Assert.AreEqual(ConfigurationType.HostWrite, firstTest.ConfigurationType);
|
||||
Assert.IsTrue(Directory.Exists(firstTest.Path));
|
||||
|
||||
// a couple data validation checks
|
||||
// check setting both fails
|
||||
await ApiAssert.ThrowsException<ApiConflictException, InstanceResponse>(() => instanceManagerClient.Update(new InstanceUpdateRequest
|
||||
{
|
||||
Id = firstTest.Id,
|
||||
AutoUpdateInterval = 9999,
|
||||
AutoUpdateCron = "0 0 0 1 1 *"
|
||||
}, cancellationToken), ErrorCode.ModelValidationFailure);
|
||||
|
||||
// check bad crons fail
|
||||
await ApiAssert.ThrowsException<ApiConflictException, InstanceResponse>(() => instanceManagerClient.Update(new InstanceUpdateRequest
|
||||
{
|
||||
Id = firstTest.Id,
|
||||
AutoUpdateCron = "not a cron"
|
||||
}, cancellationToken), ErrorCode.ModelValidationFailure);
|
||||
|
||||
// regression check
|
||||
await instanceManagerClient.Update(new InstanceUpdateRequest
|
||||
var updated = await instanceManagerClient.Update(new InstanceUpdateRequest
|
||||
{
|
||||
Id = firstTest.Id,
|
||||
AutoUpdateInterval = 9999,
|
||||
}, cancellationToken);
|
||||
|
||||
Assert.AreEqual(String.Empty, updated.AutoUpdateCron);
|
||||
Assert.AreEqual(9999U, updated.AutoUpdateInterval);
|
||||
|
||||
// check regular 6-part crons succeed
|
||||
updated = await instanceManagerClient.Update(new InstanceUpdateRequest
|
||||
{
|
||||
Id = firstTest.Id,
|
||||
AutoUpdateCron = "0 0 0 1 1 *"
|
||||
}, cancellationToken);
|
||||
|
||||
Assert.AreEqual("0 0 0 1 1 *", updated.AutoUpdateCron);
|
||||
Assert.AreEqual(0U, updated.AutoUpdateInterval);
|
||||
|
||||
//can't move online instance
|
||||
await ApiAssert.ThrowsException<ConflictException, InstanceResponse>(() => instanceManagerClient.Update(new InstanceUpdateRequest
|
||||
{
|
||||
|
||||
@@ -89,19 +89,19 @@ namespace Tgstation.Server.Tests.Live
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public ValueTask<int> CreateDeployment(NewDeployment newDeployment, string repoOwner, string repoName, CancellationToken cancellationToken)
|
||||
public ValueTask<long> CreateDeployment(NewDeployment newDeployment, string repoOwner, string repoName, CancellationToken cancellationToken)
|
||||
{
|
||||
logger.LogTrace("CreateDeployment");
|
||||
return ValueTask.FromResult(new Random().Next()); ;
|
||||
return ValueTask.FromResult<long>(new Random().Next()); ;
|
||||
}
|
||||
|
||||
public Task CreateDeploymentStatus(NewDeploymentStatus newDeploymentStatus, string repoOwner, string repoName, int deploymentId, CancellationToken cancellationToken)
|
||||
public Task CreateDeploymentStatus(NewDeploymentStatus newDeploymentStatus, string repoOwner, string repoName, long deploymentId, CancellationToken cancellationToken)
|
||||
{
|
||||
logger.LogTrace("CreateDeploymentStatus");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task CreateDeploymentStatus(NewDeploymentStatus newDeploymentStatus, long repoId, int deploymentId, CancellationToken cancellationToken)
|
||||
public Task CreateDeploymentStatus(NewDeploymentStatus newDeploymentStatus, long repoId, long deploymentId, CancellationToken cancellationToken)
|
||||
{
|
||||
logger.LogTrace("CreateDeploymentStatus");
|
||||
return Task.CompletedTask;
|
||||
@@ -113,10 +113,10 @@ namespace Tgstation.Server.Tests.Live
|
||||
return ValueTask.FromResult(cryptographySuite.GetSecureString());
|
||||
}
|
||||
|
||||
public ValueTask<int> GetCurrentUserId(CancellationToken cancellationToken)
|
||||
public ValueTask<long> GetCurrentUserId(CancellationToken cancellationToken)
|
||||
{
|
||||
logger.LogTrace("GetCurrentUserId");
|
||||
return ValueTask.FromResult(new Random().Next());
|
||||
return ValueTask.FromResult<long>(new Random().Next());
|
||||
}
|
||||
|
||||
public ValueTask<long> GetRepositoryId(string repoOwner, string repoName, CancellationToken cancellationToken)
|
||||
|
||||
@@ -104,6 +104,7 @@ namespace Tgstation.Server.Tests
|
||||
var instance = new Host.Models.Instance
|
||||
{
|
||||
AutoUpdateInterval = 0,
|
||||
AutoUpdateCron = String.Empty,
|
||||
ChatBotLimit = 1,
|
||||
ChatSettings = new List<Host.Models.ChatBot>(),
|
||||
ConfigurationType = ConfigurationType.HostWrite,
|
||||
|
||||
@@ -809,26 +809,30 @@ namespace Tgstation.Server.ReleaseNotes
|
||||
var versionsPropertyGroup = project.Elements().First(x => x.Name == xmlNamespace + "PropertyGroup");
|
||||
var coreVersion = Version.Parse(versionsPropertyGroup.Element(xmlNamespace + "TgsCoreVersion").Value);
|
||||
|
||||
const string BodyForPRSha = "b64a9a24ec6b13c819b47304625a88864c3872e0";
|
||||
const string BodyForPRSha = "5ffc3ff5901db66d782aa0e8ed2a74b16f896091";
|
||||
var prBody = $@"# Automated Pull Request
|
||||
|
||||
This pull request was generated by our [deployment pipeline]({actionUrl}) as a result of the release of [tgstation-server-v{coreVersion}](https://github.com/tgstation/tgstation-server/releases/tag/tgstation-server-v{coreVersion}). Validation was performed as part of the process.
|
||||
|
||||
The user account that created this pull request is available to correct any issues.
|
||||
|
||||
Checklist for Pull Requests
|
||||
- [x] Have you signed the [Contributor License Agreement](https://cla.opensource.microsoft.com/microsoft/winget-pkgs)?
|
||||
- [x] Is there a linked Issue?
|
||||
- Shouldn't be possible as this release was just created.
|
||||
- [x] Have you checked that there aren't other open [pull requests](https://github.com/microsoft/winget-pkgs/pulls) for the same manifest update/change?
|
||||
- This PR is generated as a direct result of a new release of `tgstation-server` this should be impossible
|
||||
- [x] Is there a linked Issue? **No**
|
||||
|
||||
Manifests
|
||||
- [x] Have you checked that there aren't other open [pull requests](https://github.com/microsoft/winget-pkgs/pulls) for the same manifest update/change? **Impossible**
|
||||
- [x] This PR only modifies one (1) manifest
|
||||
- [x] Have you [validated](https://github.com/microsoft/winget-pkgs/blob/master/doc/Authoring.md#validation) your manifest locally with `winget validate --manifest <path>`?
|
||||
- Validation is performed as a prerequisite to deployment.
|
||||
- [x] Have you tested your manifest locally with `winget install --manifest <path>`?
|
||||
- Manifest installation and uninstallation is performed as a prerequisite to deployment.
|
||||
- [x] Does your manifest conform to the [1.6 schema](https://github.com/microsoft/winget-pkgs/tree/master/doc/manifest/schema/1.6.0)?
|
||||
|
||||
###### Microsoft Reviewers: [Open in CodeFlow](https://microsoft.github.io/open-pr/?codeflow=https://github.com/microsoft/winget-pkgs/pull/$PR_NUMBER_SUBST$)";
|
||||
Note: `<path>` is the directory's name containing the manifest you're submitting.
|
||||
|
||||
###### Microsoft Reviewers: [Open in CodeFlow](https://microsoft.github.io/open-pr/?codeflow=https://github.com/microsoft/winget-pkgs/pull/$PR_NUMBER_SUBST$)
|
||||
|
||||
---
|
||||
";
|
||||
|
||||
if (expectedTemplateSha != null)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user