/// <summary> /// Gets the latest revision identifier from each project with a trigger method of Polling and pushes the project onto the build queue if there are recent changes. /// </summary> /// <returns></returns> public async Task PollOnceForChangesAsync() { var projectIds = await _repository.GetProjectsToPollForChanges(); await _logger.LogInfoAsync($"Checking for changes with {projectIds.Count()} project(s)."); foreach (var projectID in projectIds) { await _logger.LogDebugAsync($"Processing project {projectID}..."); var projectConfigTask = _repository.GetProject(projectID); var latestBuildTask = _repository.GetMostRecentBuildAsync(projectID); await Task.WhenAll(projectConfigTask, latestBuildTask); var projectConfig = projectConfigTask.Result; var latestBuild = latestBuildTask.Result; var codeRepo = CodeRepositoryFactory.Create(projectConfig); var codeStatus = await projectConfig.CheckForNeededBuild(codeRepo, latestBuild); if (codeStatus.NeedsBuild == true) { await _logger.LogInfoAsync($"Adding project {projectID} to the build queue."); await _queue.EnqueueBuild(new BuildQueueEntry() { BuildQueueID = Guid.NewGuid(), CreateDateTime = DateTimeOffset.Now, ProjectID = projectID, RevisionIdentifier = codeStatus.RevisionIdentifier, BuildNumber = projectConfig.NextBuildNumber }); } } await _logger.LogInfoAsync("Project status check complete."); }
/// <summary> /// Processes build jobs in the given queue one at a time until the queue is emtpy. /// </summary> /// <param name="projectQueue"></param> /// <returns></returns> public async Task ProcessQueueAsync(System.Collections.Concurrent.ConcurrentQueue <LocalQueuedJob> projectQueue) { LocalQueuedJob buildJob; while (projectQueue.TryDequeue(out buildJob)) { try { var environment = new BuildEnvironment(System.IO.Path.Combine(_options.WorkingPath, buildJob.Configuration.Name), buildJob.Output); int failCount = buildJob.QueueEntry.FailCount; environment.AddGlobalVariables(); environment.AddAgentVariables(_options); environment.AddProjectConfigurationVariables(buildJob.Configuration); environment.AddQueueEntryVariables(buildJob.QueueEntry); // Create the working path if it does not exist if (!System.IO.Directory.Exists(environment.WorkingPath)) { System.IO.Directory.CreateDirectory(environment.WorkingPath); } if (!System.IO.Directory.Exists(environment.CodePath)) { System.IO.Directory.CreateDirectory(environment.CodePath); } else { // Delete all the files and folders contained in the working path PathUtils.CleanFolder(environment.CodePath); } var maxFailureCount = buildJob.Configuration.MaxFailureCount; var codeRepo = CodeRepositoryFactory.Create(buildJob.Configuration); var codeInfo = await codeRepo.GetInfo(buildJob.Configuration.RepositoryPath, buildJob.RevisionIdentifier); environment.AddCodeInfoVariables(codeInfo); // export the code into the working code folder await codeRepo.Export(buildJob.Configuration.RepositoryPath, buildJob.RevisionIdentifier, environment.CodePath); // run each build task in order var buildResult = await buildJob.Configuration.Tasks.RunAllAsync(environment); if (buildResult.Success) { environment.WriteMessage(MessageSeverity.Info, "The build project completed successfully."); } else { failCount++; if (failCount >= maxFailureCount) { buildResult = BuildResult.Fail(buildResult.StatusCode, false); environment.WriteMessage(MessageSeverity.Error, $"The build project failed too many times ({failCount}) and will not be attempted again."); } else { environment.WriteMessage(MessageSeverity.Warn, $"The build project has failed on try {failCount} of {maxFailureCount} and will be attempted again."); } } await _repository.SetBuildResultAsync(buildJob.QueueEntry.BuildQueueID, buildResult); } catch (Exception ex) { await buildJob.Output.LogErrorAsync(ex.Message); await _repository.SetBuildResultAsync(buildJob.QueueEntry.BuildQueueID, BuildResult.Fail(3, true)); System.Diagnostics.Debug.WriteLine(ex.Message); } } }