예제 #1
0
        /// <inheritdoc />
        public async Task CompileProcess(Job job, IDatabaseContext databaseContext, Action <int> progressReporter, CancellationToken cancellationToken)
        {
            //DO NOT FOLLOW THE SUGGESTION FOR A THROW EXPRESSION HERE
            if (job == null)
            {
                throw new ArgumentNullException(nameof(job));
            }
            if (databaseContext == null)
            {
                throw new ArgumentNullException(nameof(databaseContext));
            }
            if (progressReporter == null)
            {
                throw new ArgumentNullException(nameof(progressReporter));
            }

            var ddSettingsTask = databaseContext.DreamDaemonSettings.Where(x => x.InstanceId == metadata.Id).Select(x => new DreamDaemonSettings
            {
                StartupTimeout = x.StartupTimeout,
            }).FirstOrDefaultAsync(cancellationToken);

            var compileJobsTask = databaseContext.CompileJobs
                                  .Where(x => x.Job.Instance.Id == metadata.Id)
                                  .OrderByDescending(x => x.Job.StoppedAt)
                                  //TODO: Replace with this select when the issues linked in https://github.com/tgstation/tgstation-server/issues/737 are fixed
                                  //.Select(x => x.Job.StoppedAt.Value - x.Job.StartedAt.Value)
                                  .Select(x => new Job
            {
                StoppedAt = x.Job.StoppedAt,
                StartedAt = x.Job.StartedAt
            })
                                  .Take(10)
                                  .ToListAsync(cancellationToken);

            var dreamMakerSettings = await databaseContext.DreamMakerSettings.Where(x => x.InstanceId == metadata.Id).FirstAsync(cancellationToken).ConfigureAwait(false);

            if (dreamMakerSettings == default)
            {
                throw new JobException("Missing DreamMakerSettings in DB!");
            }
            var ddSettings = await ddSettingsTask.ConfigureAwait(false);

            if (ddSettings == default)
            {
                throw new JobException("Missing DreamDaemonSettings in DB!");
            }

            CompileJob          compileJob;
            RevisionInformation revInfo;

            using (var repo = await RepositoryManager.LoadRepository(cancellationToken).ConfigureAwait(false))
            {
                if (repo == null)
                {
                    throw new JobException("Missing Repository!");
                }

                var repoSha = repo.Head;
                revInfo = await databaseContext.RevisionInformations.Where(x => x.CommitSha == repoSha && x.Instance.Id == metadata.Id).Include(x => x.ActiveTestMerges).ThenInclude(x => x.TestMerge).FirstOrDefaultAsync().ConfigureAwait(false);

                if (revInfo == default)
                {
                    revInfo = new RevisionInformation
                    {
                        CommitSha       = repoSha,
                        OriginCommitSha = repoSha,
                        Instance        = new Models.Instance
                        {
                            Id = metadata.Id
                        }
                    };
                    logger.LogWarning(Repository.Repository.OriginTrackingErrorTemplate, repoSha);
                    databaseContext.Instances.Attach(revInfo.Instance);
                }

                TimeSpan?averageSpan         = null;
                var      previousCompileJobs = await compileJobsTask.ConfigureAwait(false);

                if (previousCompileJobs.Count != 0)
                {
                    var totalSpan = TimeSpan.Zero;
                    foreach (var I in previousCompileJobs)
                    {
                        totalSpan += I.StoppedAt.Value - I.StartedAt.Value;
                    }
                    averageSpan = totalSpan / previousCompileJobs.Count;
                }

                compileJob = await DreamMaker.Compile(revInfo, dreamMakerSettings, ddSettings.StartupTimeout.Value, repo, progressReporter, averageSpan, cancellationToken).ConfigureAwait(false);
            }

            compileJob.Job = job;

            databaseContext.CompileJobs.Add(compileJob);                //will be saved by job context

            job.PostComplete = ct => CompileJobConsumer.LoadCompileJob(compileJob, ct);
        }
예제 #2
0
        /// <summary>
        /// Pull the repository and compile for every set of given <paramref name="minutes"/>
        /// </summary>
        /// <param name="minutes">How many minutes the operation should repeat. Does not include running time</param>
        /// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
        /// <returns>A <see cref="Task"/> representing the running operation</returns>
        async Task TimerLoop(int minutes, CancellationToken cancellationToken)
        {
            while (true)
            {
                try
                {
                    await Task.Delay(new TimeSpan(0, minutes, 0), cancellationToken).ConfigureAwait(false);

                    try
                    {
                        CompileJob job = null;
                        //need this the whole time
                        await databaseContextFactory.UseContext(async (db) =>
                        {
                            //start up queries we'll need in the future
                            var instanceQuery  = db.Instances.Where(x => x.Id == metadata.Id);
                            var ddSettingsTask = instanceQuery.Select(x => x.DreamDaemonSettings).Select(x => new DreamDaemonSettings
                            {
                                StartupTimeout = x.StartupTimeout,
                                SecurityLevel  = x.SecurityLevel
                            }).FirstAsync(cancellationToken);
                            var dmSettingsTask         = instanceQuery.Select(x => x.DreamMakerSettings).FirstAsync(cancellationToken);
                            var repositorySettingsTask = instanceQuery.Select(x => x.RepositorySettings).FirstAsync(cancellationToken);

                            using (var repo = await RepositoryManager.LoadRepository(cancellationToken).ConfigureAwait(false))
                            {
                                if (repo == null)
                                {
                                    return;
                                }

                                //start the rev info query
                                var startSha    = repo.Head;
                                var revInfoTask = instanceQuery.SelectMany(x => x.RevisionInformations).Where(x => x.CommitSha == startSha).FirstOrDefaultAsync(cancellationToken);

                                //need repo setting to fetch
                                var repositorySettings = await repositorySettingsTask.ConfigureAwait(false);
                                await repo.FetchOrigin(repositorySettings.AccessUser, repositorySettings.AccessToken, null, cancellationToken).ConfigureAwait(false);

                                //take appropriate auto update actions
                                bool shouldSyncTracked;
                                if (repositorySettings.AutoUpdatesKeepTestMerges.Value)
                                {
                                    var result = await repo.MergeOrigin(repositorySettings.CommitterName, repositorySettings.CommitterEmail, cancellationToken).ConfigureAwait(false);
                                    if (!result.HasValue)
                                    {
                                        return;
                                    }
                                    shouldSyncTracked = result.Value;
                                }
                                else
                                {
                                    await repo.ResetToOrigin(cancellationToken).ConfigureAwait(false);
                                    shouldSyncTracked = true;
                                }

                                //synch if necessary
                                if (repositorySettings.AutoUpdatesSynchronize.Value && startSha != repo.Head)
                                {
                                    await repo.Sychronize(repositorySettings.AccessUser, repositorySettings.AccessToken, shouldSyncTracked, cancellationToken).ConfigureAwait(false);
                                }

                                //finish other queries
                                var dmSettings = await dmSettingsTask.ConfigureAwait(false);
                                var ddSettings = await ddSettingsTask.ConfigureAwait(false);
                                var revInfo    = await revInfoTask.ConfigureAwait(false);

                                //null rev info handling
                                if (revInfo == default)
                                {
                                    var currentSha = repo.Head;
                                    revInfo        = new RevisionInformation
                                    {
                                        CommitSha       = currentSha,
                                        OriginCommitSha = currentSha,
                                        Instance        = new Models.Instance
                                        {
                                            Id = metadata.Id
                                        }
                                    };
                                    db.Instances.Attach(revInfo.Instance);
                                }

                                //finally start compile
                                job = await DreamMaker.Compile(revInfo, dmSettings, ddSettings.SecurityLevel.Value, ddSettings.StartupTimeout.Value, repo, cancellationToken).ConfigureAwait(false);
                            }

                            db.CompileJobs.Add(job);
                            await db.Save(cancellationToken).ConfigureAwait(false);
                        }).ConfigureAwait(false);

                        await CompileJobConsumer.LoadCompileJob(job, cancellationToken).ConfigureAwait(false);
                    }
                    catch (OperationCanceledException)
                    {
                        throw;
                    }
                    catch (Exception e)
                    {
                        logger.LogWarning("Error in auto update loop! Exception: {0}", e);
                        continue;
                    }
                }
                catch (OperationCanceledException)
                {
                    break;
                }
            }
        }
예제 #3
0
        /// <inheritdoc />
        public async Task CompileProcess(Job job, IDatabaseContext databaseContext, Action <int> progressReporter, CancellationToken cancellationToken)
        {
            //DO NOT FOLLOW THE SUGGESTION FOR A THROW EXPRESSION HERE
            if (job == null)
            {
                throw new ArgumentNullException(nameof(job));
            }
            if (databaseContext == null)
            {
                throw new ArgumentNullException(nameof(databaseContext));
            }
            if (progressReporter == null)
            {
                throw new ArgumentNullException(nameof(progressReporter));
            }

            var ddSettingsTask = databaseContext.DreamDaemonSettings.Where(x => x.InstanceId == metadata.Id).Select(x => new DreamDaemonSettings
            {
                StartupTimeout = x.StartupTimeout,
            }).FirstOrDefaultAsync(cancellationToken);

            var compileJobsTask = databaseContext.CompileJobs
                                  .Where(x => x.Job.Instance.Id == metadata.Id)
                                  .OrderByDescending(x => x.Job.StoppedAt)
                                  //TODO: Replace with this select when the issues linked in https://github.com/tgstation/tgstation-server/issues/737 are fixed
                                  //.Select(x => x.Job.StoppedAt.Value - x.Job.StartedAt.Value)
                                  .Select(x => new Job
            {
                StoppedAt = x.Job.StoppedAt,
                StartedAt = x.Job.StartedAt
            })
                                  .Take(10)
                                  .ToListAsync(cancellationToken);

            var dreamMakerSettings = await databaseContext.DreamMakerSettings.Where(x => x.InstanceId == metadata.Id).FirstAsync(cancellationToken).ConfigureAwait(false);

            if (dreamMakerSettings == default)
            {
                throw new JobException("Missing DreamMakerSettings in DB!");
            }
            var ddSettings = await ddSettingsTask.ConfigureAwait(false);

            if (ddSettings == default)
            {
                throw new JobException("Missing DreamDaemonSettings in DB!");
            }

            Task <RepositorySettings> repositorySettingsTask = null;
            string              repoOwner = null;
            string              repoName  = null;
            CompileJob          compileJob;
            RevisionInformation revInfo;

            using (var repo = await RepositoryManager.LoadRepository(cancellationToken).ConfigureAwait(false))
            {
                if (repo == null)
                {
                    throw new JobException("Missing Repository!");
                }

                if (repo.IsGitHubRepository)
                {
                    repoOwner = repo.GitHubOwner;
                    repoName  = repo.GitHubRepoName;
                    repositorySettingsTask = databaseContext.RepositorySettings.Where(x => x.InstanceId == metadata.Id).Select(x => new RepositorySettings
                    {
                        AccessToken             = x.AccessToken,
                        ShowTestMergeCommitters = x.ShowTestMergeCommitters
                    }).FirstOrDefaultAsync(cancellationToken);
                }

                var repoSha = repo.Head;
                revInfo = await databaseContext.RevisionInformations.Where(x => x.CommitSha == repoSha && x.Instance.Id == metadata.Id).Include(x => x.ActiveTestMerges).ThenInclude(x => x.TestMerge).ThenInclude(x => x.MergedBy).FirstOrDefaultAsync().ConfigureAwait(false);

                if (revInfo == default)
                {
                    revInfo = new RevisionInformation
                    {
                        CommitSha       = repoSha,
                        OriginCommitSha = repoSha,
                        Instance        = new Models.Instance
                        {
                            Id = metadata.Id
                        }
                    };
                    logger.LogWarning(Repository.Repository.OriginTrackingErrorTemplate, repoSha);
                    databaseContext.Instances.Attach(revInfo.Instance);
                }

                TimeSpan?averageSpan         = null;
                var      previousCompileJobs = await compileJobsTask.ConfigureAwait(false);

                if (previousCompileJobs.Count != 0)
                {
                    var totalSpan = TimeSpan.Zero;
                    foreach (var I in previousCompileJobs)
                    {
                        totalSpan += I.StoppedAt.Value - I.StartedAt.Value;
                    }
                    averageSpan = totalSpan / previousCompileJobs.Count;
                }

                compileJob = await DreamMaker.Compile(revInfo, dreamMakerSettings, ddSettings.StartupTimeout.Value, repo, progressReporter, averageSpan, cancellationToken).ConfigureAwait(false);
            }

            compileJob.Job = job;

            databaseContext.CompileJobs.Add(compileJob);                //will be saved by job context

            job.PostComplete = ct => CompileJobConsumer.LoadCompileJob(compileJob, ct);

            if (repositorySettingsTask != null)
            {
                var repositorySettings = await repositorySettingsTask.ConfigureAwait(false);

                if (repositorySettings == default)
                {
                    throw new JobException("Missing repository settings!");
                }

                if (repositorySettings.AccessToken != null)
                {
                    //potential for commenting on a test merge change
                    var outgoingCompileJob = LatestCompileJob();

                    if (outgoingCompileJob != null && outgoingCompileJob.RevisionInformation.CommitSha != compileJob.RevisionInformation.CommitSha)
                    {
                        var gitHubClient = gitHubClientFactory.CreateClient(repositorySettings.AccessToken);

                        async Task CommentOnPR(int prNumber, string comment)
                        {
                            try
                            {
                                await gitHubClient.Issue.Comment.Create(repoOwner, repoName, prNumber, comment).ConfigureAwait(false);
                            }
                            catch (ApiException e)
                            {
                                logger.LogWarning("Error posting GitHub comment! Exception: {0}", e);
                            }
                        }

                        var tasks = new List <Task>();

                        string FormatTestMerge(TestMerge testMerge, bool updated) => String.Format(CultureInfo.InvariantCulture, "#### Test Merge {4}{0}{0}##### Server Instance{0}{5}{1}{0}{0}##### Revision{0}Origin: {6}{0}Pull Request: {2}{0}Server: {7}{3}",
                                                                                                   Environment.NewLine,
                                                                                                   repositorySettings.ShowTestMergeCommitters.Value ? String.Format(CultureInfo.InvariantCulture, "{0}{0}##### Merged By{0}{1}", Environment.NewLine, testMerge.MergedBy.Name) : String.Empty,
                                                                                                   testMerge.PullRequestRevision,
                                                                                                   testMerge.Comment != null ? String.Format(CultureInfo.InvariantCulture, "{0}{0}##### Comment{0}{1}", Environment.NewLine, testMerge.Comment) : String.Empty,
                                                                                                   updated ? "Updated" : "Deployed",
                                                                                                   metadata.Name,
                                                                                                   compileJob.RevisionInformation.OriginCommitSha,
                                                                                                   compileJob.RevisionInformation.CommitSha
                                                                                                   );

                        //added prs
                        foreach (var I in compileJob
                                 .RevisionInformation
                                 .ActiveTestMerges
                                 .Select(x => x.TestMerge)
                                 .Where(x => !outgoingCompileJob
                                        .RevisionInformation
                                        .ActiveTestMerges
                                        .Any(y => y.TestMerge.Number == x.Number)))
                        {
                            tasks.Add(CommentOnPR(I.Number.Value, FormatTestMerge(I, false)));
                        }

                        //removed prs
                        foreach (var I in outgoingCompileJob
                                 .RevisionInformation
                                 .ActiveTestMerges
                                 .Select(x => x.TestMerge)
                                 .Where(x => !compileJob
                                        .RevisionInformation
                                        .ActiveTestMerges
                                        .Any(y => y.TestMerge.Number == x.Number)))
                        {
                            tasks.Add(CommentOnPR(I.Number.Value, "#### Test Merge Removed"));
                        }

                        //updated prs
                        foreach (var I in compileJob
                                 .RevisionInformation
                                 .ActiveTestMerges
                                 .Select(x => x.TestMerge)
                                 .Where(x => outgoingCompileJob
                                        .RevisionInformation
                                        .ActiveTestMerges
                                        .Any(y => y.TestMerge.Number == x.Number)))
                        {
                            tasks.Add(CommentOnPR(I.Number.Value, FormatTestMerge(I, true)));
                        }

                        if (tasks.Any())
                        {
                            await Task.WhenAll(tasks).ConfigureAwait(false);
                        }
                    }
                }
            }
        }