mirror of
https://github.com/tgstation/tgstation-server.git
synced 2026-08-21 12:06:59 +01:00
Gitlab graphql transition
Gitlab comment consolidation
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"projects": [
|
||||
{
|
||||
"schema": [
|
||||
"src/Tgstation.Server.Client.GraphQL/schema.graphql"
|
||||
],
|
||||
"documents": [
|
||||
"src/Tgstation.Server.Client.GraphQL/**/*.graphql"
|
||||
]
|
||||
},
|
||||
{
|
||||
"schema": [
|
||||
"src/Tgstation.Server.Host.Utils.GitLab.GraphQL/schema.graphql"
|
||||
],
|
||||
"documents": [
|
||||
"src/Tgstation.Server.Host.Utils.GitLab.GraphQL/**/*.graphql"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
mutation CreateNote($id: NoteableID!, $body: String!) {
|
||||
createNote(input: { noteableId: $id, body: $body }) {
|
||||
note {
|
||||
id
|
||||
body
|
||||
discussion {
|
||||
id
|
||||
}
|
||||
}
|
||||
errors
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
mutation ModifyNote($id: NoteID!, $body: String!) {
|
||||
updateNote(input: { id: $id, body: $body }) {
|
||||
note {
|
||||
id
|
||||
body
|
||||
}
|
||||
errors
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
query GetCurrentUser {
|
||||
currentUser
|
||||
{
|
||||
username
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
query GetMergeRequest($project: ID!, $number: String!) {
|
||||
project(fullPath: $project) {
|
||||
mergeRequest(iid: $number) {
|
||||
author { username }
|
||||
description
|
||||
title
|
||||
diffHeadSha
|
||||
webUrl
|
||||
iid
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
query GetMergeRequestNotes($project: ID!, $number: String!) {
|
||||
project(fullPath: $project) {
|
||||
mergeRequest(iid: $number) {
|
||||
iid
|
||||
id
|
||||
notes {
|
||||
nodes {
|
||||
author { username }
|
||||
body
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,10 @@ query GetMergeRequests($project: ID!, $numbers: [String!]!) {
|
||||
mergeRequests(iids: $numbers) {
|
||||
nodes {
|
||||
state
|
||||
mergeCommitSha
|
||||
diffHeadSha
|
||||
closedAt
|
||||
iid
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+8
-2
@@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="../../build/SrcCommon.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
@@ -33,7 +33,12 @@
|
||||
</Target>
|
||||
|
||||
<ItemGroup>
|
||||
<GraphQL Remove="gql\queries\GetMergeRequests.graphql" />
|
||||
<GraphQL Remove="GQL\Mutations\CreateNote.graphql" />
|
||||
<GraphQL Remove="GQL\Mutations\ModifyNote.graphql" />
|
||||
<GraphQL Remove="GQL\Queries\GetCurrentUser.graphql" />
|
||||
<GraphQL Remove="GQL\Queries\GetMergeRequest.graphql" />
|
||||
<GraphQL Remove="GQL\Queries\GetMergeRequestNotes.graphql" />
|
||||
<GraphQL Remove="GQL\Queries\GetMergeRequests.graphql" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -42,6 +47,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="GQL\Queries\" />
|
||||
<Folder Include="GQL\Mutations\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
+61
-18
@@ -6,9 +6,6 @@ using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using GitLabApiClient;
|
||||
using GitLabApiClient.Models.Notes.Requests;
|
||||
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using StrawberryShake;
|
||||
@@ -92,10 +89,10 @@ namespace Tgstation.Server.Host.Components.Deployment.Remote
|
||||
if (mergeRequest.State != MergeRequestState.Merged)
|
||||
return;
|
||||
|
||||
var mergeCommitSha = mergeRequest.MergeCommitSha;
|
||||
if (mergeCommitSha == null)
|
||||
var diffHeadSha = mergeRequest.DiffHeadSha;
|
||||
if (diffHeadSha == null)
|
||||
{
|
||||
Logger.LogWarning("MergeRequest #{id} had no MergeCommitSha!", mergeRequest.Iid);
|
||||
Logger.LogWarning("MergeRequest #{id} had no DiffHeadSha!", mergeRequest.Iid);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -119,7 +116,7 @@ namespace Tgstation.Server.Host.Components.Deployment.Remote
|
||||
}
|
||||
|
||||
// We don't just assume, actually check the repo contains the merge commit.
|
||||
if (await repository.CommittishIsParent(mergeCommitSha, cancellationToken))
|
||||
if (await repository.CommittishIsParent(diffHeadSha, cancellationToken))
|
||||
newList.Remove(
|
||||
newList.First(
|
||||
potential => potential.Number == number));
|
||||
@@ -163,19 +160,65 @@ namespace Tgstation.Server.Host.Components.Deployment.Remote
|
||||
int testMergeNumber,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var client = repositorySettings.AccessToken != null
|
||||
? new GitLabClient(GitLabRemoteFeatures.GitLabUrl, repositorySettings.AccessToken)
|
||||
: new GitLabClient(GitLabRemoteFeatures.GitLabUrl);
|
||||
|
||||
await using var client = await GraphQLGitLabClientFactory.CreateClient(repositorySettings.AccessToken);
|
||||
try
|
||||
{
|
||||
await client
|
||||
.MergeRequests
|
||||
.CreateNoteAsync(
|
||||
$"{remoteRepositoryOwner}/{remoteRepositoryName}",
|
||||
testMergeNumber,
|
||||
new CreateMergeRequestNoteRequest(comment))
|
||||
.WaitAsync(cancellationToken);
|
||||
string header = String.Format(CultureInfo.InvariantCulture, "<!-- test_merge_tgs_bot -->{0}## Test merge deployment history:{0}{0}", Environment.NewLine);
|
||||
|
||||
// Try to find an existing note
|
||||
var notesQueryResult = await client.GraphQL.GetMergeRequestNotes.ExecuteAsync(
|
||||
$"{remoteRepositoryOwner}/{remoteRepositoryName}",
|
||||
testMergeNumber.ToString(CultureInfo.InvariantCulture),
|
||||
cancellationToken);
|
||||
|
||||
notesQueryResult.EnsureNoErrors();
|
||||
|
||||
var mergeRequest = notesQueryResult.Data?.Project?.MergeRequest;
|
||||
if (mergeRequest == null)
|
||||
{
|
||||
Logger.LogWarning("GitLab GetMergeRequestNotes mergeRequest returned null!");
|
||||
return;
|
||||
}
|
||||
|
||||
var comments = mergeRequest.Notes?.Nodes;
|
||||
IGetMergeRequestNotes_Project_MergeRequest_Notes_Nodes? existingComment = null;
|
||||
if (comments != null)
|
||||
{
|
||||
for (int i = comments.Count - 1; i > -1; i--)
|
||||
{
|
||||
var currentComment = comments[i];
|
||||
if (currentComment?.Author?.Username == repositorySettings.AccessUser && (currentComment?.Body?.StartsWith(header) ?? false))
|
||||
{
|
||||
if (currentComment.Body.Length > 987856)
|
||||
{ // Limit should be 999,999 so we'll leave a 12,143 buffer
|
||||
break;
|
||||
}
|
||||
|
||||
existingComment = currentComment;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Either amend or create the note
|
||||
if (existingComment != null)
|
||||
{
|
||||
var noteModificationResult = await client.GraphQL.ModifyNote.ExecuteAsync(
|
||||
existingComment.Id,
|
||||
existingComment.Body + comment,
|
||||
cancellationToken);
|
||||
|
||||
notesQueryResult.EnsureNoErrors();
|
||||
}
|
||||
else
|
||||
{
|
||||
var noteCreationResult = await client.GraphQL.CreateNote.ExecuteAsync(
|
||||
mergeRequest.Id,
|
||||
header + comment,
|
||||
cancellationToken);
|
||||
|
||||
noteCreationResult.EnsureNoErrors();
|
||||
}
|
||||
}
|
||||
catch (Exception ex) when (ex is not OperationCanceledException)
|
||||
{
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using GitLabApiClient;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using StrawberryShake;
|
||||
|
||||
using Tgstation.Server.Api.Models;
|
||||
using Tgstation.Server.Host.Utils.GitLab.GraphQL;
|
||||
|
||||
namespace Tgstation.Server.Host.Components.Repository
|
||||
{
|
||||
@@ -45,30 +48,25 @@ namespace Tgstation.Server.Host.Components.Repository
|
||||
RepositorySettings repositorySettings,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var client = repositorySettings.AccessToken != null
|
||||
? new GitLabClient(GitLabUrl, repositorySettings.AccessToken)
|
||||
: new GitLabClient(GitLabUrl);
|
||||
|
||||
await using var client = await GraphQLGitLabClientFactory.CreateClient(repositorySettings.AccessToken);
|
||||
try
|
||||
{
|
||||
var mr = await client
|
||||
.MergeRequests
|
||||
.GetAsync($"{RemoteRepositoryOwner}/{RemoteRepositoryName}", parameters.Number)
|
||||
.WaitAsync(cancellationToken);
|
||||
var operationResult = await client.GraphQL.GetMergeRequest.ExecuteAsync(
|
||||
$"{RemoteRepositoryOwner}/{RemoteRepositoryName}",
|
||||
parameters.Number.ToString(CultureInfo.InvariantCulture),
|
||||
cancellationToken);
|
||||
|
||||
var revisionToUse = parameters.TargetCommitSha == null
|
||||
|| mr.Sha.StartsWith(parameters.TargetCommitSha, StringComparison.OrdinalIgnoreCase)
|
||||
? mr.Sha
|
||||
: parameters.TargetCommitSha;
|
||||
operationResult.EnsureNoErrors();
|
||||
var mr = operationResult.Data?.Project?.MergeRequest ?? throw new InvalidOperationException("GitLab MergeRequest check returned null!");
|
||||
|
||||
return new Models.TestMerge
|
||||
{
|
||||
Author = mr.Author.Username,
|
||||
Author = mr.Author?.Username,
|
||||
BodyAtMerge = mr.Description,
|
||||
TitleAtMerge = mr.Title,
|
||||
Comment = parameters.Comment,
|
||||
Number = parameters.Number,
|
||||
TargetCommitSha = mr.Sha,
|
||||
TargetCommitSha = mr.DiffHeadSha,
|
||||
Url = mr.WebUrl,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -7,12 +7,12 @@ using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using GitLabApiClient;
|
||||
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using StrawberryShake;
|
||||
|
||||
using Tgstation.Server.Api;
|
||||
using Tgstation.Server.Api.Models;
|
||||
using Tgstation.Server.Api.Models.Request;
|
||||
@@ -27,6 +27,7 @@ using Tgstation.Server.Host.Models;
|
||||
using Tgstation.Server.Host.Security;
|
||||
using Tgstation.Server.Host.Utils;
|
||||
using Tgstation.Server.Host.Utils.GitHub;
|
||||
using Tgstation.Server.Host.Utils.GitLab.GraphQL;
|
||||
|
||||
namespace Tgstation.Server.Host.Controllers
|
||||
{
|
||||
@@ -585,70 +586,84 @@ namespace Tgstation.Server.Host.Controllers
|
||||
switch (remoteFeatures.RemoteGitProvider!.Value)
|
||||
{
|
||||
case RemoteGitProvider.GitHub:
|
||||
var gitHubClient = await gitHubClientFactory.CreateClientForRepository(
|
||||
{
|
||||
var gitHubClient = await gitHubClientFactory.CreateClientForRepository(
|
||||
model.AccessToken,
|
||||
new RepositoryIdentifier(
|
||||
remoteFeatures.RemoteRepositoryOwner!,
|
||||
remoteFeatures.RemoteRepositoryName!),
|
||||
cancellationToken);
|
||||
if (gitHubClient == null)
|
||||
{
|
||||
return this.StatusCode(HttpStatusCode.FailedDependency, new ErrorMessageResponse(ErrorCode.RemoteApiError)
|
||||
if (gitHubClient == null)
|
||||
{
|
||||
AdditionalData = "GitHub authentication failed!",
|
||||
});
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
string username;
|
||||
if (!model.AccessToken.StartsWith(Api.Models.RepositorySettings.TgsAppPrivateKeyPrefix))
|
||||
{
|
||||
var user = await gitHubClient.User.Current();
|
||||
username = user.Login;
|
||||
}
|
||||
else
|
||||
{
|
||||
// we literally need to app auth again to get the damn bot username
|
||||
var appClient = gitHubClientFactory.CreateAppClient(model.AccessToken)!;
|
||||
var app = await appClient.GitHubApps.GetCurrent();
|
||||
username = app.Name;
|
||||
return this.StatusCode(HttpStatusCode.FailedDependency, new ErrorMessageResponse(ErrorCode.RemoteApiError)
|
||||
{
|
||||
AdditionalData = "GitHub authentication failed!",
|
||||
});
|
||||
}
|
||||
|
||||
if (username != model.AccessUser)
|
||||
return Conflict(new ErrorMessageResponse(ErrorCode.RepoTokenUsernameMismatch));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return this.StatusCode(HttpStatusCode.FailedDependency, new ErrorMessageResponse(ErrorCode.RemoteApiError)
|
||||
try
|
||||
{
|
||||
AdditionalData = $"GitHub Authentication Failure: {ex.Message}",
|
||||
});
|
||||
string username;
|
||||
if (!model.AccessToken.StartsWith(Api.Models.RepositorySettings.TgsAppPrivateKeyPrefix))
|
||||
{
|
||||
var user = await gitHubClient.User.Current();
|
||||
username = user.Login;
|
||||
}
|
||||
else
|
||||
{
|
||||
// we literally need to app auth again to get the damn bot username
|
||||
var appClient = gitHubClientFactory.CreateAppClient(model.AccessToken)!;
|
||||
var app = await appClient.GitHubApps.GetCurrent();
|
||||
username = app.Name;
|
||||
}
|
||||
|
||||
if (username != model.AccessUser)
|
||||
return Conflict(new ErrorMessageResponse(ErrorCode.RepoTokenUsernameMismatch));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return this.StatusCode(HttpStatusCode.FailedDependency, new ErrorMessageResponse(ErrorCode.RemoteApiError)
|
||||
{
|
||||
AdditionalData = $"GitHub Authentication Failure: {ex.Message}",
|
||||
});
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
case RemoteGitProvider.GitLab:
|
||||
// need to abstract this eventually
|
||||
var gitLabClient = new GitLabClient(GitLabRemoteFeatures.GitLabUrl, model.AccessToken);
|
||||
try
|
||||
{
|
||||
var user = await gitLabClient.Users.GetCurrentSessionAsync();
|
||||
if (user.Username != model.AccessUser)
|
||||
return Conflict(new ErrorMessageResponse(ErrorCode.RepoTokenUsernameMismatch));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return this.StatusCode(HttpStatusCode.FailedDependency, new ErrorMessageResponse(ErrorCode.RemoteApiError)
|
||||
// need to abstract this eventually
|
||||
await using var gitLabClient = await GraphQLGitLabClientFactory.CreateClient(model.AccessToken);
|
||||
try
|
||||
{
|
||||
AdditionalData = $"GitLab Authentication Failure: {ex.Message}",
|
||||
});
|
||||
var operationResult = await gitLabClient.GraphQL.GetCurrentUser.ExecuteAsync(cancellationToken);
|
||||
|
||||
operationResult.EnsureNoErrors();
|
||||
|
||||
var user = operationResult.Data?.CurrentUser;
|
||||
if (user == null || user.Username != model.AccessUser)
|
||||
{
|
||||
return Conflict(new ErrorMessageResponse(ErrorCode.RepoTokenUsernameMismatch));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return this.StatusCode(HttpStatusCode.FailedDependency, new ErrorMessageResponse(ErrorCode.RemoteApiError)
|
||||
{
|
||||
AdditionalData = $"GitLab Authentication Failure: {ex.Message}",
|
||||
});
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
case RemoteGitProvider.Unknown:
|
||||
default:
|
||||
Logger.LogWarning("RemoteGitProvider is {provider}, no auth check implemented!", remoteFeatures.RemoteGitProvider.Value);
|
||||
break;
|
||||
{
|
||||
Logger.LogWarning("RemoteGitProvider is {provider}, no auth check implemented!", remoteFeatures.RemoteGitProvider.Value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
Reference in New Issue
Block a user