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

v5.3.0
This commit is contained in:
Jordan Dominion
2023-01-21 20:57:10 -05:00
committed by GitHub
29 changed files with 243 additions and 114 deletions
+2 -2
View File
@@ -51,8 +51,8 @@ jobs:
dmapi-build:
name: Build DMAPI
env:
BYOND_MAJOR: 513
BYOND_MINOR: 1536
BYOND_MAJOR: 515
BYOND_MINOR: 1592
runs-on: ubuntu-latest
steps:
- name: Install x86 libc Dependencies
+5 -5
View File
@@ -3,12 +3,12 @@
<!-- Integration tests will ensure they match across the board -->
<Import Project="ControlPanelVersion.props" />
<PropertyGroup>
<TgsCoreVersion>5.2.4</TgsCoreVersion>
<TgsCoreVersion>5.3.0</TgsCoreVersion>
<TgsConfigVersion>4.4.0</TgsConfigVersion>
<TgsApiVersion>9.7.0</TgsApiVersion>
<TgsApiLibraryVersion>10.1.0</TgsApiLibraryVersion>
<TgsClientVersion>11.1.0</TgsClientVersion>
<TgsDmapiVersion>6.0.5</TgsDmapiVersion>
<TgsApiVersion>9.8.0</TgsApiVersion>
<TgsApiLibraryVersion>10.2.0</TgsApiLibraryVersion>
<TgsClientVersion>11.2.0</TgsClientVersion>
<TgsDmapiVersion>6.0.6</TgsDmapiVersion>
<TgsInteropVersion>5.3.0</TgsInteropVersion>
<TgsHostWatchdogVersion>1.2.0</TgsHostWatchdogVersion>
<TgsContainerScriptVersion>1.2.0</TgsContainerScriptVersion>
+1 -1
View File
@@ -1,6 +1,6 @@
// tgstation-server DMAPI
#define TGS_DMAPI_VERSION "6.0.5"
#define TGS_DMAPI_VERSION "6.0.6"
// All functions and datums outside this document are subject to change with any version and should not be relied on.
+2 -1
View File
@@ -5,4 +5,5 @@ This folder contains all DMAPI code not directly involved in an API.
- [_definitions.dm](./definitions.dm) contains defines needed across DMAPI internals.
- [core.dm](./core.dm) contains the implementations of the `/world/proc/TgsXXX()` procs. Many map directly to the `/datum/tgs_api` functions. It also contains the /datum selection and setup code.
- [datum.dm](./datum.dm) contains the `/datum/tgs_api` declarations that all APIs must implement.
- [tgs_version.dm](./tgs_version.dm) contains the `/datum/tgs_version` definition
- [tgs_version.dm](./tgs_version.dm) contains the `/datum/tgs_version` definition
-
+4
View File
@@ -99,7 +99,11 @@
if(skip_compat_check && !fexists(SERVICE_INTERFACE_DLL))
TGS_ERROR_LOG("Service parameter present but no interface DLL detected. This is symptomatic of running a service less than version 3.1! Please upgrade.")
return
#if DM_VERSION >= 515
call_ext(SERVICE_INTERFACE_DLL, SERVICE_INTERFACE_FUNCTION)(instance_name, command) //trust no retval
#else
call(SERVICE_INTERFACE_DLL, SERVICE_INTERFACE_FUNCTION)(instance_name, command) //trust no retval
#endif
return TRUE
/datum/tgs_api/v3210/OnTopic(T)
+16 -27
View File
@@ -1,49 +1,38 @@
using System.ComponentModel.DataAnnotations;
using System;
using System.ComponentModel.DataAnnotations;
using Tgstation.Server.Api.Models.Internal;
namespace Tgstation.Server.Api.Models
{
/// <summary>
/// Indicates a chat channel.
/// </summary>
public class ChatChannel
public class ChatChannel : ChatChannelBase
{
/// <summary>
/// The channel identifier. Supercedes <see cref="IrcChannel"/> and <see cref="DiscordChannelId"/>.
/// For <see cref="ChatProvider.Irc"/>, it's the IRC channel name and optional password colon separated.
/// For <see cref="ChatProvider.Discord"/>, it's the stringified Discord channel snowflake.
/// </summary>
[Required]
[StringLength(Limits.MaximumIndexableStringLength, MinimumLength = 1)]
public string? ChannelData { get; set; }
/// <summary>
/// The IRC channel name. Also potentially contains the channel passsword (if separated by a colon).
/// If multiple copies of the same channel with different keys are added to the server, the one that will be used is undefined.
/// </summary>
[ResponseOptions]
[StringLength(Limits.MaximumIndexableStringLength, MinimumLength = 1)]
[Obsolete($"Use {nameof(ChannelData)}")]
public string? IrcChannel { get; set; }
/// <summary>
/// The Discord channel ID.
/// </summary>
[Obsolete($"Use {nameof(ChannelData)}")]
[ResponseOptions]
public ulong? DiscordChannelId { get; set; }
/// <summary>
/// If the <see cref="ChatChannel"/> is an admin channel.
/// </summary>
[Required]
public bool? IsAdminChannel { get; set; }
/// <summary>
/// If the <see cref="ChatChannel"/> is a watchdog channel.
/// </summary>
[Required]
public bool? IsWatchdogChannel { get; set; }
/// <summary>
/// If the <see cref="ChatChannel"/> is an updates channel.
/// </summary>
[Required]
public bool? IsUpdatesChannel { get; set; }
/// <summary>
/// A custom tag users can define to group channels together.
/// </summary>
[ResponseOptions]
[StringLength(Limits.MaximumStringLength)]
public string? Tag { get; set; }
}
}
@@ -22,8 +22,8 @@ namespace Tgstation.Server.Api.Models.Internal
return true;
return Provider.Value switch
{
ChatProvider.Discord => Channels?.Select(x => x.DiscordChannelId.HasValue && x.IrcChannel == null).All(x => x) ?? true,
ChatProvider.Irc => Channels?.Select(x => !x.DiscordChannelId.HasValue && x.IrcChannel != null).All(x => x) ?? true,
ChatProvider.Discord => Channels?.Select(x => (x.DiscordChannelId.HasValue || ulong.TryParse(x.ChannelData, out _)) && x.IrcChannel == null).All(x => x) ?? true,
ChatProvider.Irc => Channels?.Select(x => !x.DiscordChannelId.HasValue && (x.IrcChannel != null || x.ChannelData != null)).All(x => x) ?? true,
_ => throw new InvalidOperationException("Invalid provider type!"),
};
}
@@ -0,0 +1,35 @@
using System.ComponentModel.DataAnnotations;
namespace Tgstation.Server.Api.Models.Internal
{
/// <summary>
/// Base chat channel class.
/// </summary>
public abstract class ChatChannelBase
{
/// <summary>
/// If the <see cref="ChatChannel"/> is an admin channel.
/// </summary>
[Required]
public bool? IsAdminChannel { get; set; }
/// <summary>
/// If the <see cref="ChatChannel"/> is a watchdog channel.
/// </summary>
[Required]
public bool? IsWatchdogChannel { get; set; }
/// <summary>
/// If the <see cref="ChatChannel"/> is an updates channel.
/// </summary>
[Required]
public bool? IsUpdatesChannel { get; set; }
/// <summary>
/// A custom tag users can define to group channels together.
/// </summary>
[ResponseOptions]
[StringLength(Limits.MaximumStringLength)]
public string? Tag { get; set; }
}
}
@@ -16,7 +16,7 @@
<RepositoryUrl>https://github.com/tgstation/tgstation-server</RepositoryUrl>
<Copyright>2018-2022</Copyright>
<PackageTags>json web api tgstation-server tgstation ss13 byond</PackageTags>
<PackageReleaseNotes>Retargeted to netstandard2.0 to support migrator.</PackageReleaseNotes>
<PackageReleaseNotes>Added ChannelData field to ChatChannels model.</PackageReleaseNotes>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<CodeAnalysisRuleSet>../../build/analyzers.ruleset</CodeAnalysisRuleSet>
@@ -16,7 +16,7 @@
<RepositoryUrl>https://github.com/tgstation/tgstation-server</RepositoryUrl>
<Copyright>2018-2022</Copyright>
<PackageTags>json web api tgstation-server tgstation ss13 byond client</PackageTags>
<PackageReleaseNotes>Retargeted to netstandard2.0 to support migrator. Fixed nullablity of IByondClient.SetActiveVersion's Stream parameter.</PackageReleaseNotes>
<PackageReleaseNotes>Updated to API library 10.2.0.</PackageReleaseNotes>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<CodeAnalysisRuleSet>../../build/analyzers.ruleset</CodeAnalysisRuleSet>
@@ -118,6 +118,32 @@ namespace Tgstation.Server.Host.Service
{
if (Uninstall)
return; // oh no, it's retarded...
// First check if the service already exists
if (Environment.UserInteractive)
foreach (ServiceController sc in ServiceController.GetServices())
if (sc.ServiceName == "tgstation-server" || sc.ServiceName == "tgstation-server-4")
{
DialogResult result = MessageBox.Show($"You already have another TGS service installed ({sc.ServiceName}). Would you like to uninstall it now? Pressing \"No\" will cancel this install.", "TGS Service", MessageBoxButtons.YesNo);
if (result != DialogResult.Yes)
return; // is this needed after exit?
// Stop it first to give it some cleanup time
if (sc.Status == ServiceControllerStatus.Running)
{
sc.Stop();
sc.WaitForStatus(ServiceControllerStatus.Stopped);
}
// And remove it
using (ServiceInstaller si = new ServiceInstaller())
{
si.Context = new InstallContext($"old-{sc.ServiceName}-uninstall.log", null);
si.ServiceName = sc.ServiceName;
si.Uninstall(null);
}
}
using (var processInstaller = new ServiceProcessInstaller())
using (var installer = new ServiceInstaller())
{
@@ -88,7 +88,7 @@ namespace Tgstation.Server.Host.Components.Chat
readonly object synchronizationLock;
/// <summary>
/// The <see cref="ICustomCommandHandler"/> for the <see cref="ChangeChannels(long, IEnumerable{Api.Models.ChatChannel}, CancellationToken)"/>.
/// The <see cref="ICustomCommandHandler"/> for the <see cref="ChangeChannels(long, IEnumerable{Models.ChatChannel}, CancellationToken)"/>.
/// </summary>
ICustomCommandHandler customCommandHandler;
@@ -175,7 +175,7 @@ namespace Tgstation.Server.Host.Components.Chat
}
/// <inheritdoc />
public async Task ChangeChannels(long connectionId, IEnumerable<Api.Models.ChatChannel> newChannels, CancellationToken cancellationToken)
public async Task ChangeChannels(long connectionId, IEnumerable<Models.ChatChannel> newChannels, CancellationToken cancellationToken)
{
if (newChannels == null)
throw new ArgumentNullException(nameof(newChannels));
@@ -554,7 +554,7 @@ namespace Tgstation.Server.Host.Components.Chat
async Task RemapProvider(IProvider provider, CancellationToken cancellationToken)
{
logger.LogTrace("Remapping channels for provider reconnection...");
IEnumerable<Api.Models.ChatChannel> channelsToMap;
IEnumerable<Models.ChatChannel> channelsToMap;
long providerId;
lock (providers)
providerId = providers.Where(x => x.Value == provider).Select(x => x.Key).First();
@@ -40,10 +40,10 @@ namespace Tgstation.Server.Host.Components.Chat
/// Change chat channels.
/// </summary>
/// <param name="connectionId">The <see cref="Api.Models.EntityId.Id"/> of the connection.</param>
/// <param name="newChannels">An <see cref="IEnumerable{T}"/> of the new list of <see cref="Api.Models.ChatChannel"/>s.</param>
/// <param name="newChannels">An <see cref="IEnumerable{T}"/> of the new list of <see cref="Models.ChatChannel"/>s.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
/// <returns>A <see cref="Task"/> representing the running operation.</returns>
Task ChangeChannels(long connectionId, IEnumerable<Api.Models.ChatChannel> newChannels, CancellationToken cancellationToken);
Task ChangeChannels(long connectionId, IEnumerable<Models.ChatChannel> newChannels, CancellationToken cancellationToken);
/// <summary>
/// Queue a chat <paramref name="message"/> to a given set of <paramref name="channelIds"/>.
@@ -608,14 +608,14 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
}
/// <inheritdoc />
protected override async Task<IReadOnlyCollection<Tuple<Api.Models.ChatChannel, ChannelRepresentation>>> MapChannelsImpl(IEnumerable<Api.Models.ChatChannel> channels, CancellationToken cancellationToken)
protected override async Task<IReadOnlyCollection<Tuple<Models.ChatChannel, ChannelRepresentation>>> MapChannelsImpl(IEnumerable<Models.ChatChannel> channels, CancellationToken cancellationToken)
{
if (channels == null)
throw new ArgumentNullException(nameof(channels));
var remapRequired = false;
async Task<Tuple<Api.Models.ChatChannel, ChannelRepresentation>> GetModelChannelFromDBChannel(Api.Models.ChatChannel channelFromDB)
async Task<Tuple<Models.ChatChannel, ChannelRepresentation>> GetModelChannelFromDBChannel(Models.ChatChannel channelFromDB)
{
if (!channelFromDB.DiscordChannelId.HasValue)
throw new InvalidOperationException("ChatChannel missing DiscordChannelId!");
@@ -33,7 +33,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
Task InitialConnectionJob { get; }
/// <summary>
/// Indicate to the provider that at least one <see cref="MapChannels(IEnumerable{Api.Models.ChatChannel}, CancellationToken)"/> call has successfully completed.
/// Indicate to the provider that at least one <see cref="MapChannels(IEnumerable{ChatChannel}, CancellationToken)"/> call has successfully completed.
/// </summary>
void InitialMappingComplete();
@@ -42,7 +42,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
/// </summary>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
/// <returns>A <see cref="Task{TResult}"/> resulting in the next available <see cref="Message"/> or <see langword="null"/> if the <see cref="IProvider"/> needed to reconnect.</returns>
/// <remarks>Note that private messages will come in the form of <see cref="ChannelRepresentation"/>s not returned in <see cref="MapChannels(IEnumerable{Api.Models.ChatChannel}, CancellationToken)"/>. Do not <see cref="IDisposable.Dispose"/> the <see cref="IProvider"/> on continuations run from the returned <see cref="Task"/>.</remarks>
/// <remarks>Note that private messages will come in the form of <see cref="ChannelRepresentation"/>s not returned in <see cref="MapChannels(IEnumerable{ChatChannel}, CancellationToken)"/>. Do not <see cref="IDisposable.Dispose"/> the <see cref="IProvider"/> on continuations run from the returned <see cref="Task"/>.</remarks>
Task<Message> NextMessage(CancellationToken cancellationToken);
/// <summary>
@@ -57,8 +57,8 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
/// </summary>
/// <param name="channels">The <see cref="Api.Models.ChatChannel"/>s to map.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
/// <returns>A <see cref="Task{TResult}"/> resulting in a <see cref="IReadOnlyCollection{T}"/> of the <see cref="Api.Models.ChatChannel"/>'s <see cref="ChannelRepresentation"/>s representing <paramref name="channels"/>.</returns>
Task<IReadOnlyCollection<Tuple<Api.Models.ChatChannel, ChannelRepresentation>>> MapChannels(IEnumerable<Api.Models.ChatChannel> channels, CancellationToken cancellationToken);
/// <returns>A <see cref="Task{TResult}"/> resulting in a <see cref="IReadOnlyCollection{T}"/> of the <see cref="ChatChannel"/>'s <see cref="ChannelRepresentation"/>s representing <paramref name="channels"/>.</returns>
Task<IReadOnlyCollection<Tuple<ChatChannel, ChannelRepresentation>>> MapChannels(IEnumerable<ChatChannel> channels, CancellationToken cancellationToken);
/// <summary>
/// Send a message to the <see cref="IProvider"/>.
@@ -252,8 +252,8 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
}
/// <inheritdoc />
protected override Task<IReadOnlyCollection<Tuple<ChatChannel, ChannelRepresentation>>> MapChannelsImpl(
IEnumerable<ChatChannel> channels,
protected override Task<IReadOnlyCollection<Tuple<Models.ChatChannel, ChannelRepresentation>>> MapChannelsImpl(
IEnumerable<Models.ChatChannel> channels,
CancellationToken cancellationToken)
=> Task.Factory.StartNew(
() =>
@@ -285,10 +285,10 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
else
client.RfcJoin(channelToJoin);
return (IReadOnlyCollection<Tuple<ChatChannel, ChannelRepresentation>>)channels
.Select(apiChannel =>
return (IReadOnlyCollection<Tuple<Models.ChatChannel, ChannelRepresentation>>)channels
.Select(dbChannel =>
{
var channelName = apiChannel.GetIrcChannelName();
var channelName = dbChannel.GetIrcChannelName();
ulong? id = null;
if (!channelIdMap.Any(y =>
{
@@ -303,15 +303,15 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
}
return Tuple.Create(
apiChannel,
dbChannel,
new ChannelRepresentation
{
RealId = id.Value,
IsAdminChannel = apiChannel.IsAdminChannel == true,
IsAdminChannel = dbChannel.IsAdminChannel == true,
ConnectionName = address,
FriendlyName = channelIdMap[id.Value],
IsPrivateChannel = false,
Tag = apiChannel.Tag,
Tag = dbChannel.Tag,
});
})
.ToList();
@@ -117,7 +117,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
public void InitialMappingComplete() => initialConnectionTcs.TrySetResult(null);
/// <inheritdoc />
public async Task<IReadOnlyCollection<Tuple<Api.Models.ChatChannel, ChannelRepresentation>>> MapChannels(IEnumerable<Api.Models.ChatChannel> channels, CancellationToken cancellationToken)
public async Task<IReadOnlyCollection<Tuple<ChatChannel, ChannelRepresentation>>> MapChannels(IEnumerable<ChatChannel> channels, CancellationToken cancellationToken)
{
try
{
@@ -193,13 +193,13 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
protected abstract Task DisconnectImpl(CancellationToken cancellationToken);
/// <summary>
/// Implementation of <see cref="MapChannels(IEnumerable{Api.Models.ChatChannel}, CancellationToken)"/>.
/// Implementation of <see cref="MapChannels(IEnumerable{ChatChannel}, CancellationToken)"/>.
/// </summary>
/// <param name="channels">The <see cref="Api.Models.ChatChannel"/>s to map.</param>
/// <param name="channels">The <see cref="ChatChannel"/>s to map.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
/// <returns>A <see cref="Task{TResult}"/> resulting in a <see cref="IReadOnlyCollection{T}"/> of the <see cref="Api.Models.ChatChannel"/>'s <see cref="ChannelRepresentation"/>s representing <paramref name="channels"/>.</returns>
protected abstract Task<IReadOnlyCollection<Tuple<Api.Models.ChatChannel, ChannelRepresentation>>> MapChannelsImpl(
IEnumerable<Api.Models.ChatChannel> channels,
/// <returns>A <see cref="Task{TResult}"/> resulting in a <see cref="IReadOnlyCollection{T}"/> of the <see cref="ChatChannel"/>'s <see cref="ChannelRepresentation"/>s representing <paramref name="channels"/>.</returns>
protected abstract Task<IReadOnlyCollection<Tuple<ChatChannel, ChannelRepresentation>>> MapChannelsImpl(
IEnumerable<ChatChannel> channels,
CancellationToken cancellationToken);
/// <summary>
@@ -9,6 +9,7 @@ using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Tgstation.Server.Host.Components.Deployment.Remote;
using Tgstation.Server.Host.Components.Events;
using Tgstation.Server.Host.Database;
using Tgstation.Server.Host.IO;
using Tgstation.Server.Host.Models;
@@ -53,6 +54,11 @@ namespace Tgstation.Server.Host.Components.Deployment
/// </summary>
readonly ILogger<DmbFactory> logger;
/// <summary>
/// The <see cref="IEventConsumer"/> for <see cref="DmbFactory"/>.
/// </summary>
readonly IEventConsumer eventConsumer;
/// <summary>
/// The <see cref="Api.Models.Instance"/> for the <see cref="DmbFactory"/>.
/// </summary>
@@ -69,7 +75,7 @@ namespace Tgstation.Server.Host.Components.Deployment
readonly IDictionary<long, int> jobLockCounts;
/// <summary>
/// <see cref="Task"/> representing calls to <see cref="CleanJob(CompileJob)"/>.
/// <see cref="Task"/> representing calls to <see cref="CleanRegisteredCompileJob(CompileJob)"/>.
/// </summary>
Task cleanupTask;
@@ -94,18 +100,21 @@ namespace Tgstation.Server.Host.Components.Deployment
/// <param name="databaseContextFactory">The value of <see cref="databaseContextFactory"/>.</param>
/// <param name="ioManager">The value of <see cref="ioManager"/>.</param>
/// <param name="remoteDeploymentManagerFactory">The value of <see cref="remoteDeploymentManagerFactory"/>.</param>
/// <param name="eventConsumer">The value of <see cref="eventConsumer"/>.</param>
/// <param name="logger">The value of <see cref="logger"/>.</param>
/// <param name="metadata">The value of <see cref="metadata"/>.</param>
public DmbFactory(
IDatabaseContextFactory databaseContextFactory,
IIOManager ioManager,
IRemoteDeploymentManagerFactory remoteDeploymentManagerFactory,
IEventConsumer eventConsumer,
ILogger<DmbFactory> logger,
Api.Models.Instance metadata)
{
this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory));
this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
this.remoteDeploymentManagerFactory = remoteDeploymentManagerFactory ?? throw new ArgumentNullException(nameof(remoteDeploymentManagerFactory));
this.eventConsumer = eventConsumer ?? throw new ArgumentNullException(nameof(eventConsumer));
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
this.metadata = metadata ?? throw new ArgumentNullException(nameof(metadata));
@@ -245,7 +254,7 @@ namespace Tgstation.Server.Host.Components.Deployment
void CleanupAction()
{
if (providerSubmitted)
CleanJob(compileJob);
CleanRegisteredCompileJob(compileJob);
}
var newProvider = new DmbProvider(compileJob, ioManager, CleanupAction);
@@ -358,7 +367,7 @@ namespace Tgstation.Server.Host.Components.Deployment
try
{
++deleting;
await ioManager.DeleteDirectory(x, cancellationToken);
await DeleteCompileJobContent(x, cancellationToken);
}
catch (OperationCanceledException)
{
@@ -386,19 +395,19 @@ namespace Tgstation.Server.Host.Components.Deployment
/// Delete the <see cref="Api.Models.Internal.CompileJob.DirectoryName"/> of <paramref name="job"/>.
/// </summary>
/// <param name="job">The <see cref="CompileJob"/> to clean.</param>
void CleanJob(CompileJob job)
void CleanRegisteredCompileJob(CompileJob job)
{
async Task HandleCleanup()
{
var deleteJob = ioManager.DeleteDirectory(job.DirectoryName.ToString(), cleanupCts.Token);
var remoteDeploymentManager = remoteDeploymentManagerFactory.CreateRemoteDeploymentManager(
metadata,
job);
// First kill the GitHub deployment
var remoteDeploymentManager = remoteDeploymentManagerFactory.CreateRemoteDeploymentManager(metadata, job);
// DCT: None available
var deploymentJob = remoteDeploymentManager.MarkInactive(job, default);
var deleteTask = DeleteCompileJobContent(job.DirectoryName.ToString(), cleanupCts.Token);
var otherTask = cleanupTask;
await Task.WhenAll(otherTask, deleteJob, deploymentJob);
await Task.WhenAll(otherTask, deleteTask, deploymentJob);
}
lock (jobLockCounts)
@@ -414,5 +423,18 @@ namespace Tgstation.Server.Host.Components.Deployment
logger.LogTrace("Compile job {0} lock count now: {1}", job.Id, decremented);
}
}
/// <summary>
/// Handles cleaning the resources of a <see cref="CompileJob"/>.
/// </summary>
/// <param name="directory">The directory to cleanup.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for this <see cref="Task"/>.</param>
/// <returns>The deletion <see cref="Task"/>.</returns>
async Task DeleteCompileJobContent(string directory, CancellationToken cancellationToken)
{
// Then call the cleanup event, waiting here first
await eventConsumer.HandleEvent(EventType.DeploymentCleanup, new List<string> { ioManager.ResolvePath(directory) }, cancellationToken);
await ioManager.DeleteDirectory(directory, cancellationToken);
}
}
}
@@ -929,6 +929,7 @@ namespace Tgstation.Server.Host.Components.Deployment
try
{
// DCT: None available
await eventConsumer.HandleEvent(EventType.DeploymentCleanup, new List<string> { jobPath }, default);
await ioManager.DeleteDirectory(jobPath, default);
}
catch (Exception e)
@@ -283,7 +283,7 @@ namespace Tgstation.Server.Host.Components.Deployment.Remote
string remoteRepositoryName,
bool updated) => String.Format(
CultureInfo.InvariantCulture,
"#### Test Merge {4}{0}{0}##### Server Instance{0}{5}{1}{0}{0}##### Revision{0}Origin: {6}{0}Pull Request: {2}{0}Server: {7}{3}{8}",
"#### Test Merge {4}{0}{0}<details><summary>Details</summary>{0}{0}##### Server Instance{0}{5}{1}{0}{0}##### Revision{0}Origin: {6}{0}Pull Request: {2}{0}Server: {7}{3}{8}{0}</details>",
Environment.NewLine,
repositorySettings.ShowTestMergeCommitters.Value
? String.Format(
@@ -155,5 +155,11 @@
/// </summary>
[EventScript("PreDreamMaker")]
PreDreamMaker,
/// <summary>
/// Whenever a deployment folder is deleted from disk. Parameters: Game directory path
/// </summary>
[EventScript("DeploymentCleanup")]
DeploymentCleanup,
}
}
@@ -286,6 +286,7 @@ namespace Tgstation.Server.Host.Components
databaseContextFactory,
gameIoManager,
remoteDeploymentManagerFactory,
eventConsumer,
loggerFactory.CreateLogger<DmbFactory>(),
metadata);
try
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Net;
@@ -57,16 +58,39 @@ namespace Tgstation.Server.Host.Controllers
/// Converts <paramref name="api"/> to a <see cref="ChatChannel"/>.
/// </summary>
/// <param name="api">The <see cref="Api.Models.ChatChannel"/>. </param>
/// <param name="chatProvider">The channel's <see cref="ChatProvider"/>.</param>
/// <returns>A <see cref="ChatChannel"/> based on <paramref name="api"/>.</returns>
static Models.ChatChannel ConvertApiChatChannel(Api.Models.ChatChannel api) => new ()
static Models.ChatChannel ConvertApiChatChannel(Api.Models.ChatChannel api, ChatProvider chatProvider)
{
DiscordChannelId = api.DiscordChannelId,
IrcChannel = api.IrcChannel,
IsAdminChannel = api.IsAdminChannel ?? false,
IsWatchdogChannel = api.IsWatchdogChannel ?? false,
IsUpdatesChannel = api.IsUpdatesChannel ?? false,
Tag = api.Tag,
};
var result = new Models.ChatChannel
{
#pragma warning disable CS0618
DiscordChannelId = api.DiscordChannelId,
IrcChannel = api.IrcChannel,
#pragma warning restore CS0618
IsAdminChannel = api.IsAdminChannel ?? false,
IsWatchdogChannel = api.IsWatchdogChannel ?? false,
IsUpdatesChannel = api.IsUpdatesChannel ?? false,
Tag = api.Tag,
};
if (api.ChannelData != null)
{
switch (chatProvider)
{
case ChatProvider.Discord:
result.DiscordChannelId = ulong.Parse(api.ChannelData, CultureInfo.InvariantCulture);
break;
case ChatProvider.Irc:
result.IrcChannel = api.ChannelData;
break;
default:
throw new InvalidOperationException($"Invalid chat provider: {chatProvider}");
}
}
return result;
}
/// <summary>
/// Create a new chat bot <paramref name="model"/>.
@@ -106,7 +130,7 @@ namespace Tgstation.Server.Host.Controllers
Name = model.Name,
ConnectionString = model.ConnectionString,
Enabled = model.Enabled,
Channels = model.Channels?.Select(x => ConvertApiChatChannel(x)).ToList() ?? new List<Models.ChatChannel>(), // important that this isn't null
Channels = model.Channels?.Select(x => ConvertApiChatChannel(x, model.Provider.Value)).ToList() ?? new List<Models.ChatChannel>(), // important that this isn't null
InstanceId = Instance.Id.Value,
Provider = model.Provider,
ReconnectionInterval = model.ReconnectionInterval,
@@ -319,7 +343,7 @@ namespace Tgstation.Server.Host.Controllers
DatabaseContext.ChatChannels.RemoveRange(current.Channels);
if (hasChannels)
{
var dbChannels = model.Channels.Select(x => ConvertApiChatChannel(x)).ToList();
var dbChannels = model.Channels.Select(x => ConvertApiChatChannel(x, model.Provider ?? current.Provider.Value)).ToList();
DatabaseContext.ChatChannels.AddRange(dbChannels);
current.Channels = dbChannels;
}
@@ -2,7 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using Tgstation.Server.Api.Models;
using Tgstation.Server.Host.Models;
namespace Tgstation.Server.Host.Extensions
{
+1 -1
View File
@@ -33,7 +33,7 @@ namespace Tgstation.Server.Host.Models
/// <inheritdoc />
public ChatBotResponse ToApi() => new ChatBotResponse
{
Channels = Channels.Select(x => x.ToApi()).ToList(),
Channels = Channels.Select(x => x.ToApi(Provider.Value)).ToList(),
ConnectionString = ConnectionString,
Enabled = Enabled,
Provider = Provider,
@@ -1,7 +1,12 @@
namespace Tgstation.Server.Host.Models
using System.ComponentModel.DataAnnotations;
using Tgstation.Server.Api.Models;
using Tgstation.Server.Api.Models.Internal;
namespace Tgstation.Server.Host.Models
{
/// <inheritdoc />
public sealed class ChatChannel : Api.Models.ChatChannel, IApiTransformable<Api.Models.ChatChannel>
public sealed class ChatChannel : ChatChannelBase
{
/// <summary>
/// The row Id.
@@ -9,23 +14,41 @@
public long Id { get; set; }
/// <summary>
/// The <see cref="Api.Models.EntityId.Id"/>.
/// The <see cref="EntityId.Id"/>.
/// </summary>
public long ChatSettingsId { get; set; }
/// <summary>
/// See <see cref="Api.Models.ChatChannel.IrcChannel"/>.
/// </summary>
[StringLength(Limits.MaximumIndexableStringLength, MinimumLength = 1)]
public string IrcChannel { get; set; }
/// <summary>
/// See <see cref="Api.Models.ChatChannel.DiscordChannelId"/>.
/// </summary>
public ulong? DiscordChannelId { get; set; }
/// <summary>
/// The <see cref="ChatBot"/>.
/// </summary>
public ChatBot ChatSettings { get; set; }
/// <inheritdoc />
public Api.Models.ChatChannel ToApi() => new Api.Models.ChatChannel
/// <summary>
/// Convert to a <see cref="Api.Models.ChatChannel"/>.
/// </summary>
/// <param name="chatProvider">The channel's <see cref="ChatProvider"/>.</param>
/// <returns>The converted <see cref="Api.Models.ChatChannel"/>.</returns>
public Api.Models.ChatChannel ToApi(ChatProvider chatProvider) => new Api.Models.ChatChannel
{
ChannelData = chatProvider == ChatProvider.Discord ? DiscordChannelId.ToString() : IrcChannel,
#pragma warning disable CS0618
IrcChannel = IrcChannel,
DiscordChannelId = DiscordChannelId,
#pragma warning restore CS0618
IsAdminChannel = IsAdminChannel,
IsWatchdogChannel = IsWatchdogChannel,
IsUpdatesChannel = IsUpdatesChannel,
IrcChannel = IrcChannel,
Tag = Tag,
};
}
@@ -22,7 +22,7 @@ namespace Tgstation.Server.Tests.Instance
{
sealed class ByondTest : JobsRequiredTest
{
public static readonly Version TestVersion = new (513, 1536);
public static readonly Version TestVersion = new (515, 1592);
readonly IByondClient byondClient;
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
@@ -81,7 +81,10 @@ namespace Tgstation.Server.Tests.Instance
IsUpdatesChannel = true,
IsWatchdogChannel = true,
Tag = "butt2",
IrcChannel = channelId
ChannelData = channelId,
#pragma warning disable CS0618
IrcChannel = "should_not_be_this!!!JHF*WW(#*(*$&(#*@))("
#pragma warning restore CS0618
}
}
}, cancellationToken);
@@ -93,8 +96,11 @@ namespace Tgstation.Server.Tests.Instance
Assert.AreEqual(true, updatedBot.Channels.First().IsUpdatesChannel);
Assert.AreEqual(true, updatedBot.Channels.First().IsWatchdogChannel);
Assert.AreEqual("butt2", updatedBot.Channels.First().Tag);
#pragma warning disable CS0618
Assert.AreEqual(channelId, updatedBot.Channels.First().IrcChannel);
Assert.IsNull(updatedBot.Channels.First().DiscordChannelId);
#pragma warning restore CS0618
Assert.AreEqual(channelId, updatedBot.Channels.First().ChannelData);
}
async Task RunDiscord(CancellationToken cancellationToken)
@@ -138,17 +144,6 @@ namespace Tgstation.Server.Tests.Instance
Assert.AreEqual(true, updatedBot.Enabled);
var channelId = UInt64.Parse(Environment.GetEnvironmentVariable("TGS_TEST_DISCORD_CHANNEL"));
firstBot.Channels = new List<ChatChannel>
{
new ChatChannel
{
IsAdminChannel = true,
IsUpdatesChannel = true,
IsWatchdogChannel = true,
Tag = "butt",
DiscordChannelId = channelId
}
};
updatedBot = await chatClient.Update(new ChatBotUpdateRequest
{
@@ -161,7 +156,10 @@ namespace Tgstation.Server.Tests.Instance
IsUpdatesChannel = true,
IsWatchdogChannel = true,
Tag = "butt",
DiscordChannelId = channelId
ChannelData = channelId.ToString(),
#pragma warning disable CS0618
DiscordChannelId = 1234,
#pragma warning restore CS0618
}
}
}, cancellationToken);
@@ -173,8 +171,11 @@ namespace Tgstation.Server.Tests.Instance
Assert.AreEqual(true, updatedBot.Channels.First().IsUpdatesChannel);
Assert.AreEqual(true, updatedBot.Channels.First().IsWatchdogChannel);
Assert.AreEqual("butt", updatedBot.Channels.First().Tag);
#pragma warning disable CS0618
Assert.AreEqual(channelId, updatedBot.Channels.First().DiscordChannelId);
Assert.IsNull(updatedBot.Channels.First().IrcChannel);
#pragma warning restore CS0618
Assert.AreEqual(channelId.ToString(), updatedBot.Channels.First().ChannelData);
}
public async Task RunPostTest(CancellationToken cancellationToken)
@@ -215,7 +216,7 @@ namespace Tgstation.Server.Tests.Instance
IsUpdatesChannel = false,
IsWatchdogChannel = true,
Tag = "butt",
DiscordChannelId = discordBotReq.Channels.First().DiscordChannelId
ChannelData = discordBotReq.Channels.First().ChannelData
});
await ApiAssert.ThrowsException<ApiConflictException>(() => chatClient.Update(discordBotReq, cancellationToken), ErrorCode.ChatBotMaxChannels);
@@ -204,13 +204,9 @@ static class Program
IsWatchdogChannel = providerInfo.WatchdogChannels.Any(x => NormalizeChannelId(x) == channelIdentifier),
IsAdminChannel = providerInfo.AdminChannels.Any(x => NormalizeChannelId(x) == channelIdentifier),
IsUpdatesChannel = providerInfo.DevChannels.Any(x => NormalizeChannelId(x) == channelIdentifier),
ChannelData = channelIdentifier,
};
if (isDiscordProvider)
newChatChannel.DiscordChannelId = ulong.Parse(channelIdentifier);
else
newChatChannel.IrcChannel = channelIdentifier;
createRequest.Channels.Add(newChatChannel);
}