public void Full() { string configTmpFile = Path.GetTempFileName(); try { File.WriteAllText(configTmpFile, BotConfigsForTesting.Full()); MultilinerBotConfiguration config = MultilinerBotConfiguration.BuidFromConfigFile(configTmpFile); Assert.That(config.Server, Is.EqualTo("localhost:8084")); Assert.That(config.Repository, Is.EqualTo("assets")); Assert.That(config.BranchPrefix, Is.EqualTo("AST-")); Assert.That(config.MergeToBranchesAttrName, Is.EqualTo("target")); Assert.That(config.UserApiKey, Is.EqualTo("BAEE806DB01")); Assert.That(config.Plastic, Is.Not.Null); Assert.That(config.Plastic.IsApprovedCodeReviewFilterEnabled, Is.False); Assert.That(config.Plastic.IsBranchAttrFilterEnabled, Is.True); Assert.That(config.Plastic.StatusAttribute, Is.Not.Null); Assert.That(config.Plastic.StatusAttribute.Name, Is.EqualTo("status")); Assert.That(config.Plastic.StatusAttribute.ResolvedValue, Is.EqualTo("resolved")); Assert.That(config.Plastic.StatusAttribute.TestingValue, Is.EqualTo("testing")); Assert.That(config.Plastic.StatusAttribute.FailedValue, Is.EqualTo("failed")); Assert.That(config.Plastic.StatusAttribute.MergedValue, Is.EqualTo("merged")); Assert.That(config.Issues, Is.Not.Null); Assert.That(config.Issues.Plug, Is.EqualTo("tts")); Assert.That(config.Issues.ProjectKey, Is.EqualTo("AST")); Assert.That(config.Issues.TitleField, Is.EqualTo("title")); Assert.That(config.Issues.StatusField, Is.Not.Null); Assert.That(config.Issues.StatusField.Name, Is.EqualTo("status")); Assert.That(config.Issues.StatusField.ResolvedValue, Is.EqualTo("validated")); Assert.That(config.Issues.StatusField.TestingValue, Is.EqualTo("testing")); Assert.That(config.Issues.StatusField.FailedValue, Is.EqualTo("open")); Assert.That(config.Issues.StatusField.MergedValue, Is.EqualTo("closed")); Assert.That(config.CI, Is.Not.Null); Assert.That(config.CI.Plug, Is.EqualTo("My Jenkins")); Assert.That(config.CI.PlanBranch, Is.EqualTo("debug plan")); Assert.That(config.CI.PlanAfterCheckin, Is.EqualTo("release plan")); Assert.That(config.Notifiers, Is.Not.Null.And.Count.EqualTo(1)); Assert.That(config.Notifiers[0].Name, Is.EqualTo("notifier1")); Assert.That(config.Notifiers[0].Plug, Is.EqualTo("email")); Assert.That(config.Notifiers[0].UserProfileField, Is.EqualTo("email")); Assert.That( config.Notifiers[0].FixedRecipients, Is.Not.Null.And.Length.EqualTo(2).And.Contains("me").And.Contains("you")); } finally { if (File.Exists(configTmpFile)) { File.Delete(configTmpFile); } } }
static BuildProperties CreateBuildProperties( IRestApi restApi, string taskNumber, string branchName, string labelName, string buildStagePreCiOrPostCi, string destinationBranch, MultilinerBotConfiguration botConfig) { int branchHeadChangesetId = MultilinerMergebotApi.GetBranchHead( restApi, botConfig.Repository, branchName); ChangesetModel branchHeadChangeset = MultilinerMergebotApi.GetChangeset( restApi, botConfig.Repository, branchHeadChangesetId); int trunkHeadChangesetId = MultilinerMergebotApi.GetBranchHead( restApi, botConfig.Repository, destinationBranch); ChangesetModel trunkHeadChangeset = MultilinerMergebotApi.GetChangeset( restApi, botConfig.Repository, trunkHeadChangesetId); return(new BuildProperties { BranchName = branchName, TaskNumber = taskNumber, BranchHead = branchHeadChangeset.ChangesetId.ToString(), BranchHeadGuid = branchHeadChangeset.Guid.ToString(), ChangesetOwner = branchHeadChangeset.Owner, TrunkBranchName = destinationBranch, TrunkHead = trunkHeadChangeset.ChangesetId.ToString(), TrunkHeadGuid = trunkHeadChangeset.Guid.ToString(), RepSpec = string.Format("{0}@{1}", botConfig.Repository, botConfig.Server), LabelName = labelName, Stage = buildStagePreCiOrPostCi }); }
internal MultilinerBot( IRestApi restApi, MultilinerBotConfiguration multilinerBotConfig, string branchesQueueFilePath, string codeReviewsTrackedFilePath, string botName) { mMultilinerBotConfig = multilinerBotConfig; mBranchesQueueFilePath = branchesQueueFilePath; mCodeReviewsTrackedFilePath = codeReviewsTrackedFilePath; mBotName = botName; mRestApi = restApi; }
public void OnlyCIPlug() { string configTmpFile = Path.GetTempFileName(); try { File.WriteAllText(configTmpFile, BotConfigsForTesting.OnlyCIPlug()); MultilinerBotConfiguration config = MultilinerBotConfiguration.BuidFromConfigFile(configTmpFile); Assert.That(config.Server, Is.EqualTo("localhost:8084")); Assert.That(config.Repository, Is.EqualTo("assets")); Assert.That(config.BranchPrefix, Is.EqualTo("AST-")); Assert.That(config.MergeToBranchesAttrName, Is.EqualTo("target")); Assert.That(config.UserApiKey, Is.EqualTo("BAEE806DB01")); Assert.That(config.Plastic, Is.Not.Null); Assert.That(config.Plastic.IsApprovedCodeReviewFilterEnabled, Is.False); Assert.That(config.Plastic.IsBranchAttrFilterEnabled, Is.True); Assert.That(config.Plastic.StatusAttribute, Is.Not.Null); Assert.That(config.Plastic.StatusAttribute.Name, Is.EqualTo("status")); Assert.That(config.Plastic.StatusAttribute.ResolvedValue, Is.EqualTo("resolved")); Assert.That(config.Plastic.StatusAttribute.TestingValue, Is.EqualTo("testing")); Assert.That(config.Plastic.StatusAttribute.FailedValue, Is.EqualTo("failed")); Assert.That(config.Plastic.StatusAttribute.MergedValue, Is.EqualTo("merged")); Assert.That(config.Issues, Is.Null); Assert.That(config.CI, Is.Not.Null); Assert.That(config.CI.Plug, Is.EqualTo("My Jenkins")); Assert.That(config.CI.PlanBranch, Is.EqualTo("debug plan")); Assert.That(config.CI.PlanAfterCheckin, Is.EqualTo("release plan")); Assert.That(config.Notifiers, Is.Not.Null.And.Empty); } finally { if (File.Exists(configTmpFile)) { File.Delete(configTmpFile); } } }
internal static Result SetTaskAsFailed( IRestApi restApi, Branch branch, string taskNumber, MultilinerBotConfiguration botConfig, string codeReviewsStorageFile) { Result result = new Result(); result.IsSuccessful = true; try { if (botConfig.Plastic.IsApprovedCodeReviewFilterEnabled) { SetBranchReviewsAsPending( restApi, branch.Repository, branch.Id, codeReviewsStorageFile); } MultilinerMergebotApi.ChangeBranchAttribute( restApi, branch.Repository, branch.FullName, botConfig.Plastic.StatusAttribute.Name, botConfig.Plastic.StatusAttribute.FailedValue); if (taskNumber != null && botConfig.Issues != null) { MultilinerMergebotApi.Issues.SetIssueField( restApi, botConfig.Issues.Plug, botConfig.Issues.ProjectKey, taskNumber, botConfig.Issues.StatusField.Name, botConfig.Issues.StatusField.FailedValue); } } catch (Exception ex) { result.IsSuccessful = false; result.ErrorMessage = BuildExceptionErrorMsg( branch.FullName, botConfig.Plastic.StatusAttribute.FailedValue, ex.Message); } return(result); }
internal static Result SetTaskAsTesting( IRestApi restApi, Branch branch, string taskNumber, MultilinerBotConfiguration botConfig) { Result result = new Result(); result.IsSuccessful = true; try { if (!string.IsNullOrEmpty(botConfig.Plastic.StatusAttribute.TestingValue)) { MultilinerMergebotApi.ChangeBranchAttribute( restApi, branch.Repository, branch.FullName, botConfig.Plastic.StatusAttribute.Name, botConfig.Plastic.StatusAttribute.TestingValue); } if (taskNumber != null && botConfig.Issues != null && !string.IsNullOrEmpty(botConfig.Issues.StatusField.TestingValue)) { MultilinerMergebotApi.Issues.SetIssueField( restApi, botConfig.Issues.Plug, botConfig.Issues.ProjectKey, taskNumber, botConfig.Issues.StatusField.Name, botConfig.Issues.StatusField.TestingValue); } } catch (Exception ex) { result.IsSuccessful = false; result.ErrorMessage = BuildExceptionErrorMsg( branch.FullName, botConfig.Plastic.StatusAttribute.TestingValue, ex.Message); } return(result); }
static int Main(string[] args) { string botName = null; try { MultilinerBotArguments botArgs = new MultilinerBotArguments(args); bool bValidArgs = botArgs.Parse(); botName = botArgs.BotName; ConfigureLogging(botName); mLog.InfoFormat("MultilinerBot [{0}] started. Version [{1}]", botName, System.Reflection.Assembly.GetExecutingAssembly().GetName().Version); string argsStr = args == null ? string.Empty : string.Join(" ", args); mLog.DebugFormat("Args: [{0}]. Are valid args?: [{1}]", argsStr, bValidArgs); if (!bValidArgs || botArgs.ShowUsage) { PrintUsage(); if (botArgs.ShowUsage) { mLog.InfoFormat( "MultilinerBot [{0}] is going to finish: " + "user explicitly requested to show usage.", botName); return(0); } mLog.ErrorFormat( "MultilinerBot [{0}] is going to finish: " + "invalid arguments found in command line.", botName); return(0); } string errorMessage = null; if (!MultilinerBotArgumentsChecker.CheckArguments( botArgs, out errorMessage)) { Console.WriteLine(errorMessage); mLog.ErrorFormat( "MultilinerBot [{0}] is going to finish: error found on arguments check", botName); mLog.Error(errorMessage); return(1); } MultilinerBotConfiguration botConfig = MultilinerBotConfiguration. BuidFromConfigFile(botArgs.ConfigFilePath); errorMessage = null; if (!MultilinerBotConfigurationChecker.CheckConfiguration( botConfig, out errorMessage)) { Console.WriteLine(errorMessage); mLog.ErrorFormat( "MultilinerBot [{0}] is going to finish: error found on configuration check", botName); mLog.Error(errorMessage); return(1); } ConfigureServicePoint(); string escapedBotName = GetEscapedBotName(botName); LaunchMultilinerMergebot( botArgs.WebSocketUrl, botArgs.RestApiUrl, botConfig, ToolConfig.GetBranchesFile(escapedBotName), ToolConfig.GetCodeReviewsFile(escapedBotName), botName, botArgs.ApiKey); mLog.InfoFormat( "MultilinerBot [{0}] is going to finish: orderly shutdown.", botName); return(0); } catch (Exception e) { Console.WriteLine(e.Message); mLog.FatalFormat( "MultilinerBot [{0}] is going to finish: uncaught exception " + "thrown during execution. Message: {1}", botName, e.Message); mLog.DebugFormat("Stack trace:{0}{1}", Environment.NewLine, e.StackTrace); return(1); } }
static void LaunchMultilinerMergebot( string webSocketUrl, string restApiUrl, MultilinerBotConfiguration botConfig, string branchesQueueFilePath, string codeReviewsTrackedFilePath, string botName, string apiKey) { if (!Directory.Exists(Path.GetDirectoryName(branchesQueueFilePath))) { Directory.CreateDirectory(Path.GetDirectoryName(branchesQueueFilePath)); } IRestApi restApi = new RestApi(restApiUrl, botConfig.UserApiKey); if (!EnsureAttributeExists( restApi, botName, botConfig.Repository, botConfig.Plastic.StatusAttribute.Name)) { mLog.FatalFormat(BuildUnableToCreateAttrMessage( botName, botConfig.Repository, botConfig.Plastic.StatusAttribute.Name)); return; } if (!EnsureAttributeExists( restApi, botName, botConfig.Repository, botConfig.MergeToBranchesAttrName)) { mLog.FatalFormat(BuildUnableToCreateAttrMessage( botName, botConfig.Repository, botConfig.MergeToBranchesAttrName)); return; } MultilinerBot multilinerBot = new MultilinerBot( restApi, botConfig, branchesQueueFilePath, codeReviewsTrackedFilePath, botName); try { multilinerBot.LoadBranchesToProcess(); } catch (Exception e) { mLog.FatalFormat( "MultilinerBot [{0}] is going to finish because it couldn't load " + "the branches to process on startup. Reason: {1}", botName, e.Message); mLog.DebugFormat("Stack trace:{0}{1}", Environment.NewLine, e.StackTrace); throw; } ThreadPool.QueueUserWorkItem(multilinerBot.ProcessBranches); string[] eventsToSubscribe = GetEventNamesToSuscribe( botConfig.Plastic.IsApprovedCodeReviewFilterEnabled, botConfig.Plastic.IsBranchAttrFilterEnabled); WebSocketClient ws = new WebSocketClient( webSocketUrl, botName, apiKey, eventsToSubscribe, multilinerBot.OnEventReceived); ws.ConnectWithRetries(); Task.Delay(-1).Wait(); }
internal static Result PostCheckinStage( IRestApi restApi, Branch branch, MergeReport mergeReport, string taskNumber, string[] destinationBranches, MergeToOperations.CheckinResult mergesToCheckins, MultilinerBotConfiguration botConfig, string codeReviewsStorageFile) { if (!HasToRunPlanAfterTaskMerged(botConfig.CI)) { string noCIMessage = "No Continuous Integration Plug was set for this mergebot. Therefore, no " + "build actions for task " + taskNumber + " will be performed."; mLog.Info(noCIMessage); return(Result.Ok); } if (mergesToCheckins == null || mergesToCheckins.ChangesetsByTargetBranch == null || mergesToCheckins.ChangesetsByTargetBranch.Count == 0) { string noChangesetsErrorMessage = string.Format( "Something wrong happened. There are no merge-to changesets to build after " + "merging branch [{0}] to its destination branches.", branch.FullName); mLog.Info(noChangesetsErrorMessage); Notifier.NotifyTaskStatus( restApi, branch.Owner, noChangesetsErrorMessage, botConfig.Notifiers); return(Result.Failed); } string startTestingMessage = string.Format( "Testing branch [{0}] after being merged in the following destination branches: [{1}].", branch.FullName, string.Join(", ", mergesToCheckins.ChangesetsByTargetBranch.Keys)); mLog.Info(startTestingMessage); Notifier.NotifyTaskStatus( restApi, branch.Owner, startTestingMessage, botConfig.Notifiers); int iniTime = Environment.TickCount; BuildOperations.Result result = BuildOperations.TryBuildTask( restApi, branch, mergeReport, taskNumber, destinationBranches, mergesToCheckins.ChangesetsByTargetBranch, Messages.BuildProperties.StageValues.POST_CHECKIN, botConfig); BuildMergeReport.AddBuildTimeProperty( mergeReport, Environment.TickCount - iniTime); if (result.AreAllSuccessful) { BuildMergeReport.AddSucceededBuildProperty( mergeReport, botConfig.CI.PlanAfterCheckin); string notifyOKMesage = string.Format( "Build successful after merging branch [{0}] " + "to the following destination branches: [{1}].", branch.FullName, string.Join(", ", mergesToCheckins.ChangesetsByTargetBranch.Keys)); Notifier.NotifyTaskStatus( restApi, branch.Owner, notifyOKMesage, botConfig.Notifiers); return(Result.Ok); } string errorMessage = string.Join(Environment.NewLine, result.ErrorMessages); BuildMergeReport.AddFailedBuildProperty( mergeReport, botConfig.CI.PlanAfterCheckin, errorMessage); Notifier.NotifyTaskStatus( restApi, branch.Owner, errorMessage, botConfig.Notifiers); return(Result.Failed); }
internal static bool PreCheckinStage( IRestApi restApi, Branch branch, MergeReport mergeReport, string taskNumber, string[] destinationBranches, MergeToOperations.ShelveResult mergesToShelves, MultilinerBotConfiguration botConfig, string codeReviewsStorageFile) { if (botConfig.CI == null) { string noCIMessage = "No Continuous Integration Plug was set for this mergebot. Therefore, no " + "build actions for task " + taskNumber + " will be performed."; mLog.Info(noCIMessage); Notifier.NotifyTaskStatus( restApi, branch.Owner, noCIMessage, botConfig.Notifiers); return(true); } if (mergesToShelves == null || mergesToShelves.ShelvesByTargetBranch == null || mergesToShelves.ShelvesByTargetBranch.Count == 0) { string noShelvesErrorMessage = "Something wrong happened. There are no merge-to shelves to build task " + taskNumber; mLog.Info(noShelvesErrorMessage); Notifier.NotifyTaskStatus( restApi, branch.Owner, noShelvesErrorMessage, botConfig.Notifiers); ChangeTaskStatus.SetTaskAsFailed( restApi, branch, taskNumber, botConfig, codeReviewsStorageFile); return(false); } string startTestingMessage = string.Format( "Testing branch [{0}] before being merged in the following destination branches: [{1}].", branch.FullName, string.Join(", ", mergesToShelves.ShelvesByTargetBranch.Keys)); mLog.Info(startTestingMessage); ChangeTaskStatus.SetTaskAsTesting(restApi, branch, taskNumber, botConfig); Notifier.NotifyTaskStatus( restApi, branch.Owner, startTestingMessage, botConfig.Notifiers); int iniTime = Environment.TickCount; BuildOperations.Result result = BuildOperations.TryBuildTask( restApi, branch, mergeReport, taskNumber, destinationBranches, mergesToShelves.ShelvesByTargetBranch, Messages.BuildProperties.StageValues.PRE_CHECKIN, botConfig); BuildMergeReport.AddBuildTimeProperty( mergeReport, Environment.TickCount - iniTime); if (result.AreAllSuccessful) { BuildMergeReport.AddSucceededBuildProperty( mergeReport, botConfig.CI.PlanBranch); return(true); } string errorMessage = string.Join(Environment.NewLine, result.ErrorMessages); BuildMergeReport.AddFailedBuildProperty( mergeReport, botConfig.CI.PlanBranch, errorMessage); ChangeTaskStatus.SetTaskAsFailed( restApi, branch, taskNumber, botConfig, codeReviewsStorageFile); Notifier.NotifyTaskStatus( restApi, branch.Owner, errorMessage, botConfig.Notifiers); return(false); }
public static Result TryProcessBranch( IRestApi restApi, Branch branch, MultilinerBotConfiguration botConfig, string botName, string codeReviewsStorageFile) { string taskNumber = null; MergeReport mergeReport = null; string[] destinationBranches = null; MergeToOperations.ShelveResult mergesToShelves = null; string notificationMsg = null; try { mLog.InfoFormat("Getting task number of branch {0} ...", branch.FullName); taskNumber = GetTaskNumber(branch.FullName, botConfig.BranchPrefix); if (!IsTaskReady( restApi, taskNumber, botConfig.Issues, botConfig.Plastic.IsApprovedCodeReviewFilterEnabled, branch.Repository, branch.Id, codeReviewsStorageFile)) { return(Result.NotReady); } destinationBranches = GetMergeToDestinationBranches( restApi, branch, botConfig.MergeToBranchesAttrName); if (destinationBranches == null || destinationBranches.Length == 0) { notificationMsg = string.Format( "The attribute [{0}] of branch [{1}@{2}@{3}] is not properly set. " + "Branch [{1}@{2}@{3}] status will be set as 'failed': [{4}].", botConfig.MergeToBranchesAttrName, branch.FullName, branch.Repository, botConfig.Server, botConfig.Plastic.StatusAttribute.FailedValue); mLog.Warn(notificationMsg); ChangeTaskStatus.SetTaskAsFailed( restApi, branch, taskNumber, botConfig, codeReviewsStorageFile); Notifier.NotifyTaskStatus( restApi, branch.Owner, notificationMsg, botConfig.Notifiers); return(Result.Failed); } foreach (string destinationBranch in destinationBranches) { if (ExistsBranch(restApi, destinationBranch, branch.Repository)) { continue; } notificationMsg = string.Format( "The destination branch [{0}@{1}@{2}] specified in attribute [{3}] " + "of branch [{4}@{1}@{2}] does not exist. " + "Branch [{4}@{1}@{2}] status will be set as 'failed': [{5}].", destinationBranch, branch.Repository, botConfig.Server, botConfig.MergeToBranchesAttrName, branch.FullName, botConfig.Plastic.StatusAttribute.FailedValue); mLog.Warn(notificationMsg); ChangeTaskStatus.SetTaskAsFailed( restApi, branch, taskNumber, botConfig, codeReviewsStorageFile); Notifier.NotifyTaskStatus( restApi, branch.Owner, notificationMsg, botConfig.Notifiers); return(Result.Failed); } foreach (string destinationBranch in destinationBranches) { if (IsMergeAllowed(restApi, branch, destinationBranch)) { continue; } return(Result.NotReady); } mLog.InfoFormat("Building the merge report of task {0} ...", taskNumber); mergeReport = BuildMergeReport.Build( MultilinerMergebotApi.GetBranch(restApi, branch.Repository, branch.FullName)); string taskTittle; string taskUrl; if (GetIssueInfo( restApi, taskNumber, botConfig.Issues, out taskTittle, out taskUrl)) { BuildMergeReport.AddIssueProperty(mergeReport, taskTittle, taskUrl); } mLog.InfoFormat("Trying to shelve server-side-merge from [{0}] to [{1}]", branch.FullName, string.Join(", ", destinationBranches)); mergesToShelves = MergeToOperations.TryMergeToShelves( restApi, branch, destinationBranches, mergeReport, taskTittle, botName); if (NoMergesNeeded(mergesToShelves)) { ChangeTaskStatus.Result chStatResult = ChangeTaskStatus.SetTaskAsMerged( restApi, branch, taskNumber, botConfig, codeReviewsStorageFile); notificationMsg = string.Join(Environment.NewLine, mergesToShelves.MergesNotNeededMessages); if (!string.IsNullOrWhiteSpace(chStatResult.ErrorMessage)) { notificationMsg = string.Concat( notificationMsg, Environment.NewLine, chStatResult.ErrorMessage); } Notifier.NotifyTaskStatus( restApi, branch.Owner, notificationMsg, botConfig.Notifiers); return(Result.Failed); } if (mergesToShelves.ErrorMessages.Count > 0) { ChangeTaskStatus.Result chStatResult = ChangeTaskStatus.SetTaskAsFailed( restApi, branch, taskNumber, botConfig, codeReviewsStorageFile); notificationMsg = string.Join(Environment.NewLine, mergesToShelves.ErrorMessages); if (mergesToShelves.MergesNotNeededMessages.Count > 0) { notificationMsg = string.Concat( notificationMsg, Environment.NewLine, string.Join(Environment.NewLine, mergesToShelves.MergesNotNeededMessages)); } if (!string.IsNullOrWhiteSpace(chStatResult.ErrorMessage)) { notificationMsg = string.Concat( notificationMsg, Environment.NewLine, chStatResult.ErrorMessage); } Notifier.NotifyTaskStatus( restApi, branch.Owner, notificationMsg, botConfig.Notifiers); return(Result.Failed); } if (mergesToShelves.MergesNotNeededMessages.Count > 0) { string alreadyMergedMessage = string.Join( Environment.NewLine + "\t", mergesToShelves.MergesNotNeededMessages); notificationMsg = string.Format( "Branch [{0}] is already merged to some " + "of the specified destination branches in the attribute [{1}]. " + "The {2} mergebot will continue building the " + "merge(s) from branch [{0}] to [{3}].{4}{4}" + "Report of already merged branches:{4}\t{5}", branch.FullName, botConfig.MergeToBranchesAttrName, botName, string.Join(", ", mergesToShelves.ShelvesByTargetBranch.Keys), Environment.NewLine, alreadyMergedMessage); mLog.Info(notificationMsg); Notifier.NotifyTaskStatus( restApi, branch.Owner, notificationMsg, botConfig.Notifiers); } bool allBuildsOk = Build.PreCheckinStage( restApi, branch, mergeReport, taskNumber, destinationBranches, mergesToShelves, botConfig, codeReviewsStorageFile); if (!allBuildsOk) { return(Result.Failed); } MergeToOperations.CheckinResult mergesToCheckins = MergeToOperations.TryApplyShelves( restApi, branch, destinationBranches, mergesToShelves, mergeReport, taskNumber, taskTittle, botName, botConfig, codeReviewsStorageFile); //checkin went OK in all target branches if (mergesToCheckins.ErrorMessages.Count == 0 && mergesToCheckins.DestinationNewChangesWarnings.Count == 0) { mLog.InfoFormat("Setting branch {0} as 'integrated'...", branch.FullName); ChangeTaskStatus.SetTaskAsMerged( restApi, branch, taskNumber, botConfig, codeReviewsStorageFile); notificationMsg = string.Format( "OK: Branch [{0}] was successfully merged to [{1}]", branch.FullName, string.Join(", ", mergesToShelves.ShelvesByTargetBranch.Keys)); Notifier.NotifyTaskStatus( restApi, branch.Owner, notificationMsg, botConfig.Notifiers); return(Build.PostCheckinStage( restApi, branch, mergeReport, taskNumber, destinationBranches, mergesToCheckins, botConfig, codeReviewsStorageFile)); } //some of the checkins went wrong -> we must run the post-ci plan for the successful ones, //but force-set as failed the source branch (or requeue if the errors are due to New DstChanges if (mergesToCheckins.ErrorMessages.Count > 0) { ChangeTaskStatus.Result chStatResult = ChangeTaskStatus.SetTaskAsFailed( restApi, branch, taskNumber, botConfig, codeReviewsStorageFile); string checkinErrorMessage = string.Join( Environment.NewLine + "\t", mergesToCheckins.ErrorMessages); if (mergesToCheckins.DestinationNewChangesWarnings.Count > 0) { string dstChangesErrorMessage = string.Join( Environment.NewLine + "\t", mergesToCheckins.DestinationNewChangesWarnings); checkinErrorMessage = string.Concat( checkinErrorMessage, Environment.NewLine + "\t", dstChangesErrorMessage); } notificationMsg = string.Format( "Failed build. The result of building merges from branch [{0}] to [{1}] went OK, " + "but there were some errors checking-in the resulting shelves:{2}\t{3}{2}{2}{4}", branch.FullName, string.Join(", ", mergesToShelves.ShelvesByTargetBranch.Keys), Environment.NewLine, checkinErrorMessage, string.IsNullOrWhiteSpace(chStatResult.ErrorMessage) ? string.Empty : chStatResult.ErrorMessage); mLog.Warn(notificationMsg); if (mergesToCheckins.ChangesetsByTargetBranch.Count == 0) { Notifier.NotifyTaskStatus( restApi, branch.Owner, notificationMsg, botConfig.Notifiers); return(Result.Failed); } Build.PostCheckinStage( restApi, branch, mergeReport, taskNumber, destinationBranches, mergesToCheckins, botConfig, codeReviewsStorageFile); Notifier.NotifyTaskStatus( restApi, branch.Owner, notificationMsg, botConfig.Notifiers); return(Result.Failed); } //LAST block -> if (mergesToCheckins.DestinationNewChangesWarnings.Count > 0) //Some of the checkins went wrong due to new changeset on at least a destination branch-> //We must run the post-ci plan for the successful ones, //but force requeuing of source branch, so the failed merges to dst branches are re-run mLog.InfoFormat("Setting branch {0} as 'resolved' (enqueue) ...", branch.FullName); MultilinerMergebotApi.ChangeBranchAttribute( restApi, branch.Repository, branch.FullName, botConfig.Plastic.StatusAttribute.Name, botConfig.Plastic.StatusAttribute.ResolvedValue); string dstBranchesNewCsetsMsg = string.Join( Environment.NewLine + "\t", mergesToCheckins.DestinationNewChangesWarnings); notificationMsg = string.Format( "Branch [{0}] will be enqueued again, as new changesets appeared in " + "merge destination branches, and thus, the branch needs to be tested again to " + "include those new changesets in the merge. Full report:{1}\t{2}", branch.FullName, Environment.NewLine, dstBranchesNewCsetsMsg); mLog.Warn(notificationMsg); if (mergesToCheckins.ChangesetsByTargetBranch.Count == 0) { Notifier.NotifyTaskStatus( restApi, branch.Owner, notificationMsg, botConfig.Notifiers); return(Result.NotReady); } Build.PostCheckinStage( restApi, branch, mergeReport, taskNumber, destinationBranches, mergesToCheckins, botConfig, codeReviewsStorageFile); Notifier.NotifyTaskStatus( restApi, branch.Owner, notificationMsg, botConfig.Notifiers); return(Result.NotReady); } catch (Exception ex) { mLog.ErrorFormat( "The attempt to process task {0} failed for branch {1}: {2}", taskNumber, branch.FullName, ex.Message); mLog.DebugFormat( "StackTrace:{0}{1}", Environment.NewLine, ex.StackTrace); ChangeTaskStatus.SetTaskAsFailed( restApi, branch, taskNumber, botConfig, codeReviewsStorageFile); notificationMsg = string.Format( "Can't process branch [{0}] because of an unexpected error: {1}.", branch.FullName, ex.Message); Notifier.NotifyTaskStatus( restApi, branch.Owner, notificationMsg, botConfig.Notifiers); BuildMergeReport.SetUnexpectedExceptionProperty(mergeReport, ex.Message); return(Result.Failed); } finally { ReportMerge(restApi, branch.Repository, branch.FullName, botName, mergeReport); MergeToOperations.SafeDeleteShelves( restApi, branch.Repository, destinationBranches, mergesToShelves); } }
internal static CheckinResult TryApplyShelves( IRestApi restApi, Branch branch, string[] destinationBranches, ShelveResult shelves, MergeReport mergeReport, string taskNumber, string taskTitle, string botName, MultilinerBotConfiguration botConfig, string codeReviewsStorageFile) { CheckinResult result = new CheckinResult(); int shelveId; foreach (string destinationBranch in destinationBranches) { if (!shelves.ShelvesByTargetBranch.ContainsKey(destinationBranch)) { continue; } shelveId = shelves.ShelvesByTargetBranch[destinationBranch]; mLog.InfoFormat( "Checking-in shelveset [{0}] from branch [{1}] to [{2}]", shelveId, branch.FullName, destinationBranch); MergeToResponse mergeResult = MultilinerMergebotApi.MergeShelveTo( restApi, branch.Repository, shelveId, destinationBranch, GetComment(branch.FullName, destinationBranch, taskTitle, botName), MultilinerMergebotApi.MergeToOptions.EnsureNoDstChanges); result.MergeStatusByTargetBranch[destinationBranch] = mergeResult.Status; BuildMergeReport.UpdateMergeProperty(mergeReport, mergeResult.Status, mergeResult.ChangesetNumber); if (mergeResult.Status == MergeToResultStatus.OK) { result.ChangesetsByTargetBranch[destinationBranch] = mergeResult.ChangesetNumber; mLog.InfoFormat( "Checkin: Created changeset [{0}] in branch [{1}]", mergeResult.ChangesetNumber, destinationBranch); continue; } if (mergeResult.Status == MergeToResultStatus.DestinationChanges) { string dstWarnMessage = string.Format( "Can't checkin shelve [{0}], the resulting shelve from merging branch " + "[{1}] to [{2}]. Reason: new changesets appeared in destination branch " + "while mergebot {3} was processing the merge from [{1}] to [{2}].{4}{5}", shelveId, branch.FullName, destinationBranch, botName, Environment.NewLine, mergeResult.Message); // it should checkin the shelve only on the exact parent shelve cset. // if there are new changes in the destination branch enqueue again the task result.DestinationNewChangesWarnings.Add(dstWarnMessage); continue; } string errorMsg = string.Format( "Can't checkin shelve [{0}], the resulting shelve from merging branch " + "[{1}] to [{2}]. Reason: {3}", shelveId, branch.FullName, destinationBranch, mergeResult.Message); result.ErrorMessages.Add(errorMsg); } return(result); }
internal static Result TryBuildTask( IRestApi restApi, Branch branch, MergeReport mergeReport, string taskNumber, string[] destinationBranches, Dictionary <string, int> branchMergesToObjectsMap, string buildStage, MultilinerBotConfiguration botConfig) { Result result = new Result(); result.AreAllSuccessful = true; string targetObjectPrefix = buildStage == BuildProperties.StageValues.PRE_CHECKIN ? "sh" : "cs"; string targetObjectName = buildStage == BuildProperties.StageValues.PRE_CHECKIN ? "shelve" : "changeset"; string ciPlan = buildStage == BuildProperties.StageValues.POST_CHECKIN ? botConfig.CI.PlanAfterCheckin : botConfig.CI.PlanBranch; string repSpec = string.Format("{0}@{1}", branch.Repository, botConfig.Server); string scmSpecToSwitchTo = string.Empty; BuildProperties properties = null; foreach (string destinationBranch in destinationBranches) { if (!branchMergesToObjectsMap.ContainsKey(destinationBranch)) { continue; } scmSpecToSwitchTo = string.Format( "{0}:{1}@{2}", targetObjectPrefix, branchMergesToObjectsMap[destinationBranch], repSpec); string comment = string.Format( "Building {0} [{1}], the resulting {0} from merging branch " + "[{2}] to [{3}]", targetObjectName, scmSpecToSwitchTo, branch.FullName, destinationBranch); properties = CreateBuildProperties( restApi, taskNumber, branch.FullName, string.Empty, buildStage, destinationBranch, botConfig); MultilinerMergebotApi.CI.PlanResult buildResult = MultilinerMergebotApi.CI.Build( restApi, botConfig.CI.Plug, ciPlan, scmSpecToSwitchTo, comment, properties); if (buildResult.Succeeded) { continue; } string errorMsg = string.Format( "Build failed. The build plan [{0}] of the resulting {1} [{2}] from merging branch " + "[{3}] to [{4}] has failed. " + "{5}" + "Please check your Continuous Integration report to find out more info about what happened.", ciPlan, targetObjectName, scmSpecToSwitchTo, branch.FullName, destinationBranch, string.IsNullOrWhiteSpace(buildResult.Explanation) ? string.Empty : "Error: [" + buildResult.Explanation + "]"); result.ErrorMessages.Add(errorMsg); result.AreAllSuccessful = false; if (buildStage == BuildProperties.StageValues.POST_CHECKIN) { continue; } return(result); } return(result); }