Exemple #1
0
 internal static void AddBuildTimeProperty(
     MergeReport mergeReport, int timeInMilliseconds)
 {
     AddNumberProperty(
         mergeReport, "build time (min)",
         GetMinutes(timeInMilliseconds));
 }
Exemple #2
0
        static void ReportMerge(
            RestApi restApi,
            string repository,
            string branchName,
            string botName,
            MergeReport mergeReport)
        {
            if (mergeReport == null)
            {
                return;
            }

            try
            {
                TrunkMergebotApi.MergeReports.ReportMerge(restApi, botName, mergeReport);
            }
            catch (Exception ex)
            {
                mLog.ErrorFormat(
                    "Unable to report merge for branch '{0}' on repository '{1}': {2}",
                    branchName, repository, ex.Message);

                mLog.DebugFormat(
                    "StackTrace:{0}{1}",
                    Environment.NewLine, ex.StackTrace);
            }
        }
Exemple #3
0
        internal static void AddSucceededMergeProperty(
            MergeReport mergeReport, MergeToResultStatus status)
        {
            MergeReport.Entry succeededMergeProperty = new MergeReport.Entry();
            succeededMergeProperty.Text = GetMergeToResultStatus(status);
            succeededMergeProperty.Type = MERGE_OK_TYPE;

            mergeReport.Properties.Add(succeededMergeProperty);
        }
Exemple #4
0
        internal static MergeReport Build(BranchModel branch)
        {
            MergeReport result = new MergeReport();

            result.Timestamp    = DateTime.UtcNow;
            result.RepositoryId = branch.RepositoryId;
            result.BranchId     = branch.Id;
            result.Properties   = new List <MergeReport.Entry>();
            return(result);
        }
Exemple #5
0
        static void AddNumberProperty(
            MergeReport mergeReport, string text, double value)
        {
            MergeReport.Entry numberProperty = new MergeReport.Entry();
            numberProperty.Text  = text;
            numberProperty.Type  = "number";
            numberProperty.Value = value.ToString();

            mergeReport.Properties.Add(numberProperty);
        }
Exemple #6
0
        internal static void AddFailedMergeProperty(
            MergeReport mergeReport, MergeToResultStatus status, string message)
        {
            MergeReport.Entry failedMergeProperty = new MergeReport.Entry();
            failedMergeProperty.Text  = GetMergeToResultStatus(status);
            failedMergeProperty.Type  = MERGE_FAILED_TYPE;
            failedMergeProperty.Value = message;

            mergeReport.Properties.Add(failedMergeProperty);
        }
Exemple #7
0
        internal static void AddSucceededBuildProperty(
            MergeReport mergeReport, string planBranch)
        {
            MergeReport.Entry succeededBuildProperty = new MergeReport.Entry();
            succeededBuildProperty.Text = string.Format(
                "build ok (plan: {0})", planBranch);
            succeededBuildProperty.Type = BUILD_OK_TYPE;

            mergeReport.Properties.Add(succeededBuildProperty);
        }
Exemple #8
0
        internal static bool TryMergeToShelve(
            RestApi restApi,
            Branch branch,
            string destinationBranch,
            MergeReport mergeReport,
            string comment,
            string taskNumber,
            TrunkBotConfiguration botConfig,
            string codeReviewsStorageFile,
            out int shelveId)
        {
            shelveId = -1;

            MergeToResponse result = TrunkMergebotApi.MergeBranchTo(
                restApi, branch.Repository, branch.FullName, destinationBranch,
                comment, TrunkMergebotApi.MergeToOptions.CreateShelve);

            if (result.Status == MergeToResultStatus.MergeNotNeeded)
            {
                ChangeTaskStatus.SetTaskAsMerged(
                    restApi,
                    branch,
                    taskNumber,
                    string.Format(
                        "Branch {0} was already merged to {1} (MergeNotNeeded).",
                        branch.FullName,
                        botConfig.TrunkBranch),
                    botConfig,
                    codeReviewsStorageFile);
                return(false);
            }

            if (result.Status == MergeToResultStatus.AncestorNotFound ||
                result.Status == MergeToResultStatus.Conflicts ||
                result.Status == MergeToResultStatus.Error ||
                result.ChangesetNumber == 0)
            {
                BuildMergeReport.AddFailedMergeProperty(mergeReport, result.Status, result.Message);
                ChangeTaskStatus.SetTaskAsFailed(
                    restApi,
                    branch,
                    taskNumber,
                    string.Format(
                        "Can't merge branch {0}. Reason: {1}",
                        branch.FullName, result.Message),
                    botConfig,
                    codeReviewsStorageFile);
                return(false);
            }

            shelveId = result.ChangesetNumber;
            BuildMergeReport.AddSucceededMergeProperty(mergeReport, result.Status);

            return(true);
        }
        internal static ShelveResult TryMergeToShelves(
            IRestApi restApi,
            Branch branch,
            string[] destinationBranches,
            MergeReport mergeReport,
            string taskTitle,
            string botName)
        {
            ShelveResult result = new ShelveResult();

            foreach (string destinationBranch in destinationBranches)
            {
                MergeToResponse mergeToResult = MultilinerMergebotApi.MergeBranchTo(
                    restApi,
                    branch.Repository,
                    branch.FullName,
                    destinationBranch,
                    GetComment(branch.FullName, destinationBranch, taskTitle, botName),
                    MultilinerMergebotApi.MergeToOptions.CreateShelve);

                result.MergeStatusByTargetBranch[destinationBranch] = mergeToResult.Status;

                if (mergeToResult.Status == MergeToResultStatus.MergeNotNeeded)
                {
                    string warningMessage = string.Format(
                        "Branch [{0}] was already merged to [{1}] (No merge needed).",
                        branch.FullName,
                        destinationBranch);

                    result.MergesNotNeededMessages.Add(warningMessage);
                    continue;
                }

                if (IsFailedMergeTo(mergeToResult))
                {
                    string errorMsg = string.Format(
                        "Can't merge branch [{0}] to [{1}]. Reason: {2}.",
                        branch.FullName,
                        destinationBranch,
                        mergeToResult.Message);

                    result.ErrorMessages.Add(errorMsg);

                    BuildMergeReport.AddFailedMergeProperty(
                        mergeReport, mergeToResult.Status, mergeToResult.Message);

                    continue;
                }

                result.ShelvesByTargetBranch[destinationBranch] = mergeToResult.ChangesetNumber;
                BuildMergeReport.AddSucceededMergeProperty(mergeReport, mergeToResult.Status);
            }
            return(result);
        }
Exemple #10
0
        internal static void AddIssueProperty(
            MergeReport mergeReport,
            string issueTitle, string issueLink)
        {
            MergeReport.Entry issueProperty = new MergeReport.Entry();
            issueProperty.Text = issueTitle;
            issueProperty.Link = issueLink;
            issueProperty.Type = ISSUETRACKER_TYPE;

            mergeReport.Properties.Add(issueProperty);
        }
Exemple #11
0
        internal static void AddFailedBuildProperty(
            MergeReport mergeReport, string planBranch, string message)
        {
            MergeReport.Entry failedBuildProperty = new MergeReport.Entry();
            failedBuildProperty.Text = string.Format(
                "build ko (plan: {0})", planBranch);
            failedBuildProperty.Type  = BUILD_FAILED_TYPE;
            failedBuildProperty.Value = message;

            mergeReport.Properties.Add(failedBuildProperty);
        }
Exemple #12
0
            public void ReportMerge(
                string mergebotName, MergeReport mergeReport)
            {
                Uri endpoint = ApiUris.GetFullUri(
                    mBaseUri, ApiEndpoints.MergeReports.ReportMerge,
                    mergebotName);

                string actionDescription = string.Format(
                    "upload merge report of br:{0} (repo ID: {1})",
                    mergeReport.BranchId,
                    mergeReport.RepositoryId);

                Internal.MakeApiRequest <MergeReport>(
                    endpoint, HttpMethod.Put, mergeReport, actionDescription, mApiKey);
            }
Exemple #13
0
        internal static void AddLabelProperty(
            MergeReport mergeReport,
            bool isSuccessfulOperation,
            string labelName,
            string message)
        {
            MergeReport.Entry labelActionProperty = new MergeReport.Entry();
            labelActionProperty.Text = string.Format(
                "label {0} ({1})", isSuccessfulOperation ? "ok" : "ko", labelName);
            labelActionProperty.Value = message;
            labelActionProperty.Type  = isSuccessfulOperation ?
                                        LABEL_OK_TYPE :
                                        LABEL_FAILED_TYPE;

            mergeReport.Properties.Add(labelActionProperty);
        }
Exemple #14
0
        internal static bool TryApplyShelve(
            RestApi restApi,
            Branch branch,
            string destinationBranch,
            MergeReport mergeReport,
            string comment,
            string taskNumber,
            int shelveId,
            TrunkBotConfiguration botConfig,
            string codeReviewsStorageFile,
            out int csetId)
        {
            MergeToResponse mergeResult = TrunkMergebotApi.MergeShelveTo(
                restApi, branch.Repository, shelveId, destinationBranch,
                comment, TrunkMergebotApi.MergeToOptions.EnsureNoDstChanges);

            csetId = mergeResult.ChangesetNumber;
            BuildMergeReport.UpdateMergeProperty(mergeReport, mergeResult.Status, csetId);

            if (mergeResult.Status == MergeToResultStatus.OK)
            {
                return(true);
            }

            if (mergeResult.Status == MergeToResultStatus.DestinationChanges)
            {
                // it should checkin the shelve only on the exact parent shelve cset.
                // if there are new changes in the trunk branch enqueue againg the task
                return(false);
            }

            ChangeTaskStatus.SetTaskAsFailed(
                restApi,
                branch,
                taskNumber,
                string.Format(
                    "Can't merge branch {0}. Reason: {1}",
                    branch.FullName,
                    mergeResult.Message),
                botConfig,
                codeReviewsStorageFile);

            return(false);
        }
Exemple #15
0
        internal static void UpdateMergeProperty(
            MergeReport mergeReport, MergeToResultStatus status, int csetId)
        {
            MergeReport.Entry mergeProperty = FindPropertyByType(
                mergeReport.Properties, MERGE_OK_TYPE);

            if (mergeProperty == null)
            {
                return;
            }

            if (csetId == -1)
            {
                mergeProperty.Text = GetMergeToResultStatus(status);
                mergeProperty.Type = MERGE_FAILED_TYPE;
                return;
            }

            mergeProperty.Value = csetId.ToString();
        }
Exemple #16
0
        internal static void SetUnexpectedExceptionProperty(
            MergeReport mergeReport, string message)
        {
            if (mergeReport == null)
            {
                return;
            }

            MergeReport.Entry failedBuildProperty = FindPropertyByType(
                mergeReport.Properties, BUILD_FAILED_TYPE);

            if (failedBuildProperty == null)
            {
                failedBuildProperty      = new MergeReport.Entry();
                failedBuildProperty.Type = BUILD_FAILED_TYPE;
                mergeReport.Properties.Add(failedBuildProperty);
            }

            failedBuildProperty.Text  = "build ko (unexpected exception)";
            failedBuildProperty.Value = message;
        }
Exemple #17
0
        static bool TryRunAfterCheckinPlan(
            RestApi restApi,
            Branch branch,
            MergeReport mergeReport,
            string taskNumber,
            int csetId,
            string labelName,
            TrunkBotConfiguration botConfig)
        {
            string repSpec           = string.Format("{0}@{1}", branch.Repository, botConfig.Server);
            string scmSpecToSwitchTo = string.Format("cs:{0}@{1}", csetId, repSpec);

            string comment = string.Format(
                "Running plan after merging branch {0}", branch.FullName);

            BuildProperties properties = CreateBuildProperties(
                restApi,
                taskNumber,
                branch.FullName,
                labelName,
                BuildProperties.StageValues.POST_CHECKIN,
                botConfig);

            int iniTime = Environment.TickCount;

            TrunkMergebotApi.CI.PlanResult buildResult = TrunkMergebotApi.CI.Build(
                restApi, botConfig.CI.Plug, botConfig.CI.PlanAfterCheckin,
                scmSpecToSwitchTo, comment, properties);

            BuildMergeReport.AddBuildTimeProperty(mergeReport,
                                                  Environment.TickCount - iniTime);

            string message = string.Empty;

            //TODO:shall we set any attr in trunk branch?
            if (buildResult.Succeeded)
            {
                BuildMergeReport.AddSucceededBuildProperty(
                    mergeReport, botConfig.CI.PlanAfterCheckin);

                message = string.Format(
                    "Plan execution after merging branch {0} was successful.",
                    branch.FullName);

                Notifier.NotifyTaskStatus(
                    restApi,
                    branch.Owner,
                    message,
                    botConfig.Notifications);
                return(true);
            }

            BuildMergeReport.AddFailedBuildProperty(
                mergeReport, botConfig.CI.PlanAfterCheckin, buildResult.Explanation);

            message = string.Format(
                "Plan execution failed after merging branch {0}.\nReason: {1}",
                branch.FullName,
                buildResult.Explanation);

            Notifier.NotifyTaskStatus(
                restApi, branch.Owner, message, botConfig.Notifications);

            return(false);
        }
Exemple #18
0
        static bool CreateLabel(
            RestApi restApi,
            int csetId,
            string branchFullName,
            string trunkBranchName,
            string repository,
            bool isAutoLabelEnabled,
            string automaticLabelPattern,
            MergeReport mergeReport,
            string branchOwner,
            TrunkBotConfiguration.Notifier notificationsConfig,
            out string labelCreated)
        {
            labelCreated = string.Empty;

            if (!isAutoLabelEnabled)
            {
                return(true);
            }

            if (string.IsNullOrEmpty(automaticLabelPattern))
            {
                return(true);
            }

            AutomaticLabeler.Result result = null;
            try
            {
                result = AutomaticLabeler.CreateLabel(
                    restApi, csetId, repository, automaticLabelPattern, DateTime.Now);
            }
            catch (Exception e)
            {
                mLog.ErrorFormat(
                    "An error occurred labeling the merged branch {0} in changeset {1}@{2}: {3}",
                    branchFullName,
                    csetId,
                    repository,
                    e.Message);

                if (result == null)
                {
                    result = new AutomaticLabeler.Result(false, string.Empty, e.Message);
                }
            }

            labelCreated = result.Name;

            BuildMergeReport.AddLabelProperty(
                mergeReport, result.IsSuccessful, result.Name, result.ErrorMessage);

            string message = result.IsSuccessful ?
                             string.Format(
                "Label {0} created successfully in {1} branch, changeset cs:{2}@{3}",
                labelCreated, trunkBranchName, csetId, repository) :
                             string.Format(
                "Failed to create label after merging branch {0} " +
                "in {1} branch, changeset cs:{2}@{3}. Error: {4}",
                branchFullName, trunkBranchName, csetId, repository, result.ErrorMessage);

            Notifier.NotifyTaskStatus(restApi, branchOwner, message, notificationsConfig);
            return(result.IsSuccessful);
        }
Exemple #19
0
        internal static Result TryProcessBranch(
            RestApi restApi,
            Branch branch,
            TrunkBotConfiguration botConfig,
            string botName,
            string codeReviewsStorageFile)
        {
            int shelveId = -1;

            string      taskNumber  = null;
            MergeReport mergeReport = 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);
                }

                if (!IsMergeAllowed(restApi, branch, botConfig.TrunkBranch))
                {
                    mLog.WarnFormat(
                        "Branch {0} is not yet ready to be merged. " +
                        "Jumping to next branch in the queue...",
                        branch.FullName);

                    return(Result.NotReady);
                }

                mLog.InfoFormat("Building the merge report of task {0} ...", taskNumber);
                mergeReport = BuildMergeReport.Build(TrunkMergebotApi.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);
                }

                string comment = GetComment(branch.FullName, taskTittle, botName);

                mLog.InfoFormat("Trying to shelve server-side-merge from {0} to {1}",
                                branch.FullName, botConfig.TrunkBranch);

                if (!MergeToOperations.TryMergeToShelve(
                        restApi, branch, botConfig.TrunkBranch, mergeReport,
                        comment, taskNumber, botConfig, codeReviewsStorageFile,
                        out shelveId))
                {
                    return(Result.Failed);
                }

                mLog.InfoFormat("Testing branch {0} ...", branch.FullName);
                if (!TryBuildTask(restApi, branch, mergeReport,
                                  taskNumber, shelveId, botConfig, codeReviewsStorageFile))
                {
                    return(Result.Failed);
                }

                mLog.InfoFormat("Checking-in shelved merged {0} from {1} to {2}",
                                shelveId, branch.FullName, botConfig.TrunkBranch);

                int csetId = -1;
                if (!MergeToOperations.TryApplyShelve(
                        restApi, branch, botConfig.TrunkBranch, mergeReport,
                        comment, taskNumber, shelveId, botConfig, codeReviewsStorageFile,
                        out csetId))
                {
                    return(Result.Failed);
                }

                mLog.InfoFormat("Checkin: Created changeset {0} in branch {1}",
                                csetId, botConfig.TrunkBranch);

                mLog.InfoFormat("Setting branch {0} as 'integrated'...", branch.FullName);
                ChangeTaskStatus.SetTaskAsMerged(
                    restApi,
                    branch,
                    taskNumber,
                    string.Format(
                        "Branch {0} was correctly merged to {1}.",
                        branch.FullName,
                        botConfig.TrunkBranch),
                    botConfig,
                    codeReviewsStorageFile);

                string labelName = string.Empty;
                if (!CreateLabel(
                        restApi,
                        csetId,
                        branch.FullName,
                        botConfig.TrunkBranch,
                        botConfig.Repository,
                        botConfig.Plastic.IsAutoLabelEnabled,
                        botConfig.Plastic.AutomaticLabelPattern,
                        mergeReport,
                        branch.Owner,
                        botConfig.Notifications,
                        out labelName))
                {
                    return(Result.Failed);
                }

                if (!HasToRunPlanAfterTaskMerged(botConfig.CI))
                {
                    return(Result.Ok);
                }

                if (!TryRunAfterCheckinPlan(
                        restApi,
                        branch,
                        mergeReport,
                        taskNumber,
                        csetId,
                        labelName,
                        botConfig))
                {
                    return(Result.Failed);
                }
            }
            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,
                    string.Format(
                        "Can't process branch {0} because of an unexpected error: {1}.",
                        branch.FullName,
                        ex.Message),
                    botConfig,
                    codeReviewsStorageFile);

                BuildMergeReport.SetUnexpectedExceptionProperty(mergeReport, ex.Message);

                return(Result.Failed);
            }
            finally
            {
                ReportMerge(restApi, branch.Repository, branch.FullName, botName, mergeReport);

                SafeDeleteShelve(restApi, branch.Repository, shelveId);
            }

            return(Result.Ok);
        }
Exemple #20
0
        static bool TryBuildTask(
            RestApi restApi,
            Branch branch,
            MergeReport mergeReport,
            string taskNumber,
            int shelveId,
            TrunkBotConfiguration botConfig,
            string codeReviewsStorageFile)
        {
            if (botConfig.CI == null)
            {
                string message =
                    "No CI plug was set for this mergebot. Therefore, no " +
                    "build actions for task " + taskNumber + " will be performed.";

                mLog.Info(message);

                Notifier.NotifyTaskStatus(
                    restApi, branch.Owner, message, botConfig.Notifications);

                return(true);
            }

            ChangeTaskStatus.SetTaskAsTesting(restApi, branch, taskNumber, string.Format(
                                                  "Starting to test branch {0}.", branch.FullName), botConfig);

            string repSpec           = string.Format("{0}@{1}", branch.Repository, botConfig.Server);
            string scmSpecToSwitchTo = string.Format("sh:{0}@{1}", shelveId, repSpec);

            string comment = string.Format(
                "Building branch {0}", branch.FullName);

            BuildProperties properties = CreateBuildProperties(
                restApi,
                taskNumber,
                branch.FullName,
                string.Empty,
                BuildProperties.StageValues.PRE_CHECKIN,
                botConfig);

            int iniTime = Environment.TickCount;

            TrunkMergebotApi.CI.PlanResult buildResult = TrunkMergebotApi.CI.Build(
                restApi, botConfig.CI.Plug, botConfig.CI.PlanBranch,
                scmSpecToSwitchTo, comment, properties);

            BuildMergeReport.AddBuildTimeProperty(mergeReport,
                                                  Environment.TickCount - iniTime);

            if (buildResult.Succeeded)
            {
                BuildMergeReport.AddSucceededBuildProperty(mergeReport, botConfig.CI.PlanBranch);

                return(true);
            }

            BuildMergeReport.AddFailedBuildProperty(mergeReport,
                                                    botConfig.CI.PlanBranch, buildResult.Explanation);

            ChangeTaskStatus.SetTaskAsFailed(
                restApi,
                branch,
                taskNumber,
                string.Format(
                    "Branch {0} build failed. \nReason: {1}",
                    branch.FullName,
                    buildResult.Explanation),
                botConfig,
                codeReviewsStorageFile);

            return(false);
        }
Exemple #21
0
 internal static void ReportMerge(RestApi restApi, string mergebotName, MergeReport mergeReport)
 {
     restApi.MergeReports.ReportMerge(mergebotName, mergeReport);
 }
Exemple #22
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);
        }
        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);
        }
        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 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);
            }
            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);
            }