2using System.Collections.Generic;
3using System.Collections.ObjectModel;
4using System.Globalization;
7using System.Threading.Tasks;
9using Microsoft.EntityFrameworkCore;
10using Microsoft.Extensions.Logging;
46 ILogger<GitHubRemoteDeploymentManager> logger,
47 Api.Models.Instance metadata)
48 : base(logger, metadata)
56 Api.Models.Internal.IGitRemoteInformation remoteInformation,
58 CancellationToken cancellationToken)
60 ArgumentNullException.ThrowIfNull(remoteInformation);
61 ArgumentNullException.ThrowIfNull(compileJob);
63 Logger.LogTrace(
"Starting deployment...");
67 async databaseContext =>
68 repositorySettings = await databaseContext
71 .Where(x => x.InstanceId ==
Metadata.Id)
72 .FirstAsync(cancellationToken));
74 var instanceAuthenticated = repositorySettings.AccessToken !=
null;
77 if (instanceAuthenticated)
80 gitHubService = authenticatedGitHubService;
84 authenticatedGitHubService =
null;
89 remoteInformation.RemoteRepositoryOwner,
90 remoteInformation.RemoteRepositoryName,
94 Logger.LogTrace(
"Not creating deployment");
95 else if (!instanceAuthenticated)
96 Logger.LogWarning(
"Can't create GitHub deployment as no access token is set for repository!");
99 Logger.LogTrace(
"Creating deployment...");
102 compileJob.GitHubDeploymentId = await authenticatedGitHubService.
CreateDeployment(
106 Description =
"TGS Game Deployment",
107 Environment = $
"TGS: {Metadata.Name}",
108 ProductionEnvironment = true,
109 RequiredContexts = new Collection<string>(),
111 remoteInformation.RemoteRepositoryOwner,
112 remoteInformation.RemoteRepositoryName,
118 new NewDeploymentStatus(DeploymentState.InProgress)
120 Description =
"The project is being deployed",
121 AutoInactive = false,
123 remoteInformation.RemoteRepositoryOwner,
124 remoteInformation.RemoteRepositoryName,
128 Logger.LogTrace(
"In-progress deployment status created");
130 catch (Exception ex) when (ex is not OperationCanceledException)
132 Logger.LogWarning(ex,
"Unable to create GitHub deployment!");
138 compileJob.GitHubRepoId = await repositoryIdTask;
141 catch (Exception ex) when (ex is not OperationCanceledException)
143 Logger.LogWarning(ex,
"Unable to set compile job repository ID!");
150 CancellationToken cancellationToken)
153 "The deployment succeeded and will be applied a the next server reboot.",
154 DeploymentState.Pending,
161 "The deployment is now live on the server.",
162 DeploymentState.Success,
170 DeploymentState.Error,
177 "The deployment has been superceeded.",
178 DeploymentState.Inactive,
186 CancellationToken cancellationToken)
188 ArgumentNullException.ThrowIfNull(repository);
189 ArgumentNullException.ThrowIfNull(repositorySettings);
190 ArgumentNullException.ThrowIfNull(revisionInformation);
194 Logger.LogTrace(
"No test merges to remove.");
198 var gitHubService = repositorySettings.AccessToken !=
null
202 var tasks = revisionInformation
207 await Task.WhenAll(tasks);
209 catch (Exception ex) when (ex is not OperationCanceledException)
211 Logger.LogWarning(ex,
"Pull requests update check failed!");
214 var newList = revisionInformation.
ActiveTestMerges.Select(x => x.TestMerge).ToList();
216 PullRequest lastMerged =
null;
217 async Task CheckRemovePR(Task<PullRequest> task)
224 if (await repository.
ShaIsParent(pr.MergeCommitSha, cancellationToken))
226 if (lastMerged ==
null || lastMerged.MergedAt < pr.MergedAt)
230 potential => potential.Number == pr.Number));
234 foreach (var prTask
in tasks)
235 await CheckRemovePR(prTask);
243 string remoteRepositoryOwner,
244 string remoteRepositoryName,
247 CancellationToken cancellationToken)
253 await gitHubService.CommentOnIssue(remoteRepositoryOwner, remoteRepositoryName, comment, testMergeNumber, cancellationToken);
255 catch (Exception ex) when (ex is not OperationCanceledException)
257 Logger.LogWarning(ex,
"Error posting GitHub comment!");
266 string remoteRepositoryOwner,
267 string remoteRepositoryName,
268 bool updated) => String.Format(
269 CultureInfo.InvariantCulture,
270 "#### Test Merge {4}{0}{0}<details><summary>Details</summary>{0}{0}##### Server Instance{0}{5}{1}{0}{0}##### Revision{0}Origin: {6}{0}Pull Request: {2}{0}Server: {7}{3}{8}{0}</details>",
272 repositorySettings.ShowTestMergeCommitters.Value
274 CultureInfo.InvariantCulture,
275 "{0}{0}##### Merged By{0}{1}",
277 testMerge.MergedBy.Name)
279 testMerge.TargetCommitSha,
280 testMerge.Comment !=
null
282 CultureInfo.InvariantCulture,
283 "{0}{0}##### Comment{0}{1}",
287 updated ?
"Updated" :
"Deployed",
289 compileJob.RevisionInformation.OriginCommitSha,
290 compileJob.RevisionInformation.CommitSha,
291 compileJob.GitHubDeploymentId.HasValue
292 ? $
"{Environment.NewLine}[GitHub Deployments](https://github.com/{remoteRepositoryOwner}/{remoteRepositoryName}/deployments/activity_log?environment=TGS%3A+{Metadata.Name.Replace(" ", "+
", StringComparison.Ordinal)})"
306 DeploymentState deploymentState,
307 CancellationToken cancellationToken)
309 ArgumentNullException.ThrowIfNull(compileJob);
313 Logger.LogTrace(
"Not updating deployment as it is missing a repo ID or deployment ID.");
317 Logger.LogTrace(
"Updating deployment {gitHubDeploymentId} to {deploymentState}...", compileJob.
GitHubDeploymentId.Value, deploymentState);
319 string gitHubAccessToken =
null;
321 async databaseContext =>
322 gitHubAccessToken = await databaseContext
325 .Where(x => x.InstanceId ==
Metadata.Id)
326 .Select(x => x.AccessToken)
327 .FirstAsync(cancellationToken));
329 if (gitHubAccessToken ==
null)
332 "GitHub access token disappeared during deployment, can't update to {deploymentState}!",
341 await gitHubService.CreateDeploymentStatus(
342 new NewDeploymentStatus(deploymentState)
344 Description = description,
350 catch (Exception ex) when (ex is not OperationCanceledException)
352 Logger.LogWarning(ex,
"Error updating GitHub deployment!");
bool? CreateGitHubDeployments
If GitHub deployments should be created. Requires AccessUser, AccessToken, and PushTestMergeCommits t...
string? AccessToken
The token/password to access the git repository with.
Base class for implementing IRemoteDeploymentManagers.
Api.Models.Instance Metadata
The Api.Models.Instance for the BaseRemoteDeploymentManager.
ILogger< BaseRemoteDeploymentManager > Logger
The ILogger for the BaseRemoteDeploymentManager.
IRemoteDeploymentManager for GitHub.com.
readonly IDatabaseContextFactory databaseContextFactory
The IDatabaseContextFactory for the GitHubRemoteDeploymentManager.
readonly IGitHubServiceFactory gitHubServiceFactory
The IGitHubServiceFactory for the GitHubRemoteDeploymentManager.
override async Task StartDeployment(Api.Models.Internal.IGitRemoteInformation remoteInformation, CompileJob compileJob, CancellationToken cancellationToken)
Start a deployment for a given compileJob . A Task representing the running operation.
override Task MarkInactive(CompileJob compileJob, CancellationToken cancellationToken)
Mark the deplotment for a given compileJob as inactive. A Task representing the running operation.
GitHubRemoteDeploymentManager(IDatabaseContextFactory databaseContextFactory, IGitHubServiceFactory gitHubServiceFactory, ILogger< GitHubRemoteDeploymentManager > logger, Api.Models.Instance metadata)
Initializes a new instance of the GitHubRemoteDeploymentManager class.
override Task ApplyDeployment(CompileJob compileJob, CompileJob oldCompileJob, CancellationToken cancellationToken)
Stage a given compileJob 's deployment. A Task representing the running operation.
override string FormatTestMerge(RepositorySettings repositorySettings, CompileJob compileJob, TestMerge testMerge, string remoteRepositoryOwner, string remoteRepositoryName, bool updated)
Formats a comment for a given testMerge . A Task representing the running operation.
override Task FailDeployment(CompileJob compileJob, string errorMessage, CancellationToken cancellationToken)
Fail a deployment for a given compileJob . A Task representing the running operation.
override async Task< IReadOnlyCollection< TestMerge > > RemoveMergedTestMerges(IRepository repository, RepositorySettings repositorySettings, RevisionInformation revisionInformation, CancellationToken cancellationToken)
Get the updated list of TestMerges for an origin merge. A Task<TResult> resulting in the IReadOnlyCol...
override async Task CommentOnTestMergeSource(RepositorySettings repositorySettings, string remoteRepositoryOwner, string remoteRepositoryName, string comment, int testMergeNumber, CancellationToken cancellationToken)
Create a comment of a given testMergeNumber 's source. A Task representing the running operation.
override Task StageDeployment(CompileJob compileJob, CancellationToken cancellationToken)
Stage a given compileJob 's deployment. A Task representing the running operation.
async Task UpdateDeployment(CompileJob compileJob, string description, DeploymentState deploymentState, CancellationToken cancellationToken)
Update the deployment for a given compileJob .
int? GitHubDeploymentId
The GitHub deployment ID associated with the CompileJob if any.
RevisionInformation RevisionInformation
See CompileJobResponse.RevisionInformation.
long? GitHubRepoId
The source GitHub repository the deployment came from if any.
Represents an on-disk git repository.
Task< bool > ShaIsParent(string sha, CancellationToken cancellationToken)
Check if a given sha is a parent of the current Head.
Factory for scoping usage of IDatabaseContexts. Meant for use by Components.
Task UseContext(Func< IDatabaseContext, Task > operation)
Run an operation in the scope of an IDatabaseContext.
IGitHubService that exposes functions that require authentication.
Task< int > CreateDeployment(NewDeployment newDeployment, string repoOwner, string repoName, CancellationToken cancellationToken)
Create a newDeployment on a target repostiory.
Task CreateDeploymentStatus(NewDeploymentStatus newDeploymentStatus, string repoOwner, string repoName, int deploymentId, CancellationToken cancellationToken)
Create a newDeploymentStatus on a target deployment.
Factory for IGitHubServices.
IGitHubService CreateService()
Create a IGitHubService.
Service for interacting with the GitHub API.
Task< long > GetRepositoryId(string repoOwner, string repoName, CancellationToken cancellationToken)
Get a target repostiory's ID.