示例#1
0
        static BuildProperties CreateBuildProperties(
            RestApi restApi,
            string taskNumber,
            string branchName,
            string labelName,
            string buildStagePreCiOrPostCi,
            TrunkBotConfiguration botConfig)
        {
            int branchHeadChangesetId = TrunkMergebotApi.GetBranchHead(
                restApi, botConfig.Repository, branchName);
            ChangesetModel branchHeadChangeset = TrunkMergebotApi.GetChangeset(
                restApi, botConfig.Repository, branchHeadChangesetId);

            int trunkHeadChangesetId = TrunkMergebotApi.GetBranchHead(
                restApi, botConfig.Repository, botConfig.TrunkBranch);
            ChangesetModel trunkHeadChangeset = TrunkMergebotApi.GetChangeset(
                restApi, botConfig.Repository, trunkHeadChangesetId);

            return(new BuildProperties
            {
                BranchName = branchName,
                TaskNumber = taskNumber,
                BranchHead = branchHeadChangeset.ChangesetId.ToString(),
                BranchHeadGuid = branchHeadChangeset.Guid.ToString(),
                ChangesetOwner = branchHeadChangeset.Owner,
                TrunkHead = trunkHeadChangeset.ChangesetId.ToString(),
                TrunkHeadGuid = trunkHeadChangeset.Guid.ToString(),
                RepSpec = string.Format("{0}@{1}", botConfig.Repository, botConfig.Server),
                LabelName = labelName,
                Stage = buildStagePreCiOrPostCi
            });
        }
示例#2
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 TrunkMergebot(
            string restApiUrl,
            TrunkBotConfiguration trunkBotConfig,
            string branchesQueueFilePath,
            string codeReviewsTrackedFilePath,
            string botName)
        {
            mTrunkBotConfig             = trunkBotConfig;
            mBranchesQueueFilePath      = branchesQueueFilePath;
            mCodeReviewsTrackedFilePath = codeReviewsTrackedFilePath;
            mBotName = botName;

            mRestApi = new RestApi(restApiUrl, mTrunkBotConfig.UserApiKey);
        }
示例#4
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);
        }
        internal static void SetTaskAsMerged(
            RestApi restApi,
            Branch branch,
            string taskNumber,
            string message,
            TrunkBotConfiguration botConfig,
            string codeReviewsStorageFile)
        {
            try
            {
                if (botConfig.Plastic.IsApprovedCodeReviewFilterEnabled)
                {
                    ReviewsStorage.DeleteBranchReviews(
                        branch.Repository, branch.Id, codeReviewsStorageFile);
                }

                TrunkMergebotApi.ChangeBranchAttribute(
                    restApi, branch.Repository, branch.FullName,
                    botConfig.Plastic.StatusAttribute.Name,
                    botConfig.Plastic.StatusAttribute.MergedValue);

                if (taskNumber != null && botConfig.Issues != null)
                {
                    TrunkMergebotApi.Issues.SetIssueField(
                        restApi, botConfig.Issues.Plug, botConfig.Issues.ProjectKey,
                        taskNumber, botConfig.Issues.StatusField.Name,
                        botConfig.Issues.StatusField.MergedValue);
                }

                Notifier.NotifyTaskStatus(
                    restApi, branch.Owner, message,
                    botConfig.Notifications);
            }
            catch (Exception ex)
            {
                Notifier.NotifyException(
                    restApi, branch, message,
                    "merged", ex, botConfig.Notifications);
            }
        }
        internal static void SetTaskAsTesting(
            RestApi restApi,
            Branch branch,
            string taskNumber,
            string message,
            TrunkBotConfiguration botConfig)
        {
            try
            {
                if (!string.IsNullOrEmpty(botConfig.Plastic.StatusAttribute.TestingValue))
                {
                    TrunkMergebotApi.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))
                {
                    TrunkMergebotApi.Issues.SetIssueField(
                        restApi, botConfig.Issues.Plug, botConfig.Issues.ProjectKey,
                        taskNumber, botConfig.Issues.StatusField.Name,
                        botConfig.Issues.StatusField.TestingValue);
                }

                Notifier.NotifyTaskStatus(
                    restApi, branch.Owner, message,
                    botConfig.Notifications);
            }
            catch (Exception ex)
            {
                Notifier.NotifyException(
                    restApi, branch, message,
                    "testing", ex, botConfig.Notifications);
            }
        }
示例#7
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);
        }
示例#8
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);
        }
示例#9
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);
        }
示例#10
0
        static int Main(string[] args)
        {
            string botName = null;

            try
            {
                TrunkBotArguments botArgs = new TrunkBotArguments(args);

                bool bValidArgs = botArgs.Parse();
                botName = botArgs.BotName;

                ConfigureLogging(botName);

                mLog.InfoFormat("TrunkBot [{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(
                            "TrunkBot [{0}] is going to finish: " +
                            "user explicitly requested to show usage.",
                            botName);
                        return(0);
                    }

                    mLog.ErrorFormat(
                        "TrunkBot [{0}] is going to finish: " +
                        "invalid arguments found in command line.",
                        botName);
                    return(0);
                }

                string errorMessage = null;
                if (!TrunkBotArgumentsChecker.CheckArguments(
                        botArgs, out errorMessage))
                {
                    Console.WriteLine(errorMessage);
                    mLog.ErrorFormat(
                        "TrunkBot [{0}] is going to finish: error found on argument check",
                        botName);
                    mLog.Error(errorMessage);
                    return(1);
                }

                TrunkBotConfiguration botConfig = TrunkBotConfiguration.
                                                  BuidFromConfigFile(botArgs.ConfigFilePath);

                errorMessage = null;
                if (!TrunkBotConfigurationChecker.CheckConfiguration(
                        botConfig, out errorMessage))
                {
                    Console.WriteLine(errorMessage);
                    mLog.ErrorFormat(
                        "TrunkBot [{0}] is going to finish: error found on argument check",
                        botName);
                    mLog.Error(errorMessage);
                    return(1);
                }

                ConfigureServicePoint();

                string escapedBotName = GetEscapedBotName(botName);

                LaunchTrunkMergebot(
                    botArgs.WebSocketUrl,
                    botArgs.RestApiUrl,
                    botConfig,
                    ToolConfig.GetBranchesFile(escapedBotName),
                    ToolConfig.GetCodeReviewsFile(escapedBotName),
                    botName,
                    botArgs.ApiKey);

                mLog.InfoFormat(
                    "TrunkBot [{0}] is going to finish: orderly shutdown.",
                    botName);

                return(0);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                mLog.FatalFormat(
                    "TrunkBot [{0}] is going to finish: uncaught exception " +
                    "thrown during execution. Message: {1}", botName, e.Message);
                mLog.DebugFormat("StackTrace: {0}", e.StackTrace);
                return(1);
            }
        }
示例#11
0
        static void LaunchTrunkMergebot(
            string webSocketUrl,
            string restApiUrl,
            TrunkBotConfiguration botConfig,
            string branchesQueueFilePath,
            string codeReviewsTrackedFilePath,
            string botName,
            string apiKey)
        {
            if (!Directory.Exists(Path.GetDirectoryName(branchesQueueFilePath)))
            {
                Directory.CreateDirectory(Path.GetDirectoryName(branchesQueueFilePath));
            }

            TrunkMergebot trunkBot = new TrunkMergebot(
                restApiUrl,
                botConfig,
                branchesQueueFilePath,
                codeReviewsTrackedFilePath,
                botName);

            try
            {
                trunkBot.EnsurePlasticStatusAttributeExists();
            }
            catch (Exception e)
            {
                mLog.FatalFormat(
                    "TrunkBot [{0}] is going to finish because it wasn't able " +
                    "to configure the required plastic status attribute [{1}] for its proper working. " +
                    "Reason: {2}", botName, botConfig.Plastic.StatusAttribute.Name, e.Message);

                mLog.DebugFormat("Stack trace:{0}{1}", Environment.NewLine, e.StackTrace);
                throw;
            }

            try
            {
                trunkBot.LoadBranchesToProcess();
            }
            catch (Exception e)
            {
                mLog.FatalFormat(
                    "TrunkBot [{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(trunkBot.ProcessBranches);

            string[] eventsToSubscribe = GetEventNamesToSuscribe(
                botConfig.Plastic.IsApprovedCodeReviewFilterEnabled,
                botConfig.Plastic.IsBranchAttrFilterEnabled);

            WebSocketClient ws = new WebSocketClient(
                webSocketUrl,
                botName,
                apiKey,
                eventsToSubscribe,
                trunkBot.OnEventReceived);

            ws.ConnectWithRetries();

            Task.Delay(-1).Wait();
        }