mirror of
https://github.com/tgstation/tgstation-server.git
synced 2026-08-24 05:27:30 +01:00
Merge pull request #1515 from tgstation/1500-TheRealProblems
GitHub Abstraction Part 1: Centralizing API calls
This commit is contained in:
+155
-13
@@ -108,6 +108,13 @@ jobs:
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y -o APT::Immediate-Configure=0 libc6-i386 libstdc++6:i386 libgcc-s1:i386
|
||||
|
||||
- name: Restore BYOND cache
|
||||
uses: actions/cache@v3
|
||||
id: cache-byond
|
||||
with:
|
||||
path: $HOME/BYOND-${{ env.BYOND_MAJOR }}.${{ env.BYOND_MINOR }}
|
||||
key: ${{ env.BYOND_MAJOR }}-${{ env.BYOND_MINOR }}-dmapibyond
|
||||
|
||||
- name: Install BYOND
|
||||
if: steps.cache-byond.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
@@ -316,12 +323,30 @@ jobs:
|
||||
watchdog-type: [ 'Basic', 'System' ]
|
||||
configuration: [ 'Debug', 'Release' ]
|
||||
runs-on: windows-2019
|
||||
env:
|
||||
BYOND_MAJOR: 514
|
||||
BYOND_MINOR: 1588
|
||||
steps:
|
||||
- name: Setup dotnet
|
||||
uses: actions/setup-dotnet@v2
|
||||
with:
|
||||
dotnet-version: ${{ env.TGS_DOTNET_VERSION }}
|
||||
|
||||
- name: Restore BYOND cache
|
||||
uses: actions/cache@v3
|
||||
id: cache-byond
|
||||
with:
|
||||
path: $HOME/BYOND-${{ env.BYOND_MAJOR }}.${{ env.BYOND_MINOR }}
|
||||
key: ${{ env.BYOND_MAJOR }}-${{ env.BYOND_MINOR }}-livebyond
|
||||
|
||||
- name: Download BYOND
|
||||
if: steps.cache-byond.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
echo "Downloading BYOND..."
|
||||
mkdir -p "$HOME/BYOND-${{ env.BYOND_MAJOR }}.${{ env.BYOND_MINOR }}"
|
||||
cd "$HOME/BYOND-${{ env.BYOND_MAJOR }}.${{ env.BYOND_MINOR }}"
|
||||
curl "http://www.byond.com/download/build/${{ env.BYOND_MAJOR }}/${{ env.BYOND_MAJOR }}.${{ env.BYOND_MINOR }}_byond.zip" -o byond.zip
|
||||
|
||||
- name: Upgrade NPM
|
||||
run: npm install -g npm
|
||||
|
||||
@@ -403,7 +428,7 @@ jobs:
|
||||
- name: Store Code Coverage
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: windows-integration-test-coverage-${{ matrix.configuration }}-${{ matrix.watchdog-type }}
|
||||
name: windows-integration-test-coverage-${{ matrix.configuration }}-${{ matrix.watchdog-type }}-${{ matrix.database-type }}
|
||||
path: ./TestResults/
|
||||
|
||||
- name: Store OpenAPI Spec
|
||||
@@ -421,6 +446,7 @@ jobs:
|
||||
cd ../Tgstation.Server.Host
|
||||
dotnet publish -c ${{ matrix.configuration }} --no-build -o ../../Artifacts/Service/lib/Default
|
||||
mv ../../Artifacts/Service/lib/Default/appsettings.yml ../../Artifacts/Service/appsettings.yml
|
||||
rm ../../Artifacts/Service/lib/Default/Tgstation.Server.Host.exe
|
||||
|
||||
- name: Store Server Service
|
||||
if: ${{ matrix.configuration == 'Release' && matrix.watchdog-type == 'Basic' }}
|
||||
@@ -481,6 +507,9 @@ jobs:
|
||||
watchdog-type: [ 'Basic', 'System' ]
|
||||
configuration: [ 'Debug', 'Release' ]
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
BYOND_MAJOR: 514
|
||||
BYOND_MINOR: 1588
|
||||
steps:
|
||||
- name: Disable ptrace_scope
|
||||
run: echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
|
||||
@@ -491,6 +520,21 @@ jobs:
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y -o APT::Immediate-Configure=0 libc6-i386 libstdc++6:i386 gdb libgcc-s1:i386
|
||||
|
||||
- name: Restore BYOND cache
|
||||
uses: actions/cache@v3
|
||||
id: cache-byond
|
||||
with:
|
||||
path: $HOME/BYOND-${{ env.BYOND_MAJOR }}.${{ env.BYOND_MINOR }}
|
||||
key: ${{ env.BYOND_MAJOR }}-${{ env.BYOND_MINOR }}-livebyond
|
||||
|
||||
- name: Download BYOND
|
||||
if: steps.cache-byond.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
echo "Downloading BYOND..."
|
||||
mkdir -p "$HOME/BYOND-${{ env.BYOND_MAJOR }}.${{ env.BYOND_MINOR }}"
|
||||
cd "$HOME/BYOND-${{ env.BYOND_MAJOR }}.${{ env.BYOND_MINOR }}"
|
||||
curl "http://www.byond.com/download/build/${{ env.BYOND_MAJOR }}/${{ env.BYOND_MAJOR }}.${{ env.BYOND_MINOR }}_byond_linux.zip" -o byond.zip
|
||||
|
||||
- name: Install Node 12.X
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
@@ -566,6 +610,7 @@ jobs:
|
||||
cd ../Tgstation.Server.Host
|
||||
dotnet publish -c ${{ matrix.configuration }}NoService --no-build -o ../../Artifacts/Console/lib/Default
|
||||
mv ../../Artifacts/Console/lib/Default/appsettings.yml ../../Artifacts/Console/appsettings.yml
|
||||
rm ../../Artifacts/Console/lib/Default/Tgstation.Server.Host
|
||||
|
||||
- name: Package Server Update Package
|
||||
if: ${{ matrix.configuration == 'Release' && matrix.watchdog-type == 'System' && matrix.database-type == 'PostgresSql' }}
|
||||
@@ -573,6 +618,7 @@ jobs:
|
||||
cd src/Tgstation.Server.Host
|
||||
dotnet publish -c ${{ matrix.configuration }}NoService --no-build -o ../../Artifacts/ServerUpdate
|
||||
rm ../../Artifacts/ServerUpdate/appsettings.yml
|
||||
rm ../../Artifacts/ServerUpdate/Tgstation.Server.Host
|
||||
|
||||
- name: Store Server Console
|
||||
if: ${{ matrix.configuration == 'Release' && matrix.watchdog-type == 'System' && matrix.database-type == 'MariaDB' }}
|
||||
@@ -751,29 +797,125 @@ jobs:
|
||||
name: windows-unit-test-coverage-Release
|
||||
path: ./code_coverage/unit_tests/windows_unit_tests_release
|
||||
|
||||
- name: Retrieve Windows Integration Test Coverage (Debug, Basic)
|
||||
- name: Retrieve Windows Integration Test Coverage (Debug, Basic, SqlServer)
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: windows-integration-test-coverage-Debug-Basic
|
||||
path: ./code_coverage/integration_tests/windows_integration_tests_debug_basic
|
||||
name: windows-integration-test-coverage-Debug-Basic-SqlServer
|
||||
path: ./code_coverage/integration_tests/windows_integration_tests_debug_basic_sqlserver
|
||||
|
||||
- name: Retrieve Windows Integration Test Coverage (Release, Basic)
|
||||
- name: Retrieve Windows Integration Test Coverage (Release, Basic, SqlServer)
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: windows-integration-test-coverage-Release-Basic
|
||||
path: ./code_coverage/integration_tests/windows_integration_tests_release_basic
|
||||
name: windows-integration-test-coverage-Release-Basic-SqlServer
|
||||
path: ./code_coverage/integration_tests/windows_integration_tests_release_basic_sqlserver
|
||||
|
||||
- name: Retrieve Windows Integration Test Coverage (Debug, System)
|
||||
- name: Retrieve Windows Integration Test Coverage (Debug, System, SqlServer)
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: windows-integration-test-coverage-Debug-System
|
||||
path: ./code_coverage/integration_tests/windows_integration_tests_debug_system
|
||||
name: windows-integration-test-coverage-Debug-System-SqlServer
|
||||
path: ./code_coverage/integration_tests/windows_integration_tests_debug_system_sqlserver
|
||||
|
||||
- name: Retrieve Windows Integration Test Coverage (Release, System)
|
||||
- name: Retrieve Windows Integration Test Coverage (Release, System, SqlServer)
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: windows-integration-test-coverage-Release-System
|
||||
path: ./code_coverage/integration_tests/windows_integration_tests_release_system
|
||||
name: windows-integration-test-coverage-Release-System-SqlServer
|
||||
path: ./code_coverage/integration_tests/windows_integration_tests_release_system_sqlserver
|
||||
|
||||
- name: Retrieve Windows Integration Test Coverage (Debug, Basic, MariaDB)
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: windows-integration-test-coverage-Debug-Basic-MariaDB
|
||||
path: ./code_coverage/integration_tests/windows_integration_tests_debug_basic_mariadb
|
||||
|
||||
- name: Retrieve Windows Integration Test Coverage (Release, Basic, MariaDB)
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: windows-integration-test-coverage-Release-Basic-MariaDB
|
||||
path: ./code_coverage/integration_tests/windows_integration_tests_release_basic_mariadb
|
||||
|
||||
- name: Retrieve Windows Integration Test Coverage (Debug, System, MariaDB)
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: windows-integration-test-coverage-Debug-System-MariaDB
|
||||
path: ./code_coverage/integration_tests/windows_integration_tests_debug_system_mariadb
|
||||
|
||||
- name: Retrieve Windows Integration Test Coverage (Release, System, MariaDB)
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: windows-integration-test-coverage-Release-System-MariaDB
|
||||
path: ./code_coverage/integration_tests/windows_integration_tests_release_system_mariadb
|
||||
|
||||
- name: Retrieve Windows Integration Test Coverage (Debug, Basic, MySql)
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: windows-integration-test-coverage-Debug-Basic-MySql
|
||||
path: ./code_coverage/integration_tests/windows_integration_tests_debug_basic_mysql
|
||||
|
||||
- name: Retrieve Windows Integration Test Coverage (Release, Basic, MySql)
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: windows-integration-test-coverage-Release-Basic-MySql
|
||||
path: ./code_coverage/integration_tests/windows_integration_tests_release_basic_mysql
|
||||
|
||||
- name: Retrieve Windows Integration Test Coverage (Debug, System, MySql)
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: windows-integration-test-coverage-Debug-System-MySql
|
||||
path: ./code_coverage/integration_tests/windows_integration_tests_debug_system_mysql
|
||||
|
||||
- name: Retrieve Windows Integration Test Coverage (Release, System, MySql)
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: windows-integration-test-coverage-Release-System-MySql
|
||||
path: ./code_coverage/integration_tests/windows_integration_tests_release_system_mysql
|
||||
|
||||
- name: Retrieve Windows Integration Test Coverage (Debug, Basic, PostgresSql)
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: windows-integration-test-coverage-Debug-Basic-PostgresSql
|
||||
path: ./code_coverage/integration_tests/windows_integration_tests_debug_basic_postgressql
|
||||
|
||||
- name: Retrieve Windows Integration Test Coverage (Release, Basic, PostgresSql)
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: windows-integration-test-coverage-Release-Basic-PostgresSql
|
||||
path: ./code_coverage/integration_tests/windows_integration_tests_release_basic_postgressql
|
||||
|
||||
- name: Retrieve Windows Integration Test Coverage (Debug, System, PostgresSql)
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: windows-integration-test-coverage-Debug-System-PostgresSql
|
||||
path: ./code_coverage/integration_tests/windows_integration_tests_debug_system_postgressql
|
||||
|
||||
- name: Retrieve Windows Integration Test Coverage (Release, System, PostgresSql)
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: windows-integration-test-coverage-Release-System-PostgresSql
|
||||
path: ./code_coverage/integration_tests/windows_integration_tests_release_system_postgressql
|
||||
|
||||
- name: Retrieve Windows Integration Test Coverage (Debug, Basic, Sqlite)
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: windows-integration-test-coverage-Debug-Basic-Sqlite
|
||||
path: ./code_coverage/integration_tests/windows_integration_tests_debug_basic_sqlite
|
||||
|
||||
- name: Retrieve Windows Integration Test Coverage (Release, Basic, Sqlite)
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: windows-integration-test-coverage-Release-Basic-Sqlite
|
||||
path: ./code_coverage/integration_tests/windows_integration_tests_release_basic_sqlite
|
||||
|
||||
- name: Retrieve Windows Integration Test Coverage (Debug, System, Sqlite)
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: windows-integration-test-coverage-Debug-System-Sqlite
|
||||
path: ./code_coverage/integration_tests/windows_integration_tests_debug_system_sqlite
|
||||
|
||||
- name: Retrieve Windows Integration Test Coverage (Release, System, Sqlite)
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: windows-integration-test-coverage-Release-System-Sqlite
|
||||
path: ./code_coverage/integration_tests/windows_integration_tests_release_system_sqlite
|
||||
|
||||
- name: Upload Coverage to CodeCov
|
||||
uses: codecov/codecov-action@v3
|
||||
|
||||
@@ -4,8 +4,6 @@ using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
using Tgstation.Server.Host.Jobs;
|
||||
|
||||
namespace Tgstation.Server.Host.Components.Byond
|
||||
@@ -14,7 +12,7 @@ namespace Tgstation.Server.Host.Components.Byond
|
||||
/// For managing the BYOND installation.
|
||||
/// </summary>
|
||||
/// <remarks>When passing in <see cref="Version"/>s, ensure they are BYOND format versions unless referring to a custom version. This means <see cref="Version.Build"/> should NEVER be 0.</remarks>
|
||||
public interface IByondManager : IHostedService, IDisposable
|
||||
public interface IByondManager : IComponentService, IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// The currently active BYOND version.
|
||||
|
||||
@@ -3,8 +3,6 @@ using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
using Tgstation.Server.Api.Models.Internal;
|
||||
using Tgstation.Server.Host.Components.Interop;
|
||||
|
||||
@@ -13,7 +11,7 @@ namespace Tgstation.Server.Host.Components.Chat
|
||||
/// <summary>
|
||||
/// For managing connected chat services.
|
||||
/// </summary>
|
||||
public interface IChatManager : IHostedService, IAsyncDisposable
|
||||
public interface IChatManager : IComponentService, IAsyncDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Registers a <paramref name="customCommandHandler"/> to use.
|
||||
|
||||
@@ -90,7 +90,7 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
IDmbProvider nextDmbProvider;
|
||||
|
||||
/// <summary>
|
||||
/// If the <see cref="DmbFactory"/> is "started" via <see cref="Microsoft.Extensions.Hosting.IHostedService"/>.
|
||||
/// If the <see cref="DmbFactory"/> is "started" via <see cref="IComponentService"/>.
|
||||
/// </summary>
|
||||
bool started;
|
||||
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
using Tgstation.Server.Host.Models;
|
||||
|
||||
namespace Tgstation.Server.Host.Components.Deployment
|
||||
@@ -11,7 +9,7 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
/// <summary>
|
||||
/// Factory for <see cref="IDmbProvider"/>s.
|
||||
/// </summary>
|
||||
public interface IDmbFactory : ILatestCompileJobProvider, IHostedService, IDisposable
|
||||
public interface IDmbFactory : ILatestCompileJobProvider, IComponentService, IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Get a <see cref="Task"/> that completes when the result of a call to <see cref="LockNextDmb"/> will be different than the previous call if any.
|
||||
|
||||
+63
-79
@@ -8,13 +8,13 @@ using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using Octokit;
|
||||
|
||||
using Tgstation.Server.Host.Components.Repository;
|
||||
using Tgstation.Server.Host.Database;
|
||||
using Tgstation.Server.Host.Extensions;
|
||||
using Tgstation.Server.Host.Models;
|
||||
using Tgstation.Server.Host.Utils;
|
||||
using Tgstation.Server.Host.Utils.GitHub;
|
||||
|
||||
namespace Tgstation.Server.Host.Components.Deployment.Remote
|
||||
{
|
||||
@@ -29,26 +29,26 @@ namespace Tgstation.Server.Host.Components.Deployment.Remote
|
||||
readonly IDatabaseContextFactory databaseContextFactory;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IGitHubClientFactory"/> for the <see cref="GitHubRemoteDeploymentManager"/>.
|
||||
/// The <see cref="IGitHubServiceFactory"/> for the <see cref="GitHubRemoteDeploymentManager"/>.
|
||||
/// </summary>
|
||||
readonly IGitHubClientFactory gitHubClientFactory;
|
||||
readonly IGitHubServiceFactory gitHubServiceFactory;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GitHubRemoteDeploymentManager"/> class.
|
||||
/// </summary>
|
||||
/// <param name="databaseContextFactory">The value of <see cref="databaseContextFactory"/>.</param>
|
||||
/// <param name="gitHubClientFactory">The value of <see cref="gitHubClientFactory"/>.</param>
|
||||
/// <param name="gitHubServiceFactory">The value of <see cref="gitHubServiceFactory"/>.</param>
|
||||
/// <param name="logger">The <see cref="ILogger"/> for the <see cref="BaseRemoteDeploymentManager"/>.</param>
|
||||
/// <param name="metadata">The <see cref="Api.Models.Instance"/> for the <see cref="BaseRemoteDeploymentManager"/>.</param>
|
||||
public GitHubRemoteDeploymentManager(
|
||||
IDatabaseContextFactory databaseContextFactory,
|
||||
IGitHubClientFactory gitHubClientFactory,
|
||||
IGitHubServiceFactory gitHubServiceFactory,
|
||||
ILogger<GitHubRemoteDeploymentManager> logger,
|
||||
Api.Models.Instance metadata)
|
||||
: base(logger, metadata)
|
||||
{
|
||||
this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory));
|
||||
this.gitHubClientFactory = gitHubClientFactory ?? throw new ArgumentNullException(nameof(gitHubClientFactory));
|
||||
this.gitHubServiceFactory = gitHubServiceFactory ?? throw new ArgumentNullException(nameof(gitHubServiceFactory));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -74,15 +74,23 @@ namespace Tgstation.Server.Host.Components.Deployment.Remote
|
||||
.FirstAsync(cancellationToken));
|
||||
|
||||
var instanceAuthenticated = repositorySettings.AccessToken != null;
|
||||
var gitHubClient = !instanceAuthenticated
|
||||
? gitHubClientFactory.CreateClient()
|
||||
: gitHubClientFactory.CreateClient(repositorySettings.AccessToken);
|
||||
IAuthenticatedGitHubService authenticatedGitHubService;
|
||||
IGitHubService gitHubService;
|
||||
if (instanceAuthenticated)
|
||||
{
|
||||
authenticatedGitHubService = gitHubServiceFactory.CreateService(repositorySettings.AccessToken);
|
||||
gitHubService = authenticatedGitHubService;
|
||||
}
|
||||
else
|
||||
{
|
||||
authenticatedGitHubService = null;
|
||||
gitHubService = gitHubServiceFactory.CreateService();
|
||||
}
|
||||
|
||||
var repositoryTask = gitHubClient
|
||||
.Repository
|
||||
.Get(
|
||||
remoteInformation.RemoteRepositoryOwner,
|
||||
remoteInformation.RemoteRepositoryName);
|
||||
var repositoryIdTask = gitHubService.GetRepositoryId(
|
||||
remoteInformation.RemoteRepositoryOwner,
|
||||
remoteInformation.RemoteRepositoryName,
|
||||
cancellationToken);
|
||||
|
||||
if (!repositorySettings.CreateGitHubDeployments.Value)
|
||||
Logger.LogTrace("Not creating deployment");
|
||||
@@ -91,59 +99,45 @@ namespace Tgstation.Server.Host.Components.Deployment.Remote
|
||||
else
|
||||
{
|
||||
Logger.LogTrace("Creating deployment...");
|
||||
Octokit.Deployment deployment;
|
||||
|
||||
try
|
||||
{
|
||||
deployment = await gitHubClient
|
||||
.Repository
|
||||
.Deployment
|
||||
.Create(
|
||||
remoteInformation.RemoteRepositoryOwner,
|
||||
remoteInformation.RemoteRepositoryName,
|
||||
new NewDeployment(compileJob.RevisionInformation.CommitSha)
|
||||
{
|
||||
AutoMerge = false,
|
||||
Description = "TGS Game Deployment",
|
||||
Environment = $"TGS: {Metadata.Name}",
|
||||
ProductionEnvironment = true,
|
||||
RequiredContexts = new Collection<string>(),
|
||||
})
|
||||
.WithToken(cancellationToken);
|
||||
compileJob.GitHubDeploymentId = await authenticatedGitHubService.CreateDeployment(
|
||||
new NewDeployment(compileJob.RevisionInformation.CommitSha)
|
||||
{
|
||||
AutoMerge = false,
|
||||
Description = "TGS Game Deployment",
|
||||
Environment = $"TGS: {Metadata.Name}",
|
||||
ProductionEnvironment = true,
|
||||
RequiredContexts = new Collection<string>(),
|
||||
},
|
||||
remoteInformation.RemoteRepositoryOwner,
|
||||
remoteInformation.RemoteRepositoryName,
|
||||
cancellationToken);
|
||||
|
||||
Logger.LogDebug("Created deployment ID {deploymentId}", deployment.Id);
|
||||
Logger.LogDebug("Created deployment ID {deploymentId}", compileJob.GitHubDeploymentId);
|
||||
|
||||
await gitHubClient
|
||||
.Repository
|
||||
.Deployment
|
||||
.Status
|
||||
.Create(
|
||||
remoteInformation.RemoteRepositoryOwner,
|
||||
remoteInformation.RemoteRepositoryName,
|
||||
deployment.Id,
|
||||
new NewDeploymentStatus(DeploymentState.InProgress)
|
||||
{
|
||||
Description = "The project is being deployed",
|
||||
AutoInactive = false,
|
||||
})
|
||||
.WithToken(cancellationToken);
|
||||
await authenticatedGitHubService.CreateDeploymentStatus(
|
||||
new NewDeploymentStatus(DeploymentState.InProgress)
|
||||
{
|
||||
Description = "The project is being deployed",
|
||||
AutoInactive = false,
|
||||
},
|
||||
remoteInformation.RemoteRepositoryOwner,
|
||||
remoteInformation.RemoteRepositoryName,
|
||||
compileJob.GitHubDeploymentId.Value,
|
||||
cancellationToken);
|
||||
|
||||
compileJob.GitHubDeploymentId = deployment.Id;
|
||||
Logger.LogTrace("In-progress deployment status created");
|
||||
}
|
||||
catch (ApiException ex)
|
||||
{
|
||||
Logger.LogError(ex, "Unable to create deployment!");
|
||||
Logger.LogWarning(ex, "Unable to create deployment!");
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var gitHubRepo = await repositoryTask
|
||||
.WithToken(cancellationToken)
|
||||
;
|
||||
|
||||
compileJob.GitHubRepoId = gitHubRepo.Id;
|
||||
compileJob.GitHubRepoId = await repositoryIdTask;
|
||||
Logger.LogTrace("Set GitHub ID as {gitHubRepoId}", compileJob.GitHubRepoId);
|
||||
}
|
||||
catch (RateLimitExceededException ex) when (!repositorySettings.CreateGitHubDeployments.Value)
|
||||
@@ -206,16 +200,13 @@ namespace Tgstation.Server.Host.Components.Deployment.Remote
|
||||
return Array.Empty<TestMerge>();
|
||||
}
|
||||
|
||||
var gitHubClient = repositorySettings.AccessToken != null
|
||||
? gitHubClientFactory.CreateClient(repositorySettings.AccessToken)
|
||||
: gitHubClientFactory.CreateClient();
|
||||
var gitHubService = repositorySettings.AccessToken != null
|
||||
? gitHubServiceFactory.CreateService(repositorySettings.AccessToken)
|
||||
: gitHubServiceFactory.CreateService();
|
||||
|
||||
var tasks = revisionInformation
|
||||
.ActiveTestMerges
|
||||
.Select(x => gitHubClient
|
||||
.PullRequest
|
||||
.Get(repository.RemoteRepositoryOwner, repository.RemoteRepositoryName, x.TestMerge.Number)
|
||||
.WithToken(cancellationToken));
|
||||
.Select(x => gitHubService.GetPullRequest(repository.RemoteRepositoryOwner, repository.RemoteRepositoryName, x.TestMerge.Number, cancellationToken));
|
||||
try
|
||||
{
|
||||
await Task.WhenAll(tasks);
|
||||
@@ -260,13 +251,11 @@ namespace Tgstation.Server.Host.Components.Deployment.Remote
|
||||
int testMergeNumber,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var gitHubClient = gitHubClientFactory.CreateClient(repositorySettings.AccessToken);
|
||||
var gitHubService = gitHubServiceFactory.CreateService(repositorySettings.AccessToken);
|
||||
|
||||
try
|
||||
{
|
||||
await gitHubClient.Issue.Comment.Create(remoteRepositoryOwner, remoteRepositoryName, testMergeNumber, comment)
|
||||
.WithToken(cancellationToken)
|
||||
;
|
||||
await gitHubService.CommentOnIssue(remoteRepositoryOwner, remoteRepositoryName, comment, testMergeNumber, cancellationToken);
|
||||
}
|
||||
catch (ApiException e)
|
||||
{
|
||||
@@ -351,21 +340,16 @@ namespace Tgstation.Server.Host.Components.Deployment.Remote
|
||||
return;
|
||||
}
|
||||
|
||||
var gitHubClient = gitHubClientFactory.CreateClient(gitHubAccessToken);
|
||||
var gitHubService = gitHubServiceFactory.CreateService(gitHubAccessToken);
|
||||
|
||||
await gitHubClient
|
||||
.Repository
|
||||
.Deployment
|
||||
.Status
|
||||
.Create(
|
||||
compileJob.GitHubRepoId.Value,
|
||||
compileJob.GitHubDeploymentId.Value,
|
||||
new NewDeploymentStatus(deploymentState)
|
||||
{
|
||||
Description = description,
|
||||
})
|
||||
.WithToken(cancellationToken)
|
||||
;
|
||||
await gitHubService.CreateDeploymentStatus(
|
||||
new NewDeploymentStatus(deploymentState)
|
||||
{
|
||||
Description = description,
|
||||
},
|
||||
compileJob.GitHubRepoId.Value,
|
||||
compileJob.GitHubDeploymentId.Value,
|
||||
cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
-6
@@ -5,7 +5,7 @@ using Microsoft.Extensions.Logging;
|
||||
using Tgstation.Server.Api.Models;
|
||||
using Tgstation.Server.Host.Components.Repository;
|
||||
using Tgstation.Server.Host.Database;
|
||||
using Tgstation.Server.Host.Utils;
|
||||
using Tgstation.Server.Host.Utils.GitHub;
|
||||
|
||||
namespace Tgstation.Server.Host.Components.Deployment.Remote
|
||||
{
|
||||
@@ -20,7 +20,7 @@ namespace Tgstation.Server.Host.Components.Deployment.Remote
|
||||
/// <summary>
|
||||
/// The <see cref="IDatabaseContextFactory"/> for the <see cref="RemoteDeploymentManagerFactory"/>.
|
||||
/// </summary>
|
||||
readonly IGitHubClientFactory gitHubClientFactory;
|
||||
readonly IGitHubServiceFactory gitHubServiceFactory;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IGitRemoteFeaturesFactory"/> for the <see cref="RemoteDeploymentManagerFactory"/>.
|
||||
@@ -41,19 +41,19 @@ namespace Tgstation.Server.Host.Components.Deployment.Remote
|
||||
/// Initializes a new instance of the <see cref="RemoteDeploymentManagerFactory"/> class.
|
||||
/// </summary>
|
||||
/// <param name="databaseContextFactory">The value of <see cref="databaseContextFactory"/>.</param>
|
||||
/// <param name="gitHubClientFactory">The value of <see cref="gitHubClientFactory"/>.</param>
|
||||
/// <param name="gitHubServiceFactory">The value of <see cref="gitHubServiceFactory"/>.</param>
|
||||
/// <param name="gitRemoteFeaturesFactory">The value of <see cref="gitRemoteFeaturesFactory"/>.</param>
|
||||
/// <param name="loggerFactory">The value of <see cref="loggerFactory"/>.</param>
|
||||
/// <param name="logger">The value of <see cref="logger"/>.</param>
|
||||
public RemoteDeploymentManagerFactory(
|
||||
IDatabaseContextFactory databaseContextFactory,
|
||||
IGitHubClientFactory gitHubClientFactory,
|
||||
IGitHubServiceFactory gitHubServiceFactory,
|
||||
IGitRemoteFeaturesFactory gitRemoteFeaturesFactory,
|
||||
ILoggerFactory loggerFactory,
|
||||
ILogger<RemoteDeploymentManagerFactory> logger)
|
||||
{
|
||||
this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory));
|
||||
this.gitHubClientFactory = gitHubClientFactory ?? throw new ArgumentNullException(nameof(gitHubClientFactory));
|
||||
this.gitHubServiceFactory = gitHubServiceFactory ?? throw new ArgumentNullException(nameof(gitHubServiceFactory));
|
||||
this.gitRemoteFeaturesFactory = gitRemoteFeaturesFactory ?? throw new ArgumentNullException(nameof(gitRemoteFeaturesFactory));
|
||||
this.loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory));
|
||||
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
@@ -70,7 +70,7 @@ namespace Tgstation.Server.Host.Components.Deployment.Remote
|
||||
{
|
||||
RemoteGitProvider.GitHub => new GitHubRemoteDeploymentManager(
|
||||
databaseContextFactory,
|
||||
gitHubClientFactory,
|
||||
gitHubServiceFactory,
|
||||
loggerFactory.CreateLogger<GitHubRemoteDeploymentManager>(),
|
||||
metadata),
|
||||
RemoteGitProvider.GitLab => new GitLabRemoteDeploymentManager(
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
namespace Tgstation.Server.Host.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a component meant to be started and stopped by its parent component.
|
||||
/// </summary>
|
||||
public interface IComponentService : IHostedService
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,11 @@
|
||||
using System;
|
||||
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
namespace Tgstation.Server.Host.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// Component version of <see cref="IInstanceCore"/>.
|
||||
/// </summary>
|
||||
interface IInstance : IInstanceCore, IHostedService, IAsyncDisposable
|
||||
interface IInstance : IInstanceCore, IComponentService, IAsyncDisposable
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
using Tgstation.Server.Host.Components.Interop.Bridge;
|
||||
using Tgstation.Server.Host.IO;
|
||||
|
||||
@@ -10,7 +8,7 @@ namespace Tgstation.Server.Host.Components
|
||||
/// <summary>
|
||||
/// Factory for creating <see cref="IInstance"/>s.
|
||||
/// </summary>
|
||||
interface IInstanceFactory : IHostedService
|
||||
interface IInstanceFactory : IComponentService
|
||||
{
|
||||
/// <summary>
|
||||
/// Create an <see cref="IInstance"/>.
|
||||
|
||||
@@ -58,9 +58,9 @@ namespace Tgstation.Server.Host.Components
|
||||
readonly IAssemblyInformationProvider assemblyInformationProvider;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IJobManager"/> for the <see cref="InstanceManager"/>.
|
||||
/// The <see cref="IJobService"/> for the <see cref="InstanceManager"/>.
|
||||
/// </summary>
|
||||
readonly IJobManager jobManager;
|
||||
readonly IJobService jobService;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IServerControl"/> for the <see cref="InstanceManager"/>.
|
||||
@@ -149,7 +149,7 @@ namespace Tgstation.Server.Host.Components
|
||||
/// <param name="ioManager">The value of <paramref name="ioManager"/>.</param>
|
||||
/// <param name="databaseContextFactory">The value of <paramref name="databaseContextFactory"/>.</param>
|
||||
/// <param name="assemblyInformationProvider">The value of <see cref="assemblyInformationProvider"/>.</param>
|
||||
/// <param name="jobManager">The value of <see cref="jobManager"/>.</param>
|
||||
/// <param name="jobService">The value of <see cref="jobService"/>.</param>
|
||||
/// <param name="serverControl">The value of <see cref="serverControl"/>.</param>
|
||||
/// <param name="systemIdentityFactory">The value of <see cref="systemIdentityFactory"/>.</param>
|
||||
/// <param name="asyncDelayer">The value of <see cref="asyncDelayer"/>.</param>
|
||||
@@ -163,7 +163,7 @@ namespace Tgstation.Server.Host.Components
|
||||
IIOManager ioManager,
|
||||
IDatabaseContextFactory databaseContextFactory,
|
||||
IAssemblyInformationProvider assemblyInformationProvider,
|
||||
IJobManager jobManager,
|
||||
IJobService jobService,
|
||||
IServerControl serverControl,
|
||||
ISystemIdentityFactory systemIdentityFactory,
|
||||
IAsyncDelayer asyncDelayer,
|
||||
@@ -177,7 +177,7 @@ namespace Tgstation.Server.Host.Components
|
||||
this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
|
||||
this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory));
|
||||
this.assemblyInformationProvider = assemblyInformationProvider ?? throw new ArgumentNullException(nameof(assemblyInformationProvider));
|
||||
this.jobManager = jobManager ?? throw new ArgumentNullException(nameof(jobManager));
|
||||
this.jobService = jobService ?? throw new ArgumentNullException(nameof(jobService));
|
||||
this.serverControl = serverControl ?? throw new ArgumentNullException(nameof(serverControl));
|
||||
this.systemIdentityFactory = systemIdentityFactory ?? throw new ArgumentNullException(nameof(systemIdentityFactory));
|
||||
this.asyncDelayer = asyncDelayer ?? throw new ArgumentNullException(nameof(asyncDelayer));
|
||||
@@ -344,7 +344,7 @@ namespace Tgstation.Server.Host.Components
|
||||
})
|
||||
.ToListAsync(cancellationToken);
|
||||
foreach (var job in jobs)
|
||||
tasks.Add(jobManager.CancelJob(job, user, true, cancellationToken));
|
||||
tasks.Add(jobService.CancelJob(job, user, true, cancellationToken));
|
||||
});
|
||||
|
||||
await Task.WhenAll(tasks);
|
||||
@@ -444,7 +444,7 @@ namespace Tgstation.Server.Host.Components
|
||||
}
|
||||
|
||||
var instanceFactoryStopTask = instanceFactory.StopAsync(cancellationToken);
|
||||
await jobManager.StopAsync(cancellationToken);
|
||||
await jobService.StopAsync(cancellationToken);
|
||||
|
||||
async Task OfflineInstanceImmediate(IInstance instance, CancellationToken cancellationToken)
|
||||
{
|
||||
@@ -563,7 +563,7 @@ namespace Tgstation.Server.Host.Components
|
||||
.ToListAsync(cancellationToken));
|
||||
|
||||
var factoryStartup = instanceFactory.StartAsync(cancellationToken);
|
||||
var jobManagerStartup = jobManager.StartAsync(cancellationToken);
|
||||
var jobManagerStartup = jobService.StartAsync(cancellationToken);
|
||||
|
||||
await Task.WhenAll(instanceEnumeration, factoryStartup, jobManagerStartup);
|
||||
|
||||
@@ -582,7 +582,7 @@ namespace Tgstation.Server.Host.Components
|
||||
|
||||
await Task.WhenAll(instanceOnliningTasks);
|
||||
|
||||
jobManager.Activate(this);
|
||||
jobService.Activate(this);
|
||||
|
||||
logger.LogInformation("Server ready!");
|
||||
readyTcs.SetResult();
|
||||
|
||||
@@ -7,8 +7,7 @@ using Octokit;
|
||||
|
||||
using Tgstation.Server.Api.Models;
|
||||
using Tgstation.Server.Api.Models.Internal;
|
||||
using Tgstation.Server.Host.Extensions;
|
||||
using Tgstation.Server.Host.Utils;
|
||||
using Tgstation.Server.Host.Utils.GitHub;
|
||||
|
||||
namespace Tgstation.Server.Host.Components.Repository
|
||||
{
|
||||
@@ -33,20 +32,20 @@ namespace Tgstation.Server.Host.Components.Repository
|
||||
public override string RemoteRepositoryName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IGitHubClientFactory"/> for the <see cref="GitHubRemoteFeatures"/>.
|
||||
/// The <see cref="IGitHubServiceFactory"/> for the <see cref="GitHubRemoteFeatures"/>.
|
||||
/// </summary>
|
||||
readonly IGitHubClientFactory gitHubClientFactory;
|
||||
readonly IGitHubServiceFactory gitHubServiceFactory;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GitHubRemoteFeatures"/> class.
|
||||
/// </summary>
|
||||
/// <param name="gitHubClientFactory">The value of <see cref="gitHubClientFactory"/>.</param>
|
||||
/// <param name="gitHubServiceFactory">The value of <see cref="gitHubServiceFactory"/>.</param>
|
||||
/// <param name="logger">The <see cref="ILogger"/> for the <see cref="GitRemoteFeaturesBase"/>.</param>
|
||||
/// <param name="remoteUrl">The remote repository <see cref="Uri"/>.</param>
|
||||
public GitHubRemoteFeatures(IGitHubClientFactory gitHubClientFactory, ILogger<GitHubRemoteFeatures> logger, Uri remoteUrl)
|
||||
public GitHubRemoteFeatures(IGitHubServiceFactory gitHubServiceFactory, ILogger<GitHubRemoteFeatures> logger, Uri remoteUrl)
|
||||
: base(logger, remoteUrl)
|
||||
{
|
||||
this.gitHubClientFactory = gitHubClientFactory ?? throw new ArgumentNullException(nameof(gitHubClientFactory));
|
||||
this.gitHubServiceFactory = gitHubServiceFactory ?? throw new ArgumentNullException(nameof(gitHubServiceFactory));
|
||||
|
||||
if (remoteUrl == null)
|
||||
throw new ArgumentNullException(nameof(remoteUrl));
|
||||
@@ -63,20 +62,16 @@ namespace Tgstation.Server.Host.Components.Repository
|
||||
RepositorySettings repositorySettings,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var gitHubClient = repositorySettings.AccessToken != null
|
||||
? gitHubClientFactory.CreateClient(repositorySettings.AccessToken)
|
||||
: gitHubClientFactory.CreateClient();
|
||||
var gitHubService = repositorySettings.AccessToken != null
|
||||
? gitHubServiceFactory.CreateService(repositorySettings.AccessToken)
|
||||
: gitHubServiceFactory.CreateService();
|
||||
|
||||
PullRequest pr = null;
|
||||
ApiException exception = null;
|
||||
string errorMessage = null;
|
||||
try
|
||||
{
|
||||
pr = await gitHubClient
|
||||
.PullRequest
|
||||
.Get(RemoteRepositoryOwner, RemoteRepositoryName, parameters.Number)
|
||||
.WithToken(cancellationToken)
|
||||
;
|
||||
pr = await gitHubService.GetPullRequest(RemoteRepositoryOwner, RemoteRepositoryName, parameters.Number, cancellationToken);
|
||||
}
|
||||
catch (RateLimitExceededException ex)
|
||||
{
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using Tgstation.Server.Api.Models;
|
||||
using Tgstation.Server.Host.Utils;
|
||||
using Tgstation.Server.Host.Utils.GitHub;
|
||||
|
||||
namespace Tgstation.Server.Host.Components.Repository
|
||||
{
|
||||
@@ -11,9 +11,9 @@ namespace Tgstation.Server.Host.Components.Repository
|
||||
sealed class GitRemoteFeaturesFactory : IGitRemoteFeaturesFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="IGitHubClientFactory"/> for the <see cref="GitRemoteFeaturesFactory"/>.
|
||||
/// The <see cref="IGitHubServiceFactory"/> for the <see cref="GitRemoteFeaturesFactory"/>.
|
||||
/// </summary>
|
||||
readonly IGitHubClientFactory gitHubClientFactory;
|
||||
readonly IGitHubServiceFactory gitHubServiceFactory;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ILoggerFactory"/> for the <see cref="GitRemoteFeaturesFactory"/>.
|
||||
@@ -28,15 +28,15 @@ namespace Tgstation.Server.Host.Components.Repository
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GitRemoteFeaturesFactory"/> class.
|
||||
/// </summary>
|
||||
/// <param name="gitHubClientFactory">The value of <see cref="gitHubClientFactory"/>.</param>
|
||||
/// <param name="gitHubServiceFactory">The value of <see cref="gitHubServiceFactory"/>.</param>
|
||||
/// <param name="loggerFactory">The value of <see cref="loggerFactory"/>.</param>
|
||||
/// <param name="logger">The value of <see cref="logger"/>.</param>
|
||||
public GitRemoteFeaturesFactory(
|
||||
IGitHubClientFactory gitHubClientFactory,
|
||||
IGitHubServiceFactory gitHubServiceFactory,
|
||||
ILoggerFactory loggerFactory,
|
||||
ILogger<GitRemoteFeaturesFactory> logger)
|
||||
{
|
||||
this.gitHubClientFactory = gitHubClientFactory ?? throw new ArgumentNullException(nameof(gitHubClientFactory));
|
||||
this.gitHubServiceFactory = gitHubServiceFactory ?? throw new ArgumentNullException(nameof(gitHubServiceFactory));
|
||||
this.loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory));
|
||||
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
@@ -52,7 +52,7 @@ namespace Tgstation.Server.Host.Components.Repository
|
||||
return remoteGitProvider switch
|
||||
{
|
||||
RemoteGitProvider.GitHub => new GitHubRemoteFeatures(
|
||||
gitHubClientFactory,
|
||||
gitHubServiceFactory,
|
||||
loggerFactory.CreateLogger<GitHubRemoteFeatures>(),
|
||||
primaryRemote),
|
||||
RemoteGitProvider.GitLab => new GitLabRemoteFeatures(
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace Tgstation.Server.Host.Components.Repository
|
||||
var repo = await Task.Factory.StartNew<LibGit2Sharp.IRepository>(
|
||||
() =>
|
||||
{
|
||||
logger.LogTrace("Creating libgit2 repostory at {repoPath}...", path);
|
||||
logger.LogTrace("Creating libgit2 repository at {repoPath}...", path);
|
||||
return new LibGit2Sharp.Repository(path);
|
||||
},
|
||||
cancellationToken,
|
||||
|
||||
@@ -967,7 +967,8 @@ namespace Tgstation.Server.Host.Components.Repository
|
||||
OnPackBuilderProgress = (stage, current, total) =>
|
||||
{
|
||||
var baseProgress = stage == PackBuilderStage.Counting ? 0 : 0.5;
|
||||
progressReporter.ReportProgress(baseProgress + (0.5 * ((double)current / total)));
|
||||
var addon = total > 0 && current <= total ? (0.5 * ((double)current / total)) : 0;
|
||||
progressReporter.ReportProgress(baseProgress + addon);
|
||||
return !cancellationToken.IsCancellationRequested;
|
||||
},
|
||||
OnNegotiationCompletedBeforePush = (a) =>
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using LibGit2Sharp;
|
||||
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using Tgstation.Server.Api.Models;
|
||||
@@ -142,7 +143,7 @@ namespace Tgstation.Server.Host.Components.Repository
|
||||
{
|
||||
using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken))
|
||||
{
|
||||
logger.LogTrace("Semaphore acquired");
|
||||
logger.LogTrace("Semaphore acquired for clone");
|
||||
var repositoryPath = ioManager.ResolvePath();
|
||||
if (!await ioManager.DirectoryExists(repositoryPath, cancellationToken))
|
||||
try
|
||||
@@ -167,8 +168,7 @@ namespace Tgstation.Server.Host.Components.Repository
|
||||
url,
|
||||
cloneOptions,
|
||||
repositoryPath,
|
||||
cancellationToken)
|
||||
;
|
||||
cancellationToken);
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -192,14 +192,14 @@ namespace Tgstation.Server.Host.Components.Repository
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
logger.LogInformation("Clone complete!");
|
||||
}
|
||||
finally
|
||||
{
|
||||
logger.LogTrace("Semaphore released after clone");
|
||||
CloneInProgress = false;
|
||||
}
|
||||
|
||||
logger.LogInformation("Clone complete!");
|
||||
return await LoadRepository(cancellationToken);
|
||||
}
|
||||
|
||||
@@ -210,11 +210,13 @@ namespace Tgstation.Server.Host.Components.Repository
|
||||
lock (semaphore)
|
||||
if (CloneInProgress)
|
||||
throw new JobException(ErrorCode.RepoCloning);
|
||||
await semaphore.WaitAsync(cancellationToken);
|
||||
|
||||
try
|
||||
{
|
||||
await semaphore.WaitAsync(cancellationToken);
|
||||
try
|
||||
{
|
||||
logger.LogTrace("Semaphore acquired for load");
|
||||
var libGit2Repo = await repositoryFactory.CreateFromPath(ioManager.ResolvePath(), cancellationToken);
|
||||
|
||||
return new Repository(
|
||||
@@ -235,6 +237,7 @@ namespace Tgstation.Server.Host.Components.Repository
|
||||
}
|
||||
catch
|
||||
{
|
||||
logger.LogTrace("Releasing semaphore as load failed");
|
||||
semaphore.Release();
|
||||
throw;
|
||||
}
|
||||
@@ -250,10 +253,17 @@ namespace Tgstation.Server.Host.Components.Repository
|
||||
public async Task DeleteRepository(CancellationToken cancellationToken)
|
||||
{
|
||||
logger.LogInformation("Deleting repository...");
|
||||
using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken))
|
||||
try
|
||||
{
|
||||
logger.LogTrace("Semaphore acquired, deleting Repository directory...");
|
||||
await ioManager.DeleteDirectory(ioManager.ResolvePath(), cancellationToken);
|
||||
using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken))
|
||||
{
|
||||
logger.LogTrace("Semaphore acquired, deleting Repository directory...");
|
||||
await ioManager.DeleteDirectory(ioManager.ResolvePath(), cancellationToken);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
logger.LogTrace("Semaphore released after delete attempt");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,6 @@ using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
using Tgstation.Server.Api.Models;
|
||||
using Tgstation.Server.Api.Models.Response;
|
||||
using Tgstation.Server.Host.Components.Events;
|
||||
@@ -15,7 +13,7 @@ namespace Tgstation.Server.Host.Components.StaticFiles
|
||||
/// <summary>
|
||||
/// For managing the Configuration directory.
|
||||
/// </summary>
|
||||
public interface IConfiguration : IHostedService, IEventConsumer, IDisposable
|
||||
public interface IConfiguration : IComponentService, IEventConsumer, IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Copies all files in the CodeModifications directory to <paramref name="destination"/>.
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
using Tgstation.Server.Api.Models;
|
||||
using Tgstation.Server.Api.Models.Internal;
|
||||
using Tgstation.Server.Host.Components.Events;
|
||||
@@ -14,7 +12,7 @@ namespace Tgstation.Server.Host.Components.Watchdog
|
||||
/// <summary>
|
||||
/// Runs and monitors the twin server controllers.
|
||||
/// </summary>
|
||||
public interface IWatchdog : IHostedService, IAsyncDisposable, IEventConsumer, IRenameNotifyee
|
||||
public interface IWatchdog : IComponentService, IAsyncDisposable, IEventConsumer, IRenameNotifyee
|
||||
{
|
||||
/// <summary>
|
||||
/// The current <see cref="WatchdogStatus"/>.
|
||||
|
||||
@@ -39,11 +39,5 @@
|
||||
/// Asset package containing the new <see cref="Host"/> assembly in zip form.
|
||||
/// </summary>
|
||||
public string UpdatePackageAssetName { get; set; } = DefaultUpdatePackageAssetName;
|
||||
|
||||
/// <summary>
|
||||
/// Dump all retrieved releases from the GitHub API when a requested release is not found.
|
||||
/// </summary>
|
||||
/// <remarks>This is an internal config and may be adjusted or removed without a version change.</remarks>
|
||||
public bool DumpReleasesOnNotFound { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,12 +19,11 @@ using Tgstation.Server.Api.Rights;
|
||||
using Tgstation.Server.Host.Configuration;
|
||||
using Tgstation.Server.Host.Core;
|
||||
using Tgstation.Server.Host.Database;
|
||||
using Tgstation.Server.Host.Extensions;
|
||||
using Tgstation.Server.Host.IO;
|
||||
using Tgstation.Server.Host.Security;
|
||||
using Tgstation.Server.Host.System;
|
||||
using Tgstation.Server.Host.Transfer;
|
||||
using Tgstation.Server.Host.Utils;
|
||||
using Tgstation.Server.Host.Utils.GitHub;
|
||||
|
||||
namespace Tgstation.Server.Host.Controllers
|
||||
{
|
||||
@@ -40,9 +39,9 @@ namespace Tgstation.Server.Host.Controllers
|
||||
const string OctokitException = "Bad GitHub API response, check configuration!";
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IGitHubClientFactory"/> for the <see cref="AdministrationController"/>.
|
||||
/// The <see cref="IGitHubService"/> for the <see cref="AdministrationController"/>.
|
||||
/// </summary>
|
||||
readonly IGitHubClientFactory gitHubClientFactory;
|
||||
readonly IGitHubService gitHubService;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IServerControl"/> for the <see cref="AdministrationController"/>.
|
||||
@@ -74,11 +73,6 @@ namespace Tgstation.Server.Host.Controllers
|
||||
/// </summary>
|
||||
readonly IFileTransferTicketProvider fileTransferService;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="UpdatesConfiguration"/> for the <see cref="AdministrationController"/>.
|
||||
/// </summary>
|
||||
readonly UpdatesConfiguration updatesConfiguration;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="FileLoggingConfiguration"/> for the <see cref="AdministrationController"/>.
|
||||
/// </summary>
|
||||
@@ -89,7 +83,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
/// </summary>
|
||||
/// <param name="databaseContext">The <see cref="IDatabaseContext"/> for the <see cref="ApiController"/>.</param>
|
||||
/// <param name="authenticationContextFactory">The <see cref="IAuthenticationContextFactory"/> for the <see cref="ApiController"/>.</param>
|
||||
/// <param name="gitHubClientFactory">The value of <see cref="gitHubClientFactory"/>.</param>
|
||||
/// <param name="gitHubService">The value of <see cref="gitHubService"/>.</param>
|
||||
/// <param name="serverControl">The value of <see cref="serverControl"/>.</param>
|
||||
/// <param name="serverUpdateInitiator">The value of <see cref="serverUpdateInitiator"/>.</param>
|
||||
/// <param name="assemblyInformationProvider">The value of <see cref="assemblyInformationProvider"/>.</param>
|
||||
@@ -97,12 +91,11 @@ namespace Tgstation.Server.Host.Controllers
|
||||
/// <param name="platformIdentifier">The value of <see cref="platformIdentifier"/>.</param>
|
||||
/// <param name="fileTransferService">The value of <see cref="fileTransferService"/>.</param>
|
||||
/// <param name="logger">The <see cref="ILogger"/> for the <see cref="ApiController"/>.</param>
|
||||
/// <param name="updatesConfigurationOptions">The <see cref="IOptions{TOptions}"/> containing value of <see cref="updatesConfiguration"/>.</param>
|
||||
/// <param name="fileLoggingConfigurationOptions">The <see cref="IOptions{TOptions}"/> containing value of <see cref="fileLoggingConfiguration"/>.</param>
|
||||
public AdministrationController(
|
||||
IDatabaseContext databaseContext,
|
||||
IAuthenticationContextFactory authenticationContextFactory,
|
||||
IGitHubClientFactory gitHubClientFactory,
|
||||
IGitHubService gitHubService,
|
||||
IServerControl serverControl,
|
||||
IServerUpdateInitiator serverUpdateInitiator,
|
||||
IAssemblyInformationProvider assemblyInformationProvider,
|
||||
@@ -110,7 +103,6 @@ namespace Tgstation.Server.Host.Controllers
|
||||
IPlatformIdentifier platformIdentifier,
|
||||
IFileTransferTicketProvider fileTransferService,
|
||||
ILogger<AdministrationController> logger,
|
||||
IOptions<UpdatesConfiguration> updatesConfigurationOptions,
|
||||
IOptions<FileLoggingConfiguration> fileLoggingConfigurationOptions)
|
||||
: base(
|
||||
databaseContext,
|
||||
@@ -118,14 +110,13 @@ namespace Tgstation.Server.Host.Controllers
|
||||
logger,
|
||||
true)
|
||||
{
|
||||
this.gitHubClientFactory = gitHubClientFactory ?? throw new ArgumentNullException(nameof(gitHubClientFactory));
|
||||
this.gitHubService = gitHubService ?? throw new ArgumentNullException(nameof(gitHubService));
|
||||
this.serverControl = serverControl ?? throw new ArgumentNullException(nameof(serverControl));
|
||||
this.serverUpdateInitiator = serverUpdateInitiator ?? throw new ArgumentNullException(nameof(serverUpdateInitiator));
|
||||
this.assemblyInformationProvider = assemblyInformationProvider ?? throw new ArgumentNullException(nameof(assemblyInformationProvider));
|
||||
this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
|
||||
this.platformIdentifier = platformIdentifier ?? throw new ArgumentNullException(nameof(platformIdentifier));
|
||||
this.fileTransferService = fileTransferService ?? throw new ArgumentNullException(nameof(fileTransferService));
|
||||
updatesConfiguration = updatesConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(updatesConfigurationOptions));
|
||||
fileLoggingConfiguration = fileLoggingConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(fileLoggingConfigurationOptions));
|
||||
}
|
||||
|
||||
@@ -150,26 +141,19 @@ namespace Tgstation.Server.Host.Controllers
|
||||
Uri repoUrl = null;
|
||||
try
|
||||
{
|
||||
var gitHubClient = gitHubClientFactory.CreateClient();
|
||||
var repositoryTask = gitHubClient
|
||||
.Repository
|
||||
.Get(updatesConfiguration.GitHubRepositoryId)
|
||||
.WithToken(cancellationToken);
|
||||
var releases = (await gitHubClient
|
||||
.Repository
|
||||
.Release
|
||||
.GetAll(updatesConfiguration.GitHubRepositoryId)
|
||||
.WithToken(cancellationToken))
|
||||
.Where(x => x.TagName.StartsWith(
|
||||
updatesConfiguration.GitTagPrefix,
|
||||
StringComparison.InvariantCulture));
|
||||
var repositoryUrlTask = gitHubService.GetUpdatesRepositoryUrl(cancellationToken);
|
||||
var releases = await gitHubService.GetTgsReleases(cancellationToken);
|
||||
|
||||
foreach (var release in releases)
|
||||
if (Version.TryParse(release.TagName.Replace(updatesConfiguration.GitTagPrefix, String.Empty, StringComparison.Ordinal), out var version)
|
||||
&& version.Major > 3 // Forward/backward compatible but not before TGS4
|
||||
foreach (var kvp in releases)
|
||||
{
|
||||
var version = kvp.Key;
|
||||
var release = kvp.Value;
|
||||
if (version.Major > 3 // Forward/backward compatible but not before TGS4
|
||||
&& (greatestVersion == null || version > greatestVersion))
|
||||
greatestVersion = version;
|
||||
repoUrl = new Uri((await repositoryTask).HtmlUrl);
|
||||
}
|
||||
|
||||
repoUrl = await repositoryUrlTask;
|
||||
}
|
||||
catch (NotFoundException e)
|
||||
{
|
||||
|
||||
@@ -367,13 +367,15 @@ namespace Tgstation.Server.Host.Core
|
||||
// configure misc services
|
||||
services.AddSingleton<IProcessExecutor, ProcessExecutor>();
|
||||
services.AddSingleton<ISynchronousIOManager, SynchronousIOManager>();
|
||||
services.AddSingleton<IFileDownloader, FileDownloader>();
|
||||
services.AddFileDownloader();
|
||||
services.AddSingleton<IServerPortProvider, ServerPortProivder>();
|
||||
services.AddSingleton<ITopicClientFactory, TopicClientFactory>();
|
||||
services.AddSingleton<IGitHubClientFactory, GitHubClientFactory>();
|
||||
|
||||
services.AddGitHub();
|
||||
|
||||
// configure root services
|
||||
services.AddSingleton<IJobManager, JobManager>();
|
||||
services.AddSingleton<IJobService, JobService>();
|
||||
services.AddSingleton<IJobManager>(x => x.GetRequiredService<IJobService>());
|
||||
|
||||
services.AddSingleton<InstanceManager>();
|
||||
services.AddSingleton<IBridgeDispatcher>(x => x.GetRequiredService<InstanceManager>());
|
||||
|
||||
@@ -8,10 +8,9 @@ using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
using Tgstation.Server.Host.Configuration;
|
||||
using Tgstation.Server.Host.Extensions;
|
||||
using Tgstation.Server.Host.IO;
|
||||
using Tgstation.Server.Host.Swarm;
|
||||
using Tgstation.Server.Host.Utils;
|
||||
using Tgstation.Server.Host.Utils.GitHub;
|
||||
|
||||
namespace Tgstation.Server.Host.Core
|
||||
{
|
||||
@@ -19,9 +18,9 @@ namespace Tgstation.Server.Host.Core
|
||||
sealed class ServerUpdater : IServerUpdater, IServerUpdateExecutor
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="IGitHubClientFactory"/> for the <see cref="ServerUpdater"/>.
|
||||
/// The <see cref="IGitHubService"/> for the <see cref="ServerUpdater"/>.
|
||||
/// </summary>
|
||||
readonly IGitHubClientFactory gitHubClientFactory;
|
||||
readonly IGitHubService gitHubService;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IIOManager"/> for the <see cref="ServerUpdater"/>.
|
||||
@@ -61,7 +60,7 @@ namespace Tgstation.Server.Host.Core
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ServerUpdater"/> class.
|
||||
/// </summary>
|
||||
/// <param name="gitHubClientFactory">The value of <see cref="gitHubClientFactory"/>.</param>
|
||||
/// <param name="gitHubService">The value of <see cref="gitHubService"/>.</param>
|
||||
/// <param name="ioManager">The value of <see cref="ioManager"/>.</param>
|
||||
/// <param name="fileDownloader">The value of <see cref="fileDownloader"/>.</param>
|
||||
/// <param name="serverControl">The value of <see cref="serverControl"/>.</param>
|
||||
@@ -69,7 +68,7 @@ namespace Tgstation.Server.Host.Core
|
||||
/// <param name="generalConfigurationOptions">The <see cref="IOptions{TOptions}"/> containing the value of <see cref="generalConfiguration"/>.</param>
|
||||
/// <param name="updatesConfigurationOptions">The <see cref="IOptions{TOptions}"/> containing the value of <see cref="updatesConfiguration"/>.</param>
|
||||
public ServerUpdater(
|
||||
IGitHubClientFactory gitHubClientFactory,
|
||||
IGitHubService gitHubService,
|
||||
IIOManager ioManager,
|
||||
IFileDownloader fileDownloader,
|
||||
IServerControl serverControl,
|
||||
@@ -77,7 +76,7 @@ namespace Tgstation.Server.Host.Core
|
||||
IOptions<GeneralConfiguration> generalConfigurationOptions,
|
||||
IOptions<UpdatesConfiguration> updatesConfigurationOptions)
|
||||
{
|
||||
this.gitHubClientFactory = gitHubClientFactory ?? throw new ArgumentNullException(nameof(gitHubClientFactory));
|
||||
this.gitHubService = gitHubService ?? throw new ArgumentNullException(nameof(gitHubService));
|
||||
this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
|
||||
this.fileDownloader = fileDownloader ?? throw new ArgumentNullException(nameof(fileDownloader));
|
||||
this.serverControl = serverControl ?? throw new ArgumentNullException(nameof(serverControl));
|
||||
@@ -224,64 +223,38 @@ namespace Tgstation.Server.Host.Core
|
||||
async Task<ServerUpdateResult> BeginUpdateImpl(ISwarmService swarmService, Version newVersion, bool recursed, CancellationToken cancellationToken)
|
||||
{
|
||||
logger.LogDebug("Looking for GitHub releases version {version}...", newVersion);
|
||||
var gitHubClient = gitHubClientFactory.CreateClient();
|
||||
var releases = await gitHubClient
|
||||
.Repository
|
||||
.Release
|
||||
.GetAll(updatesConfiguration.GitHubRepositoryId)
|
||||
.WithToken(cancellationToken);
|
||||
|
||||
logger.LogTrace("Received {releaseCount} total releases from GitHub", releases.Count);
|
||||
|
||||
var filteredReleases = releases
|
||||
.Where(x => x.TagName.StartsWith(updatesConfiguration.GitTagPrefix, StringComparison.InvariantCulture))
|
||||
.ToList();
|
||||
|
||||
logger.LogTrace(
|
||||
"Filtered to {releaseCount} releases matching the configured tag prefix of \"{tagPrefix}\"",
|
||||
filteredReleases.Count,
|
||||
updatesConfiguration.GitTagPrefix);
|
||||
|
||||
foreach (var release in filteredReleases)
|
||||
if (Version.TryParse(
|
||||
release.TagName.Replace(
|
||||
updatesConfiguration.GitTagPrefix, String.Empty, StringComparison.Ordinal),
|
||||
out var version))
|
||||
var releases = await gitHubService.GetTgsReleases(cancellationToken);
|
||||
foreach (var kvp in releases)
|
||||
{
|
||||
var version = kvp.Key;
|
||||
var release = kvp.Value;
|
||||
if (version == newVersion)
|
||||
{
|
||||
if (version == newVersion)
|
||||
var asset = release.Assets.Where(x => x.Name.Equals(updatesConfiguration.UpdatePackageAssetName, StringComparison.Ordinal)).FirstOrDefault();
|
||||
if (asset == default)
|
||||
continue;
|
||||
|
||||
serverUpdateOperation = new ServerUpdateOperation
|
||||
{
|
||||
var asset = release.Assets.Where(x => x.Name.Equals(updatesConfiguration.UpdatePackageAssetName, StringComparison.Ordinal)).FirstOrDefault();
|
||||
if (asset == default)
|
||||
continue;
|
||||
TargetVersion = version,
|
||||
UpdateZipUrl = new Uri(asset.BrowserDownloadUrl),
|
||||
SwarmService = swarmService,
|
||||
};
|
||||
|
||||
serverUpdateOperation = new ServerUpdateOperation
|
||||
{
|
||||
TargetVersion = version,
|
||||
UpdateZipUrl = new Uri(asset.BrowserDownloadUrl),
|
||||
SwarmService = swarmService,
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
if (!serverControl.TryStartUpdate(this, version))
|
||||
return ServerUpdateResult.UpdateInProgress;
|
||||
}
|
||||
finally
|
||||
{
|
||||
serverUpdateOperation = null;
|
||||
}
|
||||
|
||||
return ServerUpdateResult.Started;
|
||||
try
|
||||
{
|
||||
if (!serverControl.TryStartUpdate(this, version))
|
||||
return ServerUpdateResult.UpdateInProgress;
|
||||
}
|
||||
finally
|
||||
{
|
||||
serverUpdateOperation = null;
|
||||
}
|
||||
}
|
||||
else
|
||||
logger.LogDebug("Unparsable release tag: {releaseTag}", release.TagName);
|
||||
|
||||
if (updatesConfiguration.DumpReleasesOnNotFound)
|
||||
logger.LogInformation(
|
||||
"Found releases:{newline}\t{releases}",
|
||||
Environment.NewLine,
|
||||
String.Join($"{Environment.NewLine}\t", releases.Select(x => x.TagName).OrderBy(x => x)));
|
||||
return ServerUpdateResult.Started;
|
||||
}
|
||||
}
|
||||
|
||||
if (!recursed)
|
||||
{
|
||||
|
||||
@@ -14,7 +14,9 @@ using Serilog.Sinks.Elasticsearch;
|
||||
|
||||
using Tgstation.Server.Host.Components.Chat.Providers;
|
||||
using Tgstation.Server.Host.Configuration;
|
||||
using Tgstation.Server.Host.IO;
|
||||
using Tgstation.Server.Host.Utils;
|
||||
using Tgstation.Server.Host.Utils.GitHub;
|
||||
|
||||
namespace Tgstation.Server.Host.Extensions
|
||||
{
|
||||
@@ -26,13 +28,31 @@ namespace Tgstation.Server.Host.Extensions
|
||||
/// <summary>
|
||||
/// The <see cref="IProviderFactory"/> implementation used in calls to <see cref="AddChatProviderFactory(IServiceCollection)"/>.
|
||||
/// </summary>
|
||||
static Type chatProviderFactoryType = typeof(ProviderFactory);
|
||||
static Type chatProviderFactoryType;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IGitHubServiceFactory"/> implementation used in calls to <see cref="AddGitHub(IServiceCollection)"/>.
|
||||
/// </summary>
|
||||
static Type gitHubServiceFactoryType;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IFileDownloader"/> implementation used in calls to <see cref="AddFileDownloader(IServiceCollection)"/>.
|
||||
/// </summary>
|
||||
static Type fileDownloaderType;
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="ServiceDescriptor"/> for an additional <see cref="ILoggerProvider"/> to use.
|
||||
/// </summary>
|
||||
static ServiceDescriptor additionalLoggerProvider;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes static members of the <see cref="ServiceCollectionExtensions"/> class.
|
||||
/// </summary>
|
||||
static ServiceCollectionExtensions()
|
||||
{
|
||||
UseDefaultServices();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Change the <see cref="Type"/> used as an implementation for calls to <see cref="AddChatProviderFactory(IServiceCollection)"/>.
|
||||
/// </summary>
|
||||
@@ -42,6 +62,56 @@ namespace Tgstation.Server.Host.Extensions
|
||||
chatProviderFactoryType = typeof(TProviderFactory);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Change the <see cref="Type"/> used as an implementation for calls to <see cref="AddGitHub(IServiceCollection)"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="TGitHubServiceFactory">The <see cref="IGitHubServiceFactory"/> implementation to use.</typeparam>
|
||||
public static void UseGitHubServiceFactory<TGitHubServiceFactory>() where TGitHubServiceFactory : IGitHubServiceFactory
|
||||
{
|
||||
gitHubServiceFactoryType = typeof(TGitHubServiceFactory);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Change the <see cref="Type"/> used as an implementation for calls to <see cref="AddGitHub(IServiceCollection)"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="TFileDownloader">The <see cref="IFileDownloader"/> implementation to use.</typeparam>
|
||||
public static void UseFileDownloader<TFileDownloader>() where TFileDownloader : IFileDownloader
|
||||
{
|
||||
fileDownloaderType = typeof(TFileDownloader);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a <see cref="IFileDownloader"/> implementation to the given <paramref name="serviceCollection"/>.
|
||||
/// </summary>
|
||||
/// <param name="serviceCollection">The <see cref="IServiceCollection"/> to configure.</param>
|
||||
/// <returns><paramref name="serviceCollection"/>.</returns>
|
||||
public static IServiceCollection AddFileDownloader(this IServiceCollection serviceCollection)
|
||||
{
|
||||
if (serviceCollection == null)
|
||||
throw new ArgumentNullException(nameof(serviceCollection));
|
||||
|
||||
serviceCollection.AddSingleton(typeof(IFileDownloader), fileDownloaderType);
|
||||
|
||||
return serviceCollection;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a <see cref="IGitHubServiceFactory"/> implementation to the given <paramref name="serviceCollection"/>.
|
||||
/// </summary>
|
||||
/// <param name="serviceCollection">The <see cref="IServiceCollection"/> to configure.</param>
|
||||
/// <returns><paramref name="serviceCollection"/>.</returns>
|
||||
public static IServiceCollection AddGitHub(this IServiceCollection serviceCollection)
|
||||
{
|
||||
if (serviceCollection == null)
|
||||
throw new ArgumentNullException(nameof(serviceCollection));
|
||||
|
||||
serviceCollection.AddSingleton<IGitHubClientFactory, GitHubClientFactory>();
|
||||
serviceCollection.AddSingleton(typeof(IGitHubServiceFactory), gitHubServiceFactoryType);
|
||||
serviceCollection.AddSingleton(x => x.GetRequiredService<IGitHubServiceFactory>().CreateService());
|
||||
|
||||
return serviceCollection;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add an additional <see cref="ILoggerProvider"/> to <see cref="IServiceCollection"/>s that call <see cref="SetupLogging(IServiceCollection, Action{LoggerConfiguration}, Action{LoggerSinkConfiguration}, ElasticsearchConfiguration)"/>.
|
||||
/// </summary>
|
||||
@@ -60,6 +130,9 @@ namespace Tgstation.Server.Host.Extensions
|
||||
/// <returns><paramref name="serviceCollection"/>.</returns>
|
||||
public static IServiceCollection AddChatProviderFactory(this IServiceCollection serviceCollection)
|
||||
{
|
||||
if (serviceCollection == null)
|
||||
throw new ArgumentNullException(nameof(serviceCollection));
|
||||
|
||||
return serviceCollection.AddSingleton(typeof(IProviderFactory), chatProviderFactoryType);
|
||||
}
|
||||
|
||||
@@ -154,5 +227,15 @@ namespace Tgstation.Server.Host.Extensions
|
||||
if (additionalLoggerProvider != null)
|
||||
builder.Services.TryAddEnumerable(additionalLoggerProvider);
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// Set the modifiable services to their default types.
|
||||
/// </summary>
|
||||
static void UseDefaultServices()
|
||||
{
|
||||
UseChatProviderFactory<ProviderFactory>();
|
||||
UseGitHubServiceFactory<GitHubServiceFactory>();
|
||||
UseFileDownloader<FileDownloader>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Tgstation.Server.Api.Models.Response;
|
||||
using Tgstation.Server.Host.Components;
|
||||
using Tgstation.Server.Host.Models;
|
||||
|
||||
namespace Tgstation.Server.Host.Jobs
|
||||
@@ -11,7 +9,7 @@ namespace Tgstation.Server.Host.Jobs
|
||||
/// <summary>
|
||||
/// Manages the runtime of <see cref="Job"/>s.
|
||||
/// </summary>
|
||||
public interface IJobManager : IHostedService
|
||||
public interface IJobManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Set the <see cref="JobResponse.Progress"/> and <see cref="JobResponse.Stage"/> for a given <paramref name="apiResponse"/>.
|
||||
@@ -47,11 +45,5 @@ namespace Tgstation.Server.Host.Jobs
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in the updated <paramref name="job"/> if it was cancelled, <see langword="null"/> if it couldn't be found.</returns>
|
||||
Task<Job> CancelJob(Job job, User user, bool blocking, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Activate the <see cref="IJobManager"/>.
|
||||
/// </summary>
|
||||
/// <param name="instanceCoreProvider">The <see cref="IInstanceCoreProvider"/> for the <see cref="IJobManager"/>.</param>
|
||||
void Activate(IInstanceCoreProvider instanceCoreProvider);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
using Tgstation.Server.Host.Components;
|
||||
|
||||
namespace Tgstation.Server.Host.Jobs
|
||||
{
|
||||
/// <summary>
|
||||
/// The service that manages everything to do with jobs.
|
||||
/// </summary>
|
||||
public interface IJobService : IJobManager, IComponentService
|
||||
{
|
||||
/// <summary>
|
||||
/// Activate the <see cref="IJobManager"/>.
|
||||
/// </summary>
|
||||
/// <param name="instanceCoreProvider">The <see cref="IInstanceCoreProvider"/> for the <see cref="IJobManager"/>.</param>
|
||||
void Activate(IInstanceCoreProvider instanceCoreProvider);
|
||||
}
|
||||
}
|
||||
+8
-8
@@ -17,22 +17,22 @@ using Tgstation.Server.Host.Utils;
|
||||
namespace Tgstation.Server.Host.Jobs
|
||||
{
|
||||
/// <inheritdoc />
|
||||
sealed class JobManager : IJobManager, IDisposable
|
||||
sealed class JobService : IJobService, IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="IServiceProvider"/> for the <see cref="JobManager"/>.
|
||||
/// The <see cref="IServiceProvider"/> for the <see cref="JobService"/>.
|
||||
/// </summary>
|
||||
readonly IDatabaseContextFactory databaseContextFactory;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ILoggerFactory"/> for the <see cref="JobManager"/>.
|
||||
/// The <see cref="ILoggerFactory"/> for the <see cref="JobService"/>.
|
||||
/// </summary>
|
||||
readonly ILoggerFactory loggerFactory;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ILogger"/> for the <see cref="JobManager"/>.
|
||||
/// The <see cref="ILogger"/> for the <see cref="JobService"/>.
|
||||
/// </summary>
|
||||
readonly ILogger<JobManager> logger;
|
||||
readonly ILogger<JobService> logger;
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="Dictionary{TKey, TValue}"/> of <see cref="Job"/> <see cref="Api.Models.EntityId.Id"/>s to running <see cref="JobHandler"/>s.
|
||||
@@ -60,15 +60,15 @@ namespace Tgstation.Server.Host.Jobs
|
||||
volatile bool noMoreJobsShouldStart;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="JobManager"/> class.
|
||||
/// Initializes a new instance of the <see cref="JobService"/> class.
|
||||
/// </summary>
|
||||
/// <param name="databaseContextFactory">The value of <see cref="databaseContextFactory"/>.</param>
|
||||
/// <param name="loggerFactory">The value of <see cref="loggerFactory"/>.</param>
|
||||
/// <param name="logger">The value of <see cref="logger"/>.</param>
|
||||
public JobManager(
|
||||
public JobService(
|
||||
IDatabaseContextFactory databaseContextFactory,
|
||||
ILoggerFactory loggerFactory,
|
||||
ILogger<JobManager> logger)
|
||||
ILogger<JobService> logger)
|
||||
{
|
||||
this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory));
|
||||
this.loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory));
|
||||
@@ -8,7 +8,7 @@ using Octokit;
|
||||
|
||||
using Tgstation.Server.Api.Models;
|
||||
using Tgstation.Server.Host.Configuration;
|
||||
using Tgstation.Server.Host.Utils;
|
||||
using Tgstation.Server.Host.Utils.GitHub;
|
||||
|
||||
namespace Tgstation.Server.Host.Security.OAuth
|
||||
{
|
||||
@@ -21,9 +21,9 @@ namespace Tgstation.Server.Host.Security.OAuth
|
||||
public OAuthProvider Provider => OAuthProvider.GitHub;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IGitHubClientFactory"/> for the <see cref="GitHubOAuthValidator"/>.
|
||||
/// The <see cref="IGitHubServiceFactory"/> for the <see cref="GitHubOAuthValidator"/>.
|
||||
/// </summary>
|
||||
readonly IGitHubClientFactory gitHubClientFactory;
|
||||
readonly IGitHubServiceFactory gitHubServiceFactory;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ILogger"/> for the <see cref="GitHubOAuthValidator"/>.
|
||||
@@ -38,15 +38,15 @@ namespace Tgstation.Server.Host.Security.OAuth
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GitHubOAuthValidator"/> class.
|
||||
/// </summary>
|
||||
/// <param name="gitHubClientFactory">The value of <see cref="gitHubClientFactory"/>.</param>
|
||||
/// <param name="gitHubServiceFactory">The value of <see cref="gitHubServiceFactory"/>.</param>
|
||||
/// <param name="logger">The value of <see cref="logger"/>.</param>
|
||||
/// <param name="oAuthConfiguration">The value of <see cref="oAuthConfiguration"/>.</param>
|
||||
public GitHubOAuthValidator(
|
||||
IGitHubClientFactory gitHubClientFactory,
|
||||
IGitHubServiceFactory gitHubServiceFactory,
|
||||
ILogger<GitHubOAuthValidator> logger,
|
||||
OAuthConfiguration oAuthConfiguration)
|
||||
{
|
||||
this.gitHubClientFactory = gitHubClientFactory ?? throw new ArgumentNullException(nameof(gitHubClientFactory));
|
||||
this.gitHubServiceFactory = gitHubServiceFactory ?? throw new ArgumentNullException(nameof(gitHubServiceFactory));
|
||||
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
this.oAuthConfiguration = oAuthConfiguration ?? throw new ArgumentNullException(nameof(oAuthConfiguration));
|
||||
}
|
||||
@@ -57,35 +57,21 @@ namespace Tgstation.Server.Host.Security.OAuth
|
||||
if (code == null)
|
||||
throw new ArgumentNullException(nameof(code));
|
||||
|
||||
var client = gitHubClientFactory.CreateClient();
|
||||
try
|
||||
{
|
||||
logger.LogTrace("Validating response code...");
|
||||
var response = await client
|
||||
.Oauth
|
||||
.CreateAccessToken(
|
||||
new OauthTokenRequest(
|
||||
oAuthConfiguration.ClientId,
|
||||
oAuthConfiguration.ClientSecret,
|
||||
code)
|
||||
{
|
||||
RedirectUri = oAuthConfiguration.RedirectUrl,
|
||||
})
|
||||
;
|
||||
|
||||
var token = response.AccessToken;
|
||||
var gitHubService = gitHubServiceFactory.CreateService();
|
||||
var token = await gitHubService.CreateOAuthAccessToken(oAuthConfiguration, code, cancellationToken);
|
||||
if (token == null)
|
||||
return null;
|
||||
|
||||
var authenticatedClient = gitHubClientFactory.CreateClient(token);
|
||||
var authenticatedClient = gitHubServiceFactory.CreateService(token);
|
||||
|
||||
logger.LogTrace("Getting user details...");
|
||||
var userDetails = await authenticatedClient
|
||||
.User
|
||||
.Current()
|
||||
;
|
||||
var userId = await authenticatedClient.GetCurrentUserId(cancellationToken);
|
||||
|
||||
return userDetails.Id.ToString(CultureInfo.InvariantCulture);
|
||||
return userId.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
catch (RateLimitExceededException)
|
||||
{
|
||||
|
||||
@@ -10,7 +10,7 @@ using Microsoft.Extensions.Options;
|
||||
using Tgstation.Server.Api.Models;
|
||||
using Tgstation.Server.Common;
|
||||
using Tgstation.Server.Host.Configuration;
|
||||
using Tgstation.Server.Host.Utils;
|
||||
using Tgstation.Server.Host.Utils.GitHub;
|
||||
|
||||
namespace Tgstation.Server.Host.Security.OAuth
|
||||
{
|
||||
@@ -25,12 +25,12 @@ namespace Tgstation.Server.Host.Security.OAuth
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="OAuthProviders"/> class.
|
||||
/// </summary>
|
||||
/// <param name="gitHubClientFactory">The <see cref="IGitHubClientFactory"/> to use.</param>
|
||||
/// <param name="gitHubServiceFactory">The <see cref="IGitHubServiceFactory"/> to use.</param>
|
||||
/// <param name="httpClientFactory">The <see cref="IAbstractHttpClientFactory"/> to use.</param>
|
||||
/// <param name="loggerFactory">The <see cref="ILoggerFactory"/> to use.</param>
|
||||
/// <param name="securityConfigurationOptions">The <see cref="IOptions{TOptions}"/> containing the <see cref="SecurityConfiguration"/> to use.</param>
|
||||
public OAuthProviders(
|
||||
IGitHubClientFactory gitHubClientFactory,
|
||||
IGitHubServiceFactory gitHubServiceFactory,
|
||||
IAbstractHttpClientFactory httpClientFactory,
|
||||
ILoggerFactory loggerFactory,
|
||||
IOptions<SecurityConfiguration> securityConfigurationOptions)
|
||||
@@ -49,7 +49,7 @@ namespace Tgstation.Server.Host.Security.OAuth
|
||||
if (securityConfiguration.OAuth.TryGetValue(OAuthProvider.GitHub, out var gitHubConfig))
|
||||
validatorsBuilder.Add(
|
||||
new GitHubOAuthValidator(
|
||||
gitHubClientFactory,
|
||||
gitHubServiceFactory,
|
||||
loggerFactory.CreateLogger<GitHubOAuthValidator>(),
|
||||
gitHubConfig));
|
||||
|
||||
|
||||
+4
-2
@@ -9,7 +9,7 @@ using Octokit;
|
||||
using Tgstation.Server.Host.Configuration;
|
||||
using Tgstation.Server.Host.System;
|
||||
|
||||
namespace Tgstation.Server.Host.Utils
|
||||
namespace Tgstation.Server.Host.Utils.GitHub
|
||||
{
|
||||
/// <inheritdoc />
|
||||
sealed class GitHubClientFactory : IGitHubClientFactory
|
||||
@@ -67,7 +67,9 @@ namespace Tgstation.Server.Host.Utils
|
||||
public IGitHubClient CreateClient() => GetOrCreateClient(generalConfiguration.GitHubAccessToken);
|
||||
|
||||
/// <inheritdoc />
|
||||
public IGitHubClient CreateClient(string accessToken) => GetOrCreateClient(accessToken ?? throw new ArgumentNullException(nameof(accessToken)));
|
||||
public IGitHubClient CreateClient(string accessToken)
|
||||
=> GetOrCreateClient(
|
||||
accessToken ?? throw new ArgumentNullException(nameof(accessToken)));
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve a <see cref="GitHubClient"/> from the <see cref="clientCache"/> or add a new one based on a given <paramref name="accessToken"/>.
|
||||
@@ -0,0 +1,280 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using Octokit;
|
||||
|
||||
using Tgstation.Server.Host.Configuration;
|
||||
using Tgstation.Server.Host.Extensions;
|
||||
|
||||
namespace Tgstation.Server.Host.Utils.GitHub
|
||||
{
|
||||
/// <summary>
|
||||
/// Service for interacting with the GitHub API. Authenticated or otherwise.
|
||||
/// </summary>
|
||||
sealed class GitHubService : IAuthenticatedGitHubService
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="IGitHubClient"/> for the <see cref="GitHubService"/>.
|
||||
/// </summary>
|
||||
readonly IGitHubClient gitHubClient;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ILogger"/> for the <see cref="GitHubService"/>.
|
||||
/// </summary>
|
||||
readonly ILogger<GitHubService> logger;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="UpdatesConfiguration"/> for the <see cref="GitHubService"/>.
|
||||
/// </summary>
|
||||
readonly UpdatesConfiguration updatesConfiguration;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GitHubService"/> class.
|
||||
/// </summary>
|
||||
/// <param name="gitHubClient">The value of <see cref="gitHubClient"/>.</param>
|
||||
/// <param name="logger">The value of <see cref="logger"/>.</param>
|
||||
/// <param name="updatesConfiguration">The value of <see cref="updatesConfiguration"/>.</param>
|
||||
public GitHubService(IGitHubClient gitHubClient, ILogger<GitHubService> logger, UpdatesConfiguration updatesConfiguration)
|
||||
{
|
||||
this.gitHubClient = gitHubClient ?? throw new ArgumentNullException(nameof(gitHubClient));
|
||||
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
this.updatesConfiguration = updatesConfiguration ?? throw new ArgumentNullException(nameof(updatesConfiguration));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<string> CreateOAuthAccessToken(OAuthConfiguration oAuthConfiguration, string code, CancellationToken cancellationToken)
|
||||
{
|
||||
if (oAuthConfiguration == null)
|
||||
throw new ArgumentNullException(nameof(oAuthConfiguration));
|
||||
|
||||
if (code == null)
|
||||
throw new ArgumentNullException(nameof(code));
|
||||
|
||||
logger.LogTrace("CreateOAuthAccessToken");
|
||||
|
||||
var response = await gitHubClient
|
||||
.Oauth
|
||||
.CreateAccessToken(
|
||||
new OauthTokenRequest(
|
||||
oAuthConfiguration.ClientId,
|
||||
oAuthConfiguration.ClientSecret,
|
||||
code)
|
||||
{
|
||||
RedirectUri = oAuthConfiguration.RedirectUrl,
|
||||
})
|
||||
.WithToken(cancellationToken);
|
||||
|
||||
var token = response.AccessToken;
|
||||
return token;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Dictionary<Version, Release>> GetTgsReleases(CancellationToken cancellationToken)
|
||||
{
|
||||
logger.LogTrace("GetTgsReleases");
|
||||
var allReleases = await gitHubClient
|
||||
.Repository
|
||||
.Release
|
||||
.GetAll(updatesConfiguration.GitHubRepositoryId)
|
||||
.WithToken(cancellationToken);
|
||||
|
||||
logger.LogTrace("{totalReleases} total releases", allReleases.Count);
|
||||
var releases = allReleases
|
||||
.Where(release =>
|
||||
{
|
||||
if (!release.PublishedAt.HasValue)
|
||||
{
|
||||
logger.LogDebug("Release tag without PublishedAt: {releaseTag}", release.TagName);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!release.TagName.StartsWith(updatesConfiguration.GitTagPrefix, StringComparison.InvariantCulture))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
})
|
||||
.GroupBy(release =>
|
||||
{
|
||||
if (!Version.TryParse(release.TagName.Replace(updatesConfiguration.GitTagPrefix, String.Empty, StringComparison.Ordinal), out var version))
|
||||
{
|
||||
logger.LogDebug("Unparsable release tag: {releaseTag}", release.TagName);
|
||||
return null;
|
||||
}
|
||||
|
||||
return version;
|
||||
})
|
||||
.Where(grouping => grouping.Key != null)
|
||||
|
||||
// GitHub can return the same result twice or some other nonsense
|
||||
.Select(grouping => Tuple.Create(grouping.Key, grouping.OrderBy(x => x.PublishedAt.Value).First()))
|
||||
.ToDictionary(tuple => tuple.Item1, tuple => tuple.Item2);
|
||||
|
||||
logger.LogTrace("{parsedReleases} parsed releases", releases.Count);
|
||||
return releases;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Uri> GetUpdatesRepositoryUrl(CancellationToken cancellationToken)
|
||||
{
|
||||
logger.LogTrace("GetUpdatesRepositoryUrl");
|
||||
var repository = await gitHubClient
|
||||
.Repository
|
||||
.Get(updatesConfiguration.GitHubRepositoryId)
|
||||
.WithToken(cancellationToken);
|
||||
|
||||
var repoUrl = new Uri(repository.HtmlUrl);
|
||||
logger.LogTrace("Maps to {repostioryUrl}", repoUrl);
|
||||
|
||||
return repoUrl;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<int> GetCurrentUserId(CancellationToken cancellationToken)
|
||||
{
|
||||
logger.LogTrace("CreateOAuthAccessToken");
|
||||
|
||||
var userDetails = await gitHubClient.User.Current().WithToken(cancellationToken);
|
||||
return userDetails.Id;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task CommentOnIssue(string repoOwner, string repoName, string comment, int issueNumber, CancellationToken cancellationToken)
|
||||
{
|
||||
if (repoOwner == null)
|
||||
throw new ArgumentNullException(nameof(repoOwner));
|
||||
|
||||
if (repoName == null)
|
||||
throw new ArgumentNullException(nameof(repoName));
|
||||
|
||||
if (comment == null)
|
||||
throw new ArgumentNullException(nameof(comment));
|
||||
|
||||
logger.LogTrace("CommentOnIssue");
|
||||
|
||||
return gitHubClient
|
||||
.Issue
|
||||
.Comment
|
||||
.Create(
|
||||
repoOwner,
|
||||
repoName,
|
||||
issueNumber,
|
||||
comment)
|
||||
.WithToken(cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<long> GetRepositoryId(string repoOwner, string repoName, CancellationToken cancellationToken)
|
||||
{
|
||||
if (repoOwner == null)
|
||||
throw new ArgumentNullException(nameof(repoOwner));
|
||||
|
||||
if (repoName == null)
|
||||
throw new ArgumentNullException(nameof(repoName));
|
||||
|
||||
logger.LogTrace("GetRepositoryId");
|
||||
|
||||
var repo = await gitHubClient
|
||||
.Repository
|
||||
.Get(
|
||||
repoOwner,
|
||||
repoName)
|
||||
.WithToken(cancellationToken);
|
||||
|
||||
return repo.Id;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<int> CreateDeployment(NewDeployment newDeployment, string repoOwner, string repoName, CancellationToken cancellationToken)
|
||||
{
|
||||
if (newDeployment == null)
|
||||
throw new ArgumentNullException(nameof(newDeployment));
|
||||
|
||||
if (repoOwner == null)
|
||||
throw new ArgumentNullException(nameof(repoOwner));
|
||||
|
||||
if (repoName == null)
|
||||
throw new ArgumentNullException(nameof(repoName));
|
||||
|
||||
logger.LogTrace("CreateDeployment");
|
||||
|
||||
var deployment = await gitHubClient
|
||||
.Repository
|
||||
.Deployment
|
||||
.Create(
|
||||
repoOwner,
|
||||
repoName,
|
||||
newDeployment)
|
||||
.WithToken(cancellationToken);
|
||||
|
||||
return deployment.Id;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task CreateDeploymentStatus(NewDeploymentStatus newDeploymentStatus, string repoOwner, string repoName, int deploymentId, CancellationToken cancellationToken)
|
||||
{
|
||||
if (newDeploymentStatus == null)
|
||||
throw new ArgumentNullException(nameof(newDeploymentStatus));
|
||||
|
||||
if (repoOwner == null)
|
||||
throw new ArgumentNullException(nameof(repoOwner));
|
||||
|
||||
if (repoName == null)
|
||||
throw new ArgumentNullException(nameof(repoName));
|
||||
|
||||
logger.LogTrace("CreateDeploymentStatus");
|
||||
return gitHubClient
|
||||
.Repository
|
||||
.Deployment
|
||||
.Status
|
||||
.Create(
|
||||
repoOwner,
|
||||
repoName,
|
||||
deploymentId,
|
||||
newDeploymentStatus)
|
||||
.WithToken(cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task CreateDeploymentStatus(NewDeploymentStatus newDeploymentStatus, long repoId, int deploymentId, CancellationToken cancellationToken)
|
||||
{
|
||||
if (newDeploymentStatus == null)
|
||||
throw new ArgumentNullException(nameof(newDeploymentStatus));
|
||||
|
||||
logger.LogTrace("CreateDeploymentStatus");
|
||||
return gitHubClient
|
||||
.Repository
|
||||
.Deployment
|
||||
.Status
|
||||
.Create(
|
||||
repoId,
|
||||
deploymentId,
|
||||
newDeploymentStatus)
|
||||
.WithToken(cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<PullRequest> GetPullRequest(string repoOwner, string repoName, int pullRequestNumber, CancellationToken cancellationToken)
|
||||
{
|
||||
if (repoOwner == null)
|
||||
throw new ArgumentNullException(nameof(repoOwner));
|
||||
|
||||
if (repoName == null)
|
||||
throw new ArgumentNullException(nameof(repoName));
|
||||
|
||||
logger.LogTrace("GetPullRequest");
|
||||
return gitHubClient
|
||||
.Repository
|
||||
.PullRequest
|
||||
.Get(
|
||||
repoOwner,
|
||||
repoName,
|
||||
pullRequestNumber)
|
||||
.WithToken(cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
using System;
|
||||
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
using Octokit;
|
||||
|
||||
using Tgstation.Server.Host.Configuration;
|
||||
|
||||
namespace Tgstation.Server.Host.Utils.GitHub
|
||||
{
|
||||
/// <inheritdoc />
|
||||
sealed class GitHubServiceFactory : IGitHubServiceFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="IGitHubClientFactory"/> for the <see cref="GitHubServiceFactory"/>.
|
||||
/// </summary>
|
||||
readonly IGitHubClientFactory gitHubClientFactory;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ILoggerFactory"/> for the <see cref="GitHubServiceFactory"/>.
|
||||
/// </summary>
|
||||
readonly ILoggerFactory loggerFactory;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="UpdatesConfiguration"/> for the <see cref="GitHubServiceFactory"/>.
|
||||
/// </summary>
|
||||
readonly UpdatesConfiguration updatesConfiguration;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GitHubServiceFactory"/> class.
|
||||
/// </summary>
|
||||
/// <param name="gitHubClientFactory">The value of <see cref="gitHubClientFactory"/>.</param>
|
||||
/// <param name="loggerFactory">The value of <see cref="loggerFactory"/>.</param>
|
||||
/// <param name="updatesConfigurationOptions">The <see cref="IOptions{TOptions}"/> containing value of <see cref="updatesConfiguration"/>.</param>
|
||||
public GitHubServiceFactory(
|
||||
IGitHubClientFactory gitHubClientFactory,
|
||||
ILoggerFactory loggerFactory,
|
||||
IOptions<UpdatesConfiguration> updatesConfigurationOptions)
|
||||
{
|
||||
this.gitHubClientFactory = gitHubClientFactory ?? throw new ArgumentNullException(nameof(gitHubClientFactory));
|
||||
this.loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory));
|
||||
updatesConfiguration = updatesConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(updatesConfigurationOptions));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IGitHubService CreateService() => CreateServiceImpl(gitHubClientFactory.CreateClient());
|
||||
|
||||
/// <inheritdoc />
|
||||
public IAuthenticatedGitHubService CreateService(string accessToken)
|
||||
=> CreateServiceImpl(
|
||||
gitHubClientFactory.CreateClient(
|
||||
accessToken ?? throw new ArgumentNullException(nameof(accessToken))));
|
||||
|
||||
/// <summary>
|
||||
/// Create a <see cref="GitHubService"/>.
|
||||
/// </summary>
|
||||
/// <param name="gitHubClient">The <see cref="IGitHubClient"/> for the <see cref="GitHubService"/>.</param>
|
||||
/// <returns>A new <see cref="GitHubService"/>.</returns>
|
||||
GitHubService CreateServiceImpl(IGitHubClient gitHubClient)
|
||||
=> new (
|
||||
gitHubClient,
|
||||
loggerFactory.CreateLogger<GitHubService>(),
|
||||
updatesConfiguration);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Octokit;
|
||||
|
||||
namespace Tgstation.Server.Host.Utils.GitHub
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="IGitHubService"/> that exposes functions that require authentication.
|
||||
/// </summary>
|
||||
public interface IAuthenticatedGitHubService : IGitHubService
|
||||
{
|
||||
/// <summary>
|
||||
/// Create a comment on a given <paramref name="issueNumber"/>.
|
||||
/// </summary>
|
||||
/// <param name="repoOwner">The owner of the target repository.</param>
|
||||
/// <param name="repoName">The name of the target repository.</param>
|
||||
/// <param name="comment">The text of the comment.</param>
|
||||
/// <param name="issueNumber">The number of the issue to comment on.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the running operation.</returns>
|
||||
Task CommentOnIssue(string repoOwner, string repoName, string comment, int issueNumber, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Create a <paramref name="newDeployment"/> on a target repostiory.
|
||||
/// </summary>
|
||||
/// <param name="newDeployment">The <see cref="NewDeployment"/>.</param>
|
||||
/// <param name="repoOwner">The owner of the target repository.</param>
|
||||
/// <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="Task{TResult}"/> resulting in the new deployment's ID.</returns>
|
||||
Task<int> CreateDeployment(NewDeployment newDeployment, string repoOwner, string repoName, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Create a <paramref name="newDeploymentStatus"/> on a target deployment.
|
||||
/// </summary>
|
||||
/// <param name="newDeploymentStatus">The <see cref="NewDeploymentStatus"/>.</param>
|
||||
/// <param name="repoOwner">The owner of the target repository.</param>
|
||||
/// <param name="repoName">The name of the target repository.</param>
|
||||
/// <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);
|
||||
|
||||
/// <summary>
|
||||
/// Create a <paramref name="newDeploymentStatus"/> on a target deployment.
|
||||
/// </summary>
|
||||
/// <param name="newDeploymentStatus">The <see cref="NewDeploymentStatus"/>.</param>
|
||||
/// <param name="repoId">The ID of the target repository.</param>
|
||||
/// <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);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
using Octokit;
|
||||
|
||||
namespace Tgstation.Server.Host.Utils
|
||||
namespace Tgstation.Server.Host.Utils.GitHub
|
||||
{
|
||||
/// <summary>
|
||||
/// For creating <see cref="IGitHubClient"/>s.
|
||||
@@ -0,0 +1,67 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Octokit;
|
||||
|
||||
using Tgstation.Server.Host.Configuration;
|
||||
|
||||
namespace Tgstation.Server.Host.Utils.GitHub
|
||||
{
|
||||
/// <summary>
|
||||
/// Service for interacting with the GitHub API.
|
||||
/// </summary>
|
||||
public interface IGitHubService
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the <see cref="Uri"/> of the repository designated as the updates repository.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in the <see cref="Uri"/> of the designated updates repository.</returns>
|
||||
Task<Uri> GetUpdatesRepositoryUrl(CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Get all valid TGS <see cref="Release"/>s from the configured update source.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in a <see cref="Dictionary{TKey, TValue}"/> of TGS <see cref="Release"/>s keyed by their <see cref="Version"/>.</returns>
|
||||
/// <remarks>GitHub has been known to return incomplete results from the API with this call.</remarks>
|
||||
Task<Dictionary<Version, Release>> GetTgsReleases(CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to get an OAuth token from a given <paramref name="code"/>.
|
||||
/// </summary>
|
||||
/// <param name="oAuthConfiguration">The <see cref="OAuthConfiguration"/>. Must have <see cref="OAuthConfiguration.RedirectUrl"/>, <see cref="OAuthConfigurationBase.ClientId"/> and <see cref="OAuthConfigurationBase.ClientSecret"/> set.</param>
|
||||
/// <param name="code">The OAuth response code.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in a <see cref="string"/> representing the returned OAuth code from GitHub on success, <see langword="null"/> otherwise.</returns>
|
||||
Task<string> CreateOAuthAccessToken(OAuthConfiguration oAuthConfiguration, string code, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Get a target repostiory's ID.
|
||||
/// </summary>
|
||||
/// <param name="repoOwner">The owner of the target repository.</param>
|
||||
/// <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="Task{TResult}"/> resulting in the target repository's ID.</returns>
|
||||
Task<long> GetRepositoryId(string repoOwner, string repoName, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Get the current user's ID.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in the current user's ID.</returns>
|
||||
Task<int> GetCurrentUserId(CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Get a given <paramref name="pullRequestNumber"/>.
|
||||
/// </summary>
|
||||
/// <param name="repoOwner">The owner of the target repository.</param>
|
||||
/// <param name="repoName">The name of the target repository.</param>
|
||||
/// <param name="pullRequestNumber">The target <see cref="PullRequest.Number"/>.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in the target <see cref="PullRequest"/>.</returns>
|
||||
Task<PullRequest> GetPullRequest(string repoOwner, string repoName, int pullRequestNumber, CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
namespace Tgstation.Server.Host.Utils.GitHub
|
||||
{
|
||||
/// <summary>
|
||||
/// Factory for <see cref="IGitHubService"/>s.
|
||||
/// </summary>
|
||||
public interface IGitHubServiceFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Create a <see cref="IGitHubService"/>.
|
||||
/// </summary>
|
||||
/// <returns>A new <see cref="IGitHubService"/>.</returns>
|
||||
public IGitHubService CreateService();
|
||||
|
||||
/// <summary>
|
||||
/// Create an <see cref="IAuthenticatedGitHubService"/>.
|
||||
/// </summary>
|
||||
/// <param name="accessToken">The access token to use for communication with GitHub.</param>
|
||||
/// <returns>A new <see cref="IAuthenticatedGitHubService"/>.</returns>
|
||||
public IAuthenticatedGitHubService CreateService(string accessToken);
|
||||
}
|
||||
}
|
||||
@@ -18,11 +18,11 @@ namespace Tgstation.Server.Host.Components.Chat.Providers.Tests
|
||||
[TestClass]
|
||||
public sealed class TestDiscordProvider
|
||||
{
|
||||
ChatBot testToken1;
|
||||
IJobManager mockJobManager;
|
||||
static ChatBot testToken1;
|
||||
static IJobManager mockJobManager;
|
||||
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
[ClassInitialize]
|
||||
public static void Initialize(TestContext _)
|
||||
{
|
||||
var actualToken = Environment.GetEnvironmentVariable("TGS_TEST_DISCORD_TOKEN");
|
||||
if (!String.IsNullOrWhiteSpace(actualToken))
|
||||
|
||||
@@ -10,10 +10,10 @@ namespace Tgstation.Server.Host.IO.Tests
|
||||
[TestClass]
|
||||
public sealed class TestSymlinkFactory
|
||||
{
|
||||
ISymlinkFactory symlinkFactory;
|
||||
static ISymlinkFactory symlinkFactory;
|
||||
|
||||
[TestInitialize]
|
||||
public void SelectFactory()
|
||||
[ClassInitialize]
|
||||
public static void SelectFactory(TestContext _)
|
||||
{
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
symlinkFactory = new WindowsSymlinkFactory();
|
||||
|
||||
@@ -13,12 +13,12 @@ namespace Tgstation.Server.Host.Swarm.Tests
|
||||
[TestClass]
|
||||
public sealed class TestSwarmProtocol
|
||||
{
|
||||
readonly HashSet<ushort> usedPorts = new ();
|
||||
ILoggerFactory loggerFactory;
|
||||
ILogger<TestSwarmProtocol> logger;
|
||||
static readonly HashSet<ushort> usedPorts = new ();
|
||||
static ILoggerFactory loggerFactory;
|
||||
static ILogger<TestSwarmProtocol> logger;
|
||||
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
[ClassInitialize]
|
||||
public static void Initialize(TestContext _)
|
||||
{
|
||||
loggerFactory = LoggerFactory.Create(builder =>
|
||||
{
|
||||
@@ -29,8 +29,8 @@ namespace Tgstation.Server.Host.Swarm.Tests
|
||||
logger = loggerFactory.CreateLogger<TestSwarmProtocol>();
|
||||
}
|
||||
|
||||
[TestCleanup]
|
||||
public void Shutdown()
|
||||
[ClassCleanup]
|
||||
public static void Shutdown()
|
||||
{
|
||||
usedPorts.Clear();
|
||||
loggerFactory.Dispose();
|
||||
|
||||
@@ -15,10 +15,10 @@ namespace Tgstation.Server.Host.System.Tests
|
||||
[TestClass]
|
||||
public sealed class TestProcessFeatures
|
||||
{
|
||||
IProcessFeatures features;
|
||||
static IProcessFeatures features;
|
||||
|
||||
[TestInitialize]
|
||||
public void Init()
|
||||
[ClassInitialize]
|
||||
public static void Init(TestContext _)
|
||||
{
|
||||
features = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
|
||||
? (IProcessFeatures)new WindowsProcessFeatures(Mock.Of<ILogger<WindowsProcessFeatures>>())
|
||||
|
||||
+6
-6
@@ -13,15 +13,15 @@ using Octokit;
|
||||
using Tgstation.Server.Host.Configuration;
|
||||
using Tgstation.Server.Host.System;
|
||||
|
||||
namespace Tgstation.Server.Host.Utils.Tests
|
||||
namespace Tgstation.Server.Host.Utils.GitHub.Tests
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class TestGitHubClientFactory
|
||||
{
|
||||
ILoggerFactory loggerFactory;
|
||||
static ILoggerFactory loggerFactory;
|
||||
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
[ClassInitialize]
|
||||
public static void Initialize(TestContext _)
|
||||
{
|
||||
loggerFactory = LoggerFactory.Create(builder =>
|
||||
{
|
||||
@@ -30,8 +30,8 @@ namespace Tgstation.Server.Host.Utils.Tests
|
||||
});
|
||||
}
|
||||
|
||||
[TestCleanup]
|
||||
public void Cleanup()
|
||||
[ClassCleanup]
|
||||
public static void Cleanup()
|
||||
{
|
||||
loggerFactory.Dispose();
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
using System;
|
||||
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
using Moq;
|
||||
|
||||
using Octokit;
|
||||
|
||||
using Tgstation.Server.Host.Configuration;
|
||||
|
||||
namespace Tgstation.Server.Host.Utils.GitHub.Tests
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class TestGitHubServiceFactory
|
||||
{
|
||||
[TestMethod]
|
||||
public void TestConstructor()
|
||||
{
|
||||
Assert.ThrowsException<ArgumentNullException>(() => new GitHubServiceFactory(null, null, null));
|
||||
Assert.ThrowsException<ArgumentNullException>(() => new GitHubServiceFactory(Mock.Of<IGitHubClientFactory>(), null, null));
|
||||
Assert.ThrowsException<ArgumentNullException>(() => new GitHubServiceFactory(Mock.Of<IGitHubClientFactory>(), Mock.Of<ILoggerFactory>(), null));
|
||||
var mockOptions = new Mock<IOptions<UpdatesConfiguration>>();
|
||||
mockOptions.SetupGet(x => x.Value).Returns(new UpdatesConfiguration());
|
||||
|
||||
_ = new GitHubServiceFactory(Mock.Of<IGitHubClientFactory>(), Mock.Of<ILoggerFactory>(), mockOptions.Object);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestCreateService()
|
||||
{
|
||||
var mockFactory = new Mock<IGitHubClientFactory>();
|
||||
|
||||
mockFactory.Setup(x => x.CreateClient()).Returns(Mock.Of<IGitHubClient>()).Verifiable();
|
||||
|
||||
var mockToken = "asdf";
|
||||
mockFactory.Setup(x => x.CreateClient(mockToken)).Returns(Mock.Of<IGitHubClient>()).Verifiable();
|
||||
|
||||
var mockOptions = new Mock<IOptions<UpdatesConfiguration>>();
|
||||
mockOptions.SetupGet(x => x.Value).Returns(new UpdatesConfiguration());
|
||||
|
||||
var factory = new GitHubServiceFactory(mockFactory.Object, Mock.Of<ILoggerFactory>(), mockOptions.Object);
|
||||
|
||||
Assert.ThrowsException<ArgumentNullException>(() => factory.CreateService(null));
|
||||
Assert.AreEqual(0, mockFactory.Invocations.Count);
|
||||
|
||||
var result1 = factory.CreateService();
|
||||
Assert.IsNotNull(result1);
|
||||
|
||||
var result2 = factory.CreateService(mockToken);
|
||||
Assert.IsNotNull(result2);
|
||||
|
||||
mockFactory.VerifyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
using Tgstation.Server.Common;
|
||||
using Tgstation.Server.Host.Extensions;
|
||||
using Tgstation.Server.Host.IO;
|
||||
using Tgstation.Server.Host.System;
|
||||
using Tgstation.Server.Tests.Live.Instance;
|
||||
|
||||
namespace Tgstation.Server.Tests.Live
|
||||
{
|
||||
sealed class CachingFileDownloader : IFileDownloader
|
||||
{
|
||||
static readonly Dictionary<string, Tuple<string, bool>> cachedPaths = new Dictionary<string, Tuple<string, bool>>();
|
||||
|
||||
readonly ILogger<CachingFileDownloader> logger;
|
||||
|
||||
public CachingFileDownloader(ILogger<CachingFileDownloader> logger)
|
||||
{
|
||||
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
logger.LogTrace("Created");
|
||||
}
|
||||
|
||||
public static async Task InitializeAndInject(CancellationToken cancellationToken)
|
||||
{
|
||||
if (LiveTestUtils.RunningInGitHubActions)
|
||||
{
|
||||
// actions caches BYOND for us
|
||||
var url = new Uri(
|
||||
$"https://secure.byond.com/download/build/{ByondTest.TestVersion.Major}/{ByondTest.TestVersion.Major}.{ByondTest.TestVersion.Minor}_byond{(!new PlatformIdentifier().IsWindows ? "_linux" : String.Empty)}.zip");
|
||||
|
||||
var path = Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
|
||||
$"BYOND-{ByondTest.TestVersion.Major}.{ByondTest.TestVersion.Minor}",
|
||||
"byond.zip");
|
||||
Assert.IsTrue(File.Exists(path));
|
||||
System.Console.WriteLine($"CACHE PREWARMED: {url}");
|
||||
cachedPaths.Add(url.ToString(), Tuple.Create(path, false));
|
||||
}
|
||||
|
||||
// predownload the target github release update asset
|
||||
var gitHubToken = Environment.GetEnvironmentVariable("TGS_TEST_GITHUB_TOKEN");
|
||||
if (String.IsNullOrWhiteSpace(gitHubToken))
|
||||
gitHubToken = null;
|
||||
|
||||
// this can fail, try a few times
|
||||
var succeeded = false;
|
||||
using var loggerFactory = LoggerFactory.Create(builder =>
|
||||
{
|
||||
builder.AddConsole();
|
||||
builder.SetMinimumLevel(LogLevel.Trace);
|
||||
});
|
||||
var logger = loggerFactory.CreateLogger("CachingFileDownloader");
|
||||
for (var i = 0; i < 10; ++i)
|
||||
try
|
||||
{
|
||||
var url = new Uri($"https://github.com/tgstation/tgstation-server/releases/download/tgstation-server-v{TestLiveServer.TestUpdateVersion}/ServerUpdatePackage.zip");
|
||||
await using var stream = await CacheFile(logger, url, gitHubToken, !LiveTestUtils.RunningInGitHubActions, cancellationToken);
|
||||
succeeded = true;
|
||||
break;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogWarning(ex, $"TEST: FAILED TO CACHE GITHUB RELEASE.");
|
||||
}
|
||||
|
||||
Assert.IsTrue(succeeded);
|
||||
|
||||
ServiceCollectionExtensions.UseFileDownloader<CachingFileDownloader>();
|
||||
}
|
||||
|
||||
public static void Cleanup()
|
||||
{
|
||||
lock (cachedPaths)
|
||||
{
|
||||
foreach (var pathAndDelete in cachedPaths.Values)
|
||||
if (pathAndDelete.Item2)
|
||||
try
|
||||
{
|
||||
File.Delete(pathAndDelete.Item1);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
cachedPaths.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<MemoryStream> DownloadFile(Uri url, string bearerToken, CancellationToken cancellationToken)
|
||||
{
|
||||
Tuple<string, bool> tuple;
|
||||
lock (cachedPaths)
|
||||
if (!cachedPaths.TryGetValue(url.ToString(), out tuple))
|
||||
{
|
||||
logger.LogInformation("Cache miss: {url}", url);
|
||||
tuple = null;
|
||||
}
|
||||
|
||||
if (tuple == null)
|
||||
return await CacheFile(logger, url, bearerToken, true, cancellationToken);
|
||||
|
||||
logger.LogTrace("Cache hit: {url}", url);
|
||||
var bytes = await new DefaultIOManager().ReadAllBytes(tuple.Item1, cancellationToken);
|
||||
return new MemoryStream(bytes);
|
||||
}
|
||||
|
||||
static async Task<MemoryStream> CacheFile(ILogger logger, Uri url, string bearerToken, bool temporal, CancellationToken cancellationToken)
|
||||
{
|
||||
var downloader = new FileDownloader(
|
||||
new HttpClientFactory(
|
||||
new AssemblyInformationProvider().ProductInfoHeaderValue),
|
||||
new Logger<FileDownloader>(
|
||||
LiveTestUtils.CreateLoggerFactoryForLogger(
|
||||
logger,
|
||||
out _)));
|
||||
var download = await downloader.DownloadFile(url, bearerToken, cancellationToken);
|
||||
try
|
||||
{
|
||||
var path = Path.GetTempFileName();
|
||||
try
|
||||
{
|
||||
await using var fs = new DefaultIOManager().CreateAsyncSequentialWriteStream(path);
|
||||
await download.CopyToAsync(fs, cancellationToken);
|
||||
|
||||
lock (cachedPaths)
|
||||
cachedPaths.Add(url.ToString(), Tuple.Create(path, temporal));
|
||||
|
||||
logger.LogTrace("Cached to {path}", path);
|
||||
}
|
||||
catch
|
||||
{
|
||||
File.Delete(path);
|
||||
throw;
|
||||
}
|
||||
|
||||
download.Seek(0, SeekOrigin.Begin);
|
||||
return download;
|
||||
}
|
||||
catch
|
||||
{
|
||||
await download.DisposeAsync();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -43,21 +43,6 @@ namespace Tgstation.Server.Tests.Live
|
||||
|
||||
ulong channelIdAllocator;
|
||||
|
||||
public static ILoggerFactory CreateLoggerFactoryForLogger(ILogger logger, out Mock<ILoggerFactory> mockLoggerFactory)
|
||||
{
|
||||
mockLoggerFactory = new Mock<ILoggerFactory>();
|
||||
mockLoggerFactory.Setup(x => x.CreateLogger(It.IsAny<string>())).Returns(() =>
|
||||
{
|
||||
var temp = logger;
|
||||
logger = null;
|
||||
|
||||
Assert.IsNotNull(temp);
|
||||
return temp;
|
||||
})
|
||||
.Verifiable();
|
||||
return mockLoggerFactory.Object;
|
||||
}
|
||||
|
||||
static IAsyncDelayer CreateMockDelayer()
|
||||
{
|
||||
// at time of writing, this is used exclusively for the reconnection interval which works in minutes
|
||||
@@ -79,7 +64,7 @@ namespace Tgstation.Server.Tests.Live
|
||||
ICryptographySuite cryptographySuite,
|
||||
IReadOnlyCollection<ICommand> commands,
|
||||
Random random)
|
||||
: base(jobManager, CreateMockDelayer(), new Logger<DummyChatProvider>(CreateLoggerFactoryForLogger(logger, out var mockLoggerFactory)), chatBot)
|
||||
: base(jobManager, CreateMockDelayer(), new Logger<DummyChatProvider>(LiveTestUtils.CreateLoggerFactoryForLogger(logger, out var mockLoggerFactory)), chatBot)
|
||||
{
|
||||
mockLoggerFactory.VerifyAll();
|
||||
this.cryptographySuite = cryptographySuite ?? throw new ArgumentNullException(nameof(cryptographySuite));
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
using Moq;
|
||||
|
||||
using Octokit;
|
||||
|
||||
using Tgstation.Server.Host.Configuration;
|
||||
using Tgstation.Server.Host.Extensions;
|
||||
using Tgstation.Server.Host.Security;
|
||||
using Tgstation.Server.Host.System;
|
||||
using Tgstation.Server.Host.Utils.GitHub;
|
||||
|
||||
namespace Tgstation.Server.Tests.Live
|
||||
{
|
||||
sealed class DummyGitHubService : IAuthenticatedGitHubService
|
||||
{
|
||||
static Dictionary<Version, Release> releasesDictionary;
|
||||
static PullRequest testPr;
|
||||
|
||||
readonly ICryptographySuite cryptographySuite;
|
||||
readonly ILogger<DummyGitHubService> logger;
|
||||
|
||||
public static async Task InitializeAndInject(CancellationToken cancellationToken)
|
||||
{
|
||||
var mockOptions = new Mock<IOptions<GeneralConfiguration>>();
|
||||
mockOptions.SetupGet(x => x.Value).Returns(new GeneralConfiguration
|
||||
{
|
||||
GitHubAccessToken = Environment.GetEnvironmentVariable("TGS_TEST_GITHUB_TOKEN")
|
||||
});
|
||||
|
||||
var gitHubClientFactory = new GitHubClientFactory(new AssemblyInformationProvider(), Mock.Of<ILogger<GitHubClientFactory>>(), mockOptions.Object);
|
||||
var gitHubClient = gitHubClientFactory.CreateClient();
|
||||
|
||||
Release targetRelease;
|
||||
do
|
||||
{
|
||||
var releases = await gitHubClient
|
||||
.Repository
|
||||
.Release
|
||||
.GetAll("tgstation", "tgstation-server")
|
||||
.WithToken(cancellationToken);
|
||||
|
||||
targetRelease = releases.FirstOrDefault(release => release.TagName == $"{new UpdatesConfiguration().GitTagPrefix}{TestLiveServer.TestUpdateVersion}");
|
||||
}
|
||||
while (targetRelease == null);
|
||||
|
||||
releasesDictionary = new Dictionary<Version, Release>
|
||||
{
|
||||
{ TestLiveServer.TestUpdateVersion, targetRelease }
|
||||
};
|
||||
|
||||
testPr = await gitHubClient
|
||||
.PullRequest
|
||||
.Get("Cyberboss", "common_core", 2)
|
||||
.WithToken(cancellationToken);
|
||||
|
||||
ServiceCollectionExtensions.UseGitHubServiceFactory<DummyGitHubServiceFactory>();
|
||||
}
|
||||
|
||||
public DummyGitHubService(ICryptographySuite cryptographySuite, ILogger<DummyGitHubService> logger)
|
||||
{
|
||||
this.cryptographySuite = cryptographySuite ?? throw new ArgumentNullException(nameof(cryptographySuite));
|
||||
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
logger.LogTrace("Created");
|
||||
}
|
||||
|
||||
public Task CommentOnIssue(string repoOwner, string repoName, string comment, int issueNumber, CancellationToken cancellationToken)
|
||||
{
|
||||
logger.LogTrace("CommentOnIssue");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task<int> CreateDeployment(NewDeployment newDeployment, string repoOwner, string repoName, CancellationToken cancellationToken)
|
||||
{
|
||||
logger.LogTrace("CreateDeployment");
|
||||
return Task.FromResult(new Random().Next()); ;
|
||||
}
|
||||
|
||||
public Task CreateDeploymentStatus(NewDeploymentStatus newDeploymentStatus, string repoOwner, string repoName, int deploymentId, CancellationToken cancellationToken)
|
||||
{
|
||||
logger.LogTrace("CreateDeploymentStatus");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task CreateDeploymentStatus(NewDeploymentStatus newDeploymentStatus, long repoId, int deploymentId, CancellationToken cancellationToken)
|
||||
{
|
||||
logger.LogTrace("CreateDeploymentStatus");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task<string> CreateOAuthAccessToken(OAuthConfiguration oAuthConfiguration, string code, CancellationToken cancellationToken)
|
||||
{
|
||||
logger.LogTrace("CreateOAuthAccessToken");
|
||||
return Task.FromResult(cryptographySuite.GetSecureString());
|
||||
}
|
||||
|
||||
public Task<int> GetCurrentUserId(CancellationToken cancellationToken)
|
||||
{
|
||||
logger.LogTrace("GetCurrentUserId");
|
||||
return Task.FromResult(new Random().Next());
|
||||
}
|
||||
|
||||
public Task<long> GetRepositoryId(string repoOwner, string repoName, CancellationToken cancellationToken)
|
||||
{
|
||||
logger.LogTrace("GetRepositoryId");
|
||||
return Task.FromResult(new Random().NextInt64());
|
||||
}
|
||||
|
||||
public Task<Uri> GetUpdatesRepositoryUrl(CancellationToken cancellationToken)
|
||||
{
|
||||
logger.LogTrace("GetUpdatesRepositoryUrl");
|
||||
return Task.FromResult(new Uri("https://github.com/tgstation/tgstation-server"));
|
||||
}
|
||||
|
||||
public Task<PullRequest> GetPullRequest(string repoOwner, string repoName, int pullRequestNumber, CancellationToken cancellationToken)
|
||||
{
|
||||
logger.LogTrace("GetPullRequest");
|
||||
return Task.FromResult(testPr);
|
||||
}
|
||||
|
||||
public Task<Dictionary<Version, Release>> GetTgsReleases(CancellationToken cancellationToken)
|
||||
{
|
||||
logger.LogTrace("GetTgsReleases");
|
||||
return Task.FromResult(releasesDictionary);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using Tgstation.Server.Host.Security;
|
||||
using Tgstation.Server.Host.Utils.GitHub;
|
||||
|
||||
namespace Tgstation.Server.Tests.Live
|
||||
{
|
||||
sealed class DummyGitHubServiceFactory : IGitHubServiceFactory
|
||||
{
|
||||
readonly ICryptographySuite cryptographySuite;
|
||||
readonly ILogger<DummyGitHubService> logger;
|
||||
|
||||
public DummyGitHubServiceFactory(ICryptographySuite cryptographySuite, ILogger<DummyGitHubService> logger)
|
||||
{
|
||||
this.cryptographySuite = cryptographySuite ?? throw new ArgumentNullException(nameof(cryptographySuite));
|
||||
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
public IGitHubService CreateService() => CreateDummyService();
|
||||
|
||||
public IAuthenticatedGitHubService CreateService(string accessToken)
|
||||
{
|
||||
if (accessToken == null)
|
||||
throw new ArgumentNullException(nameof(accessToken));
|
||||
|
||||
return CreateDummyService();
|
||||
}
|
||||
|
||||
DummyGitHubService CreateDummyService() => new DummyGitHubService(cryptographySuite, logger);
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,6 @@ using Tgstation.Server.Api.Models;
|
||||
using Tgstation.Server.Api.Models.Request;
|
||||
using Tgstation.Server.Client;
|
||||
using Tgstation.Server.Client.Components;
|
||||
using Tgstation.Server.Common;
|
||||
using Tgstation.Server.Host.Components.Byond;
|
||||
using Tgstation.Server.Host.Configuration;
|
||||
using Tgstation.Server.Host.IO;
|
||||
@@ -27,13 +26,15 @@ namespace Tgstation.Server.Tests.Live.Instance
|
||||
public static readonly Version TestVersion = new(514, 1588);
|
||||
|
||||
readonly IByondClient byondClient;
|
||||
readonly IFileDownloader fileDownloader;
|
||||
|
||||
readonly Api.Models.Instance metadata;
|
||||
|
||||
public ByondTest(IByondClient byondClient, IJobsClient jobsClient, Api.Models.Instance metadata)
|
||||
public ByondTest(IByondClient byondClient, IJobsClient jobsClient, IFileDownloader fileDownloader, Api.Models.Instance metadata)
|
||||
: base(jobsClient)
|
||||
{
|
||||
this.byondClient = byondClient ?? throw new ArgumentNullException(nameof(byondClient));
|
||||
this.fileDownloader = fileDownloader ?? throw new ArgumentNullException(nameof(fileDownloader));
|
||||
this.metadata = metadata ?? throw new ArgumentNullException(nameof(metadata));
|
||||
}
|
||||
|
||||
@@ -159,9 +160,6 @@ namespace Tgstation.Server.Tests.Live.Instance
|
||||
generalConfigOptionsMock.SetupGet(x => x.Value).Returns(new GeneralConfiguration());
|
||||
|
||||
var assemblyInformationProvider = new AssemblyInformationProvider();
|
||||
var fileDownloader = new FileDownloader(
|
||||
new HttpClientFactory(assemblyInformationProvider.ProductInfoHeaderValue),
|
||||
Mock.Of<ILogger<FileDownloader>>());
|
||||
|
||||
IByondInstaller byondInstaller = new PlatformIdentifier().IsWindows
|
||||
? new WindowsByondInstaller(
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Threading.Tasks;
|
||||
using Tgstation.Server.Client;
|
||||
using Tgstation.Server.Client.Components;
|
||||
using Tgstation.Server.Host.Components;
|
||||
using Tgstation.Server.Host.IO;
|
||||
|
||||
namespace Tgstation.Server.Tests.Live.Instance
|
||||
{
|
||||
@@ -12,20 +13,22 @@ namespace Tgstation.Server.Tests.Live.Instance
|
||||
{
|
||||
readonly IInstanceClient instanceClient;
|
||||
readonly IInstanceManagerClient instanceManagerClient;
|
||||
readonly IFileDownloader fileDownloader;
|
||||
readonly InstanceManager instanceManager;
|
||||
readonly ushort serverPort;
|
||||
|
||||
public InstanceTest(IInstanceClient instanceClient, IInstanceManagerClient instanceManagerClient, InstanceManager instanceManager, ushort serverPort)
|
||||
public InstanceTest(IInstanceClient instanceClient, IInstanceManagerClient instanceManagerClient, IFileDownloader fileDownloader, InstanceManager instanceManager, ushort serverPort)
|
||||
{
|
||||
this.instanceClient = instanceClient ?? throw new ArgumentNullException(nameof(instanceClient));
|
||||
this.instanceManagerClient = instanceManagerClient ?? throw new ArgumentNullException(nameof(instanceManagerClient));
|
||||
this.fileDownloader = fileDownloader ?? throw new ArgumentNullException(nameof(fileDownloader));
|
||||
this.instanceManager = instanceManager ?? throw new ArgumentNullException(nameof(instanceManager));
|
||||
this.serverPort = serverPort;
|
||||
}
|
||||
|
||||
public async Task RunTests(CancellationToken cancellationToken, bool highPrioDD, bool lowPrioDeployment)
|
||||
{
|
||||
var byondTest = new ByondTest(instanceClient.Byond, instanceClient.Jobs, instanceClient.Metadata);
|
||||
var byondTest = new ByondTest(instanceClient.Byond, instanceClient.Jobs, fileDownloader, instanceClient.Metadata);
|
||||
var chatTest = new ChatTest(instanceClient.ChatBots, instanceManagerClient, instanceClient.Jobs, instanceClient.Metadata);
|
||||
var configTest = new ConfigurationTest(instanceClient.Configuration, instanceClient.Metadata);
|
||||
var repoTest = new RepositoryTest(instanceClient.Repository, instanceClient.Jobs);
|
||||
@@ -34,7 +37,7 @@ namespace Tgstation.Server.Tests.Live.Instance
|
||||
var byondTask = byondTest.Run(cancellationToken, out var firstInstall);
|
||||
var chatTask = chatTest.RunPreWatchdog(cancellationToken);
|
||||
|
||||
var repoLongJob = repoTest.RunLongClone(cancellationToken);
|
||||
var repoLongJob = await repoTest.RunLongClone(cancellationToken);
|
||||
|
||||
await dmTest.RunPreRepoClone(cancellationToken);
|
||||
await repoTest.AbortLongCloneAndCloneSomethingQuick(repoLongJob, cancellationToken);
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace Tgstation.Server.Tests.Live.Instance
|
||||
this.repositoryClient = repositoryClient ?? throw new ArgumentNullException(nameof(repositoryClient));
|
||||
}
|
||||
|
||||
public async Task<JobResponse> RunLongClone(CancellationToken cancellationToken)
|
||||
public async Task<Task<JobResponse>> RunLongClone(CancellationToken cancellationToken)
|
||||
{
|
||||
var workingBranch = "master";
|
||||
|
||||
@@ -35,24 +35,40 @@ namespace Tgstation.Server.Tests.Live.Instance
|
||||
};
|
||||
|
||||
var clone = await repositoryClient.Clone(cloneRequest, cancellationToken);
|
||||
await ApiAssert.ThrowsException<ConflictException>(() => repositoryClient.Read(cancellationToken), ErrorCode.RepoCloning);
|
||||
Assert.IsNotNull(clone);
|
||||
Assert.AreEqual(cloneRequest.Origin, clone.Origin);
|
||||
Assert.AreEqual(workingBranch, clone.Reference);
|
||||
Assert.IsNull(clone.RevisionInformation);
|
||||
Assert.IsNotNull(clone.ActiveJob);
|
||||
|
||||
// throwing this small jobs consistency test in here
|
||||
await Task.Delay(TimeSpan.FromSeconds(15), cancellationToken);
|
||||
var activeJobs = await JobsClient.ListActive(null, cancellationToken);
|
||||
var allJobs = await JobsClient.List(null, cancellationToken);
|
||||
return Rest();
|
||||
|
||||
Assert.IsTrue(activeJobs.Any(x => x.Id == clone.ActiveJob.Id));
|
||||
Assert.IsTrue(allJobs.Any(x => x.Id == clone.ActiveJob.Id));
|
||||
Assert.IsTrue(activeJobs.First(x => x.Id == clone.ActiveJob.Id).Progress.HasValue);
|
||||
Assert.IsTrue(allJobs.First(x => x.Id == clone.ActiveJob.Id).Progress.HasValue);
|
||||
async Task<JobResponse> Rest()
|
||||
{
|
||||
await ApiAssert.ThrowsException<ConflictException>(() => repositoryClient.Read(cancellationToken), ErrorCode.RepoCloning);
|
||||
Assert.IsNotNull(clone);
|
||||
Assert.AreEqual(cloneRequest.Origin, clone.Origin);
|
||||
Assert.AreEqual(workingBranch, clone.Reference);
|
||||
Assert.IsNull(clone.RevisionInformation);
|
||||
Assert.IsNotNull(clone.ActiveJob);
|
||||
|
||||
return clone.ActiveJob;
|
||||
// throwing this small jobs consistency test in here
|
||||
await Task.Delay(TimeSpan.FromSeconds(15), cancellationToken);
|
||||
var activeJobs = await JobsClient.ListActive(null, cancellationToken);
|
||||
var allJobs = await JobsClient.List(null, cancellationToken);
|
||||
|
||||
Assert.IsTrue(activeJobs.Any(x => x.Id == clone.ActiveJob.Id));
|
||||
Assert.IsTrue(allJobs.Any(x => x.Id == clone.ActiveJob.Id));
|
||||
|
||||
var targetActiveJob = activeJobs.First(x => x.Id == clone.ActiveJob.Id);
|
||||
|
||||
if (!targetActiveJob.Progress.HasValue)
|
||||
{
|
||||
// give it 15 more seconds
|
||||
targetActiveJob = await WaitForJobProgress(targetActiveJob, 15, cancellationToken);
|
||||
allJobs = await JobsClient.List(null, cancellationToken);
|
||||
}
|
||||
|
||||
Assert.IsTrue(targetActiveJob.Progress.HasValue);
|
||||
Assert.IsTrue(allJobs.First(x => x.Id == clone.ActiveJob.Id).Progress.HasValue);
|
||||
|
||||
return clone.ActiveJob;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task AbortLongCloneAndCloneSomethingQuick(Task<JobResponse> longCloneJob, CancellationToken cancellationToken)
|
||||
@@ -70,7 +86,7 @@ namespace Tgstation.Server.Tests.Live.Instance
|
||||
Assert.IsNotNull(secondRead);
|
||||
Assert.IsNull(secondRead.ActiveJob);
|
||||
|
||||
const string Origin = "https://github.com/tgstation/common_core";
|
||||
const string Origin = "https://github.com/Cyberboss/common_core";
|
||||
var cloneRequest = new RepositoryCreateRequest
|
||||
{
|
||||
Origin = new Uri(Origin),
|
||||
@@ -108,7 +124,20 @@ namespace Tgstation.Server.Tests.Live.Instance
|
||||
// Back
|
||||
updated = await Checkout(new RepositoryUpdateRequest { Reference = "master" }, false, true, cancellationToken);
|
||||
|
||||
var prNumber = 37;
|
||||
// enable the good shit if possible
|
||||
if (LiveTestUtils.RunningInGitHubActions
|
||||
|| String.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("TGS_TEST_GITHUB_TOKEN"))
|
||||
|| Environment.MachineName.Equals("CYBERSTATIONXVI", StringComparison.OrdinalIgnoreCase))
|
||||
await repositoryClient.Update(new RepositoryUpdateRequest
|
||||
{
|
||||
CreateGitHubDeployments = true,
|
||||
PostTestMergeComment = true,
|
||||
PushTestMergeCommits = true,
|
||||
AccessUser = "Cyberboss",
|
||||
AccessToken = Environment.GetEnvironmentVariable("TGS_TEST_GITHUB_TOKEN"),
|
||||
}, cancellationToken);
|
||||
|
||||
var prNumber = 2;
|
||||
await TestMergeTests(updated, prNumber, cancellationToken);
|
||||
}
|
||||
|
||||
@@ -164,7 +193,7 @@ namespace Tgstation.Server.Tests.Live.Instance
|
||||
Assert.IsNotNull(withMerge.RevisionInformation.PrimaryTestMerge.TitleAtMerge);
|
||||
Assert.IsNotNull(withMerge.RevisionInformation.PrimaryTestMerge.BodyAtMerge);
|
||||
if (withMerge.RevisionInformation.PrimaryTestMerge.Url != "GITHUB API ERROR: RATE LIMITED")
|
||||
Assert.AreEqual($"https://github.com/tgstation/common_core/pull/{prNumber}", withMerge.RevisionInformation.PrimaryTestMerge.Url);
|
||||
Assert.AreEqual($"https://github.com/Cyberboss/common_core/pull/{prNumber}", withMerge.RevisionInformation.PrimaryTestMerge.Url);
|
||||
Assert.AreEqual(orignCommit, withMerge.RevisionInformation.OriginCommitSha);
|
||||
Assert.AreNotEqual(orignCommit, withMerge.RevisionInformation.CommitSha);
|
||||
|
||||
|
||||
@@ -570,7 +570,7 @@ namespace Tgstation.Server.Tests.Live.Instance
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
var currentSize = baseSize + (int)Math.Pow(2, nextPow);
|
||||
var topicRequestResult = await TopicClient.SendTopic(
|
||||
var topicRequestResult = await TopicClientNoLogger.SendTopic(
|
||||
IPAddress.Loopback,
|
||||
$"tgs_integration_test_tactics4={TopicClient.SanitizeString(currentSize.ToString())}",
|
||||
TestLiveServer.DDPort,
|
||||
@@ -923,7 +923,7 @@ namespace Tgstation.Server.Tests.Live.Instance
|
||||
ReceiveTimeout = TimeSpan.FromSeconds(30),
|
||||
ConnectTimeout = TimeSpan.FromSeconds(30),
|
||||
DisconnectTimeout = TimeSpan.FromSeconds(30)
|
||||
}, new Logger<TopicClient>(DummyChatProvider.CreateLoggerFactoryForLogger(loggerFactory.CreateLogger("WatchdogTest.TopicClient"), out var mockLoggerFactory)));
|
||||
}, new Logger<TopicClient>(LiveTestUtils.CreateLoggerFactoryForLogger(loggerFactory.CreateLogger("WatchdogTest.TopicClient"), out var mockLoggerFactory)));
|
||||
|
||||
public static readonly TopicClient TopicClientNoLogger = new(new SocketParameters
|
||||
{
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
using Moq;
|
||||
|
||||
namespace Tgstation.Server.Tests.Live
|
||||
{
|
||||
static class LiveTestUtils
|
||||
{
|
||||
public static bool RunningInGitHubActions { get; } = !String.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("GITHUB_RUN_ID"));
|
||||
|
||||
public static ILoggerFactory CreateLoggerFactoryForLogger(ILogger logger, out Mock<ILoggerFactory> mockLoggerFactory)
|
||||
{
|
||||
mockLoggerFactory = new Mock<ILoggerFactory>();
|
||||
mockLoggerFactory.Setup(x => x.CreateLogger(It.IsAny<string>())).Returns(() =>
|
||||
{
|
||||
var temp = logger;
|
||||
logger = null;
|
||||
|
||||
Assert.IsNotNull(temp);
|
||||
return temp;
|
||||
})
|
||||
.Verifiable();
|
||||
return mockLoggerFactory.Object;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -49,7 +49,7 @@ namespace Tgstation.Server.Tests.Live
|
||||
SerilogContextHelper.AddSwarmNodeIdentifierToTemplate();
|
||||
}
|
||||
|
||||
public LiveTestingServer(SwarmConfiguration swarmConfiguration, bool enableOAuth, ushort port = 5010, bool dumpOnMissingUpdate = true)
|
||||
public LiveTestingServer(SwarmConfiguration swarmConfiguration, bool enableOAuth, ushort port = 5010)
|
||||
{
|
||||
Directory = Environment.GetEnvironmentVariable("TGS_TEST_TEMP_DIRECTORY");
|
||||
if (string.IsNullOrWhiteSpace(Directory))
|
||||
@@ -89,9 +89,8 @@ namespace Tgstation.Server.Tests.Live
|
||||
// neither of these should really matter but it's better that we test them
|
||||
// high prio DD might help with some topic flakiness actually
|
||||
// github doesn't allow nicing on linux though
|
||||
var runningInGitHubActions = !String.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("GITHUB_RUN_ID"));
|
||||
var windows = new Host.System.PlatformIdentifier().IsWindows;
|
||||
var nicingAllowed = windows || !runningInGitHubActions;
|
||||
var nicingAllowed = windows || !LiveTestUtils.RunningInGitHubActions;
|
||||
HighPriorityDreamDaemon = nicingAllowed;
|
||||
LowPriorityDeployments = nicingAllowed;
|
||||
|
||||
@@ -116,9 +115,6 @@ namespace Tgstation.Server.Tests.Live
|
||||
$"Session:LowPriorityDeploymentProcesses={LowPriorityDeployments}",
|
||||
};
|
||||
|
||||
if (dumpOnMissingUpdate)
|
||||
args.Add("Updates:DumpReleasesOnNotFound=true");
|
||||
|
||||
swarmArgs = new List<string>();
|
||||
if (swarmConfiguration != null)
|
||||
{
|
||||
|
||||
@@ -3,6 +3,8 @@ using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
using Tgstation.Server.Api;
|
||||
using Tgstation.Server.Client;
|
||||
using Tgstation.Server.Common;
|
||||
@@ -29,6 +31,8 @@ namespace Tgstation.Server.Tests.Live
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
|
||||
Console.WriteLine($"TEST ERROR RATE LIMITED: {ex}");
|
||||
if (!LiveTestUtils.RunningInGitHubActions)
|
||||
Assert.Inconclusive("Rate limited by GitHub!");
|
||||
|
||||
var sleepTime = ex.RetryAfter.Value - now;
|
||||
Console.WriteLine($"Sleeping for {sleepTime.TotalMinutes} minutes and retrying...");
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace Tgstation.Server.Tests.Live
|
||||
public static ushort DDPort { get; } = FreeTcpPort();
|
||||
public static ushort DMPort { get; } = GetDMPort();
|
||||
|
||||
readonly Version TestUpdateVersion = new(5, 11, 0);
|
||||
public static readonly Version TestUpdateVersion = new(5, 11, 0);
|
||||
|
||||
readonly IServerClientFactory clientFactory = new ServerClientFactory(new ProductHeaderValue(Assembly.GetExecutingAssembly().GetName().Name, Assembly.GetExecutingAssembly().GetName().Version.ToString()));
|
||||
|
||||
@@ -79,9 +79,14 @@ namespace Tgstation.Server.Tests.Live
|
||||
}
|
||||
}
|
||||
|
||||
[TestInitialize]
|
||||
public async Task Initialize()
|
||||
[ClassInitialize]
|
||||
public static async Task Initialize(TestContext _)
|
||||
{
|
||||
if (LiveTestUtils.RunningInGitHubActions || String.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("TGS_TEST_GITHUB_TOKEN")))
|
||||
await DummyGitHubService.InitializeAndInject(default);
|
||||
|
||||
await CachingFileDownloader.InitializeAndInject(default);
|
||||
|
||||
await DummyChatProvider.RandomDisconnections(true, default);
|
||||
ServerClientFactory.ApiClientFactory = new RateLimitRetryingApiClientFactory();
|
||||
|
||||
@@ -146,6 +151,12 @@ namespace Tgstation.Server.Tests.Live
|
||||
}
|
||||
}
|
||||
|
||||
[ClassCleanup]
|
||||
public static void Cleanup()
|
||||
{
|
||||
CachingFileDownloader.Cleanup();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task TestUpdateProtocolAndDisabledOAuth()
|
||||
{
|
||||
@@ -155,7 +166,6 @@ namespace Tgstation.Server.Tests.Live
|
||||
var serverTask = server.Run(cancellationToken);
|
||||
try
|
||||
{
|
||||
var testUpdateVersion = new Version(4, 3, 0);
|
||||
using (var adminClient = await CreateAdminClient(server.Url, cancellationToken))
|
||||
{
|
||||
// Disabled OAuth test
|
||||
@@ -178,10 +188,21 @@ namespace Tgstation.Server.Tests.Live
|
||||
//attempt to update to stable
|
||||
await adminClient.Administration.Update(new ServerUpdateRequest
|
||||
{
|
||||
NewVersion = testUpdateVersion
|
||||
NewVersion = TestUpdateVersion
|
||||
}, cancellationToken);
|
||||
var serverInfo = await adminClient.ServerInformation(cancellationToken);
|
||||
Assert.IsTrue(serverInfo.UpdateInProgress);
|
||||
|
||||
try
|
||||
{
|
||||
var serverInfoTask = adminClient.ServerInformation(cancellationToken);
|
||||
var completedTask = await Task.WhenAny(serverTask, serverInfoTask);
|
||||
if (completedTask == serverInfoTask)
|
||||
{
|
||||
var serverInfo = await serverInfoTask;
|
||||
Assert.IsTrue(serverInfo.UpdateInProgress);
|
||||
}
|
||||
}
|
||||
catch (ServiceUnavailableException) { }
|
||||
catch (HttpRequestException) { }
|
||||
}
|
||||
|
||||
//wait up to 3 minutes for the dl and install
|
||||
@@ -195,7 +216,7 @@ namespace Tgstation.Server.Tests.Live
|
||||
Assert.IsTrue(File.Exists(updatedAssemblyPath), "Updated assembly missing!");
|
||||
|
||||
var updatedAssemblyVersion = FileVersionInfo.GetVersionInfo(updatedAssemblyPath);
|
||||
Assert.AreEqual(testUpdateVersion, Version.Parse(updatedAssemblyVersion.FileVersion).Semver());
|
||||
Assert.AreEqual(TestUpdateVersion, Version.Parse(updatedAssemblyVersion.FileVersion).Semver());
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -218,7 +239,7 @@ namespace Tgstation.Server.Tests.Live
|
||||
[TestMethod]
|
||||
public async Task TestUpdateBadVersion()
|
||||
{
|
||||
using var server = new LiveTestingServer(null, false, dumpOnMissingUpdate: false);
|
||||
using var server = new LiveTestingServer(null, false);
|
||||
using var serverCts = new CancellationTokenSource();
|
||||
var cancellationToken = serverCts.Token;
|
||||
var serverTask = server.Run(cancellationToken);
|
||||
@@ -746,8 +767,8 @@ namespace Tgstation.Server.Tests.Live
|
||||
Assert.AreEqual(ProcessPriorityClass.Normal, currentProcess.PriorityClass);
|
||||
}
|
||||
|
||||
const int MaximumTestMinutes = 30;
|
||||
using var hardCancellationTokenSource = new CancellationTokenSource(TimeSpan.FromMinutes(MaximumTestMinutes));
|
||||
var maximumTestMinutes = LiveTestUtils.RunningInGitHubActions ? 90 : 20;
|
||||
using var hardCancellationTokenSource = new CancellationTokenSource(TimeSpan.FromMinutes(maximumTestMinutes));
|
||||
var hardCancellationToken = hardCancellationTokenSource.Token;
|
||||
|
||||
ServiceCollectionExtensions.UseAdditionalLoggerProvider<HardFailLoggerProvider>();
|
||||
@@ -880,7 +901,17 @@ namespace Tgstation.Server.Tests.Live
|
||||
|
||||
Assert.IsTrue(Directory.Exists(instanceClient.Metadata.Path));
|
||||
|
||||
var instanceTests = FailFast(new InstanceTest(instanceClient, adminClient.Instances, GetInstanceManager(), (ushort)server.Url.Port).RunTests(cancellationToken, server.HighPriorityDreamDaemon, server.LowPriorityDeployments));
|
||||
var instanceTests = FailFast(
|
||||
new InstanceTest(
|
||||
instanceClient,
|
||||
adminClient.Instances,
|
||||
((Host.Server)server.RealServer).Host.Services.GetRequiredService<Host.IO.IFileDownloader>(),
|
||||
GetInstanceManager(),
|
||||
(ushort)server.Url.Port)
|
||||
.RunTests(
|
||||
cancellationToken,
|
||||
server.HighPriorityDreamDaemon,
|
||||
server.LowPriorityDeployments));
|
||||
|
||||
await Task.WhenAll(rootTest, adminTest, instancesTest, instanceTests, usersTest);
|
||||
|
||||
@@ -1133,9 +1164,7 @@ namespace Tgstation.Server.Tests.Live
|
||||
url,
|
||||
DefaultCredentials.AdminUserName,
|
||||
DefaultCredentials.DefaultAdminUserPassword,
|
||||
attemptLoginRefresh: false,
|
||||
cancellationToken: cancellationToken)
|
||||
;
|
||||
cancellationToken: cancellationToken);
|
||||
}
|
||||
catch (HttpRequestException)
|
||||
{
|
||||
|
||||
@@ -12,10 +12,10 @@ namespace Tgstation.Server.Tests
|
||||
[TestClass]
|
||||
public sealed class TestDMApiConstants
|
||||
{
|
||||
string[] definesFileLines;
|
||||
static string[] definesFileLines;
|
||||
|
||||
[TestInitialize]
|
||||
public async Task Initialize()
|
||||
[ClassInitialize]
|
||||
public static async Task Initialize(TestContext _)
|
||||
{
|
||||
definesFileLines = await File.ReadAllLinesAsync("../../../../../src/DMAPI/tgs/v5/_defines.dm");
|
||||
}
|
||||
|
||||
@@ -20,12 +20,12 @@ namespace Tgstation.Server.Tests
|
||||
[TestCategory("SkipWhenLiveUnitTesting")]
|
||||
public sealed class TestVersions
|
||||
{
|
||||
XNamespace xmlNamespace;
|
||||
static XNamespace xmlNamespace;
|
||||
|
||||
XElement versionsPropertyGroup;
|
||||
static XElement versionsPropertyGroup;
|
||||
|
||||
[TestInitialize]
|
||||
public void Init()
|
||||
[ClassInitialize]
|
||||
public static void Init(TestContext _)
|
||||
{
|
||||
var doc = XDocument.Load("../../../../../build/Version.props");
|
||||
var project = doc.Root;
|
||||
|
||||
Reference in New Issue
Block a user