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);
                }
            }
        }
Example #2
0
        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
            });
        }
Example #3
0
        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);
        }
Example #13
0
        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);
        }