예제 #1
0
 /// <summary>
 /// Initializes a new instance of the <see cref="SpiceInstance"/> class.
 /// </summary>
 /// <param name="parameters">The method description.</param>
 /// <param name="state">The biasing simulation state.</param>
 /// <param name="maxOrder">The maximum order.</param>
 protected SpiceInstance(SpiceMethod parameters, IBiasingSimulationState state, int maxOrder)
 {
     Parameters = parameters.ThrowIfNull(nameof(parameters));
     State      = state.ThrowIfNull(nameof(state));
     MaxOrder   = maxOrder;
     States     = new NodeHistory <SpiceIntegrationState>(maxOrder + 2);
 }
예제 #2
0
        public void WhenNodeHasPlacementWithName_ThenZoneAndProjectIdAreNotNull()
        {
            var instance = new InstanceHistory(
                1,
                new InstanceLocator("project-1", "zone-1", "instance-1"),
                InstanceHistoryState.MissingImage,
                null,
                null);
            var node = new NodeHistory(
                "server-1",
                null,
                DateTime.UtcNow.AddDays(-1),
                DateTime.UtcNow,
                0,
                new[]
            {
                new NodePlacement(
                    DateTime.UtcNow.AddDays(-1),
                    DateTime.UtcNow,
                    instance)
            });

            Assert.AreEqual("project-1", node.ProjectId);
            Assert.AreEqual("zone-1", node.Zone);
        }
예제 #3
0
        public void WhenNodeHasPlacementWithoutName_ThenZoneAndProjectIdAreNull()
        {
            var instance = new InstanceHistory(
                1,
                null,
                InstanceHistoryState.MissingName,
                null,
                null);
            var node = new NodeHistory(
                "server-1",
                null,
                DateTime.UtcNow.AddDays(-1),
                DateTime.UtcNow,
                0,
                new[]
            {
                new NodePlacement(
                    DateTime.UtcNow.AddDays(-1),
                    DateTime.UtcNow,
                    instance)
            });

            Assert.IsNull(node.ProjectId);
            Assert.IsNull(node.Zone);
        }
예제 #4
0
        public void WhenNodeHasPlacements_ThenMaxInstancePlacementsByDayUsesFirstAndLastDate()
        {
            var baselineDate = new DateTime(2020, 1, 1, 0, 0, 0, DateTimeKind.Utc);
            var instance     = new InstanceHistory(
                1,
                new InstanceLocator("project-1", "zone-1", "instance-1"),
                InstanceHistoryState.MissingImage,
                null,
                null);
            var node = new NodeHistory(
                "server-1",
                null,
                baselineDate,
                baselineDate.AddDays(2),
                0,
                new[]
            {
                new NodePlacement(
                    baselineDate.AddDays(1),
                    baselineDate.AddDays(2),
                    instance),
                new NodePlacement(
                    baselineDate,
                    baselineDate.AddDays(1),
                    instance)
            });

            var histogram = node.MaxInstancePlacementsByDay;

            Assert.AreEqual(baselineDate, histogram.First().Timestamp);
            Assert.AreEqual(baselineDate.AddDays(2), histogram.Last().Timestamp);
        }
예제 #5
0
        public void WhenNodeHasNoPlacements_ThenMaxInstancePlacementsByDayIsEmpty()
        {
            var node = new NodeHistory(
                "server-1",
                DateTime.UtcNow.AddDays(-1),
                DateTime.UtcNow,
                0,
                Enumerable.Empty <NodePlacement>());

            Assert.IsFalse(node.MaxInstancePlacementsByDay.Any());
        }
예제 #6
0
        public void WhenNodeHasNoPlacements_ThenZoneAndProjectIdAreNull()
        {
            var node = new NodeHistory(
                "server-1",
                DateTime.UtcNow.AddDays(-1),
                DateTime.UtcNow,
                0,
                Enumerable.Empty <NodePlacement>());

            Assert.IsNull(node.ProjectId);
            Assert.IsNull(node.Zone);
        }
        internal override void Repopulate()
        {
            // Get instances, filtered by whatever filter applies,
            // then derive sole tenant nodes (ignoring fleet).
            this.currentNodeSet = NodeSetHistory.FromInstancyHistory(
                this.parent.GetInstancesMatchingCurrentFilters(),
                Tenancies.SoleTenant);

            // Create histogram, disregarding the date selection.
            this.Histogram = this.currentNodeSet.MaxNodesByDay;

            Debug.Assert(this.currentNodeSet.Nodes.All(n => n.ServerId != null));

            // For the list of nodes, apply the date selection.
            this.Nodes.Clear();
            this.Nodes.AddRange(this.currentNodeSet.Nodes
                                .Where(n => n.FirstUse <= this.Selection.EndDate && n.LastUse >= this.Selection.StartDate));

            this.selectedNode = null;
            RepopulateNodePlacements();
        }
        private void AddToHistory(Node previousNode, Node currentCandidateNode)
        {
            if (previousNode == currentCandidateNode)
            {
                return;
            }

            // Add node to dictionary first
            NodeDictionary.Add(previousNode.Id, previousNode);


            if (NodeHistory.ContainsKey(previousNode.Id))
            {
                NodeHistory[previousNode.Id].Add(currentCandidateNode.Id);
            }
            else
            {
                NodeHistory[previousNode.Id] = new List <string>();
            }

            NodeHistory[previousNode.Id].Add(currentCandidateNode.Id);
        }
예제 #9
0
    public override void ExecuteBuild()
    {
        Log("************************* GUBP");

        var HostPlatforms = new List<UnrealTargetPlatform>();
        if (!ParseParam("OnlyMac"))
        {
            HostPlatforms.Add(UnrealTargetPlatform.Win64);
        }

        if (ParseParam("WithMac") || HostPlatforms.Count == 0)
        {
            HostPlatforms.Add(UnrealTargetPlatform.Mac);
        }

        StoreName = ParseParamValue("Store");
        string StoreSuffix = ParseParamValue("StoreSuffix", "");
        CL = ParseParamInt("CL", 0);
        bool bCleanLocalTempStorage = ParseParam("CleanLocal");
        bool bChanges = ParseParam("Changes") || ParseParam("AllChanges");
        bool bHistory = ParseParam("History") || bChanges;
        bool bListOnly = ParseParam("ListOnly") || bHistory;
        bool bSkipTriggers = ParseParam("SkipTriggers");
        bool bFake = ParseParam("fake");
        bool bFakeEC = ParseParam("FakeEC");
        bWide = ParseParam("Wide");
        bool bSaveSharedTempStorage = false;

        if (bHistory && !P4Enabled)
        {
            throw new AutomationException("-Changes and -History require -P4.");
        }
        bool LocalOnly = true;
        string CLString = "";
        if (String.IsNullOrEmpty(StoreName))
        {
            if (P4Enabled)
            {
                if (CL == 0)
                {
                    CL = P4Env.Changelist;
                }
                CLString = String.Format("{0}", CL);
                StoreName = "GUBP-" + P4Env.BuildRootEscaped + "-CL-" + CLString + "-" + StoreSuffix;
                bSaveSharedTempStorage = CommandUtils.IsBuildMachine;
                LocalOnly = false;
            }
            else
            {
                StoreName = "TempLocal" + "-" + StoreSuffix;
                bSaveSharedTempStorage = false;
            }
        }
        if (bFakeEC)
        {
            LocalOnly = true;
        }
        if (bSaveSharedTempStorage)
        {
            if (!HaveSharedTempStorage())
            {
                throw new AutomationException("Request to save to temp storage, but {0} is unavailable.", UE4TempStorageDirectory());
            }
            bSignBuildProducts = true;
        }
        else if (!LocalOnly && !HaveSharedTempStorage())
        {
            Log("Looks like we want to use shared temp storage, but since we don't have it, we won't use it.");
            LocalOnly = false;
        }

        Log("************************* CL:			                    {0}", CL);
        Log("************************* P4Enabled:			            {0}", P4Enabled);
        Log("************************* HostPlatform:		            {0}", HostPlatforms.ToString());
        Log("************************* StoreName:		                {0}", StoreName.ToString());
        Log("************************* bCleanLocalTempStorage:		    {0}", bCleanLocalTempStorage);
        Log("************************* bSkipTriggers:		            {0}", bSkipTriggers);
        Log("************************* bSaveSharedTempStorage:		    {0}", bSaveSharedTempStorage);
        Log("************************* bSignBuildProducts:		        {0}", bSignBuildProducts);
        Log("************************* bFake:           		        {0}", bFake);
        Log("************************* bFakeEC:           		        {0}", bFakeEC);
        Log("************************* bHistory:           		        {0}", bHistory);

        GUBPNodes = new Dictionary<string, GUBPNode>();

        Branch = new BranchInfo(HostPlatforms);

        if (IsBuildMachine || ParseParam("AllPlatforms"))
        {
            ActivePlatforms = new List<UnrealTargetPlatform>();
            foreach (var GameProj in Branch.CodeProjects)
            {
                foreach (var Kind in BranchInfo.MonolithicKinds)
                {
                    if (GameProj.Properties.Targets.ContainsKey(Kind))
                    {
                        var Target = GameProj.Properties.Targets[Kind];
                        foreach (var HostPlatform in HostPlatforms)
                        {
                            var Platforms = Target.Rules.GUBP_GetPlatforms_MonolithicOnly(HostPlatform);
                            foreach (var Plat in Platforms)
                            {
                                if (Target.Rules.SupportsPlatform(Plat) && !ActivePlatforms.Contains(Plat))
                                {
                                    ActivePlatforms.Add(Plat);
                                }
                            }
                        }
                    }
                }
            }
        }
        else
        {
            ActivePlatforms = new List<UnrealTargetPlatform>(CommandUtils.KnownTargetPlatforms);
        }
        foreach (var Plat in ActivePlatforms)
        {
            Log("Active Platform: {0}", Plat.ToString());
        }

        AddNode(new VersionFilesNode());
        foreach (var HostPlatform in HostPlatforms)
        {

            AddNode(new RootEditorNode(HostPlatform));
            AddNode(new ToolsNode(HostPlatform));
            AddNode(new EditorAndToolsNode(HostPlatform));
            AddNode(new NonUnityTestNode(HostPlatform));

            foreach (var Plat in ActivePlatforms)
            {
                if (Plat != HostPlatform && Plat != GetAltHostPlatform(HostPlatform))
                {
                    AddNode(new EditorPlatformNode(HostPlatform, Plat));
                }
            }
            int NumShared = 0;
            foreach (var CodeProj in Branch.CodeProjects)
            {
                var Options = CodeProj.Options(HostPlatform);

                if (Options.bIsPromotable && !Options.bSeparateGamePromotion)
                {
                    NumShared++;
                }

                if (!Options.bIsPromotable && !Options.bTestWithShared)
                {
                    continue; // we skip things that aren't promotable and aren't tested
                }

                AddNode(new EditorGameNode(this, HostPlatform, CodeProj));
                {
                    var EditorTests = CodeProj.Properties.Targets[TargetRules.TargetType.Editor].Rules.GUBP_GetEditorTests_EditorTypeOnly();
                    var EditorTestNodes = new List<string>();
                    foreach (var Test in EditorTests)
                    {
                        EditorTestNodes.Add(AddNode(new UATTestNode(this, HostPlatform, CodeProj, Test.Key, Test.Value)));
                    }
                    if (EditorTestNodes.Count > 0)
                    {
                        AddNode(new GameAggregateNode(this, HostPlatform, CodeProj, "AllEditorTests", EditorTestNodes, true, 0.0f));
                    }
                }

                var ServerPlatforms = new List<UnrealTargetPlatform>();
                var GamePlatforms = new List<UnrealTargetPlatform>();
                var GameTestNodes = new List<string>();
                foreach (var Kind in BranchInfo.MonolithicKinds)
                {
                    if (CodeProj.Properties.Targets.ContainsKey(Kind))
                    {
                        var Target = CodeProj.Properties.Targets[Kind];
                        var Platforms = Target.Rules.GUBP_GetPlatforms_MonolithicOnly(HostPlatform);
                        foreach (var Plat in Platforms)
                        {
                            if (ActivePlatforms.Contains(Plat))
                            {
                                if (Kind == TargetRules.TargetType.Server && !ServerPlatforms.Contains(Plat))
                                {
                                    ServerPlatforms.Add(Plat);
                                }
                                if (Kind == TargetRules.TargetType.Game && !GamePlatforms.Contains(Plat))
                                {
                                    GamePlatforms.Add(Plat);
                                }
                                if (!GUBPNodes.ContainsKey(GamePlatformMonolithicsNode.StaticGetFullName(HostPlatform, CodeProj, Plat)))
                                {
                                    AddNode(new GamePlatformMonolithicsNode(this, HostPlatform, CodeProj, Plat));
                                }
                                string CookedPlatform = Platform.Platforms[Plat].GetCookPlatform(Kind == TargetRules.TargetType.Server, Kind == TargetRules.TargetType.Client, "");
                                if (!GUBPNodes.ContainsKey(CookNode.StaticGetFullName(HostPlatform, CodeProj, CookedPlatform)))
                                {
                                    AddNode(new CookNode(this, HostPlatform, CodeProj, Plat, CookedPlatform));
                                }
                                if (!GUBPNodes.ContainsKey(GamePlatformCookedAndCompiledNode.StaticGetFullName(HostPlatform, CodeProj, Plat)))
                                {
                                    AddNode(new GamePlatformCookedAndCompiledNode(this, HostPlatform, CodeProj, Plat, true));
                                }
                                var GameTests = Target.Rules.GUBP_GetGameTests_MonolithicOnly(HostPlatform, GetAltHostPlatform(HostPlatform), Plat);
                                var RequiredPlatforms = new List<UnrealTargetPlatform> { Plat };
                                var ThisMonoGameTestNodes = new List<string>();
                                foreach (var Test in GameTests)
                                {
                                    ThisMonoGameTestNodes.Add(AddNode(new UATTestNode(this, HostPlatform, CodeProj, Test.Key + "_" + Plat.ToString(), Test.Value, false, RequiredPlatforms)));
                                }
                                if (ThisMonoGameTestNodes.Count > 0)
                                {
                                    GameTestNodes.Add(AddNode(new GameAggregateNode(this, HostPlatform, CodeProj, "CookedTests_" + Plat.ToString() + "_" + Kind.ToString(), ThisMonoGameTestNodes, true, 0.0f)));
                                }
                            }
                        }
                    }
                }
                foreach (var ServerPlatform in ServerPlatforms)
                {
                    foreach (var GamePlatform in GamePlatforms)
                    {
                        var Target = CodeProj.Properties.Targets[TargetRules.TargetType.Game];
                        var ClientServerTests = Target.Rules.GUBP_GetClientServerTests_MonolithicOnly(HostPlatform, GetAltHostPlatform(HostPlatform), ServerPlatform, GamePlatform);
                        var RequiredPlatforms = new List<UnrealTargetPlatform> { ServerPlatform };
                        if (ServerPlatform != GamePlatform)
                        {
                            RequiredPlatforms.Add(GamePlatform);
                        }
                        foreach (var Test in ClientServerTests)
                        {
                            GameTestNodes.Add(AddNode(new UATTestNode(this, HostPlatform, CodeProj, Test.Key + "_" + GamePlatform.ToString() + "_" + ServerPlatform.ToString(), Test.Value, false, RequiredPlatforms, true)));
                        }
                    }
                }
                if (GameTestNodes.Count > 0)
                {
                    AddNode(new GameAggregateNode(this, HostPlatform, CodeProj, "AllCookedTests", GameTestNodes, false));
                }
            }

            bool DoASharedPromotable = false;

            {
                var Options = Branch.BaseEngineProject.Options(HostPlatform);

                if (!Options.bIsPromotable || Options.bSeparateGamePromotion)
                {
                    if (NumShared > 0)
                    {
                        throw new AutomationException("We assume that if we have shared promotable, the base engine is in it. Some games are marked as shared and promotable, but the base engine is not.");
                    }
                }
                else
                {
                    DoASharedPromotable = true;
                }
            }

            if (DoASharedPromotable)
            {
                Dictionary<string, List<UnrealTargetPlatform>> NonCodeProjectNames;
                {
                    var Target = Branch.BaseEngineProject.Properties.Targets[TargetRules.TargetType.Editor];
                    NonCodeProjectNames = Target.Rules.GUBP_NonCodeProjects_BaseEditorTypeOnly(HostPlatform);

                    var Options = Branch.BaseEngineProject.Options(HostPlatform);

                    if (!Options.bIsPromotable || Options.bSeparateGamePromotion)
                    {
                        throw new AutomationException("We assume that if we have shared promotable, the base engine is in it.");
                    }
                }
                {
                    var EditorTests = Branch.BaseEngineProject.Properties.Targets[TargetRules.TargetType.Editor].Rules.GUBP_GetEditorTests_EditorTypeOnly();
                    var EditorTestNodes = new List<string>();
                    foreach (var Test in EditorTests)
                    {
                        EditorTestNodes.Add(AddNode(new UATTestNode(this, HostPlatform, Branch.BaseEngineProject, Test.Key, Test.Value)));
                        foreach (var NonCodeProject in Branch.NonCodeProjects)
                        {
                            if (!NonCodeProjectNames.ContainsKey(NonCodeProject.GameName))
                            {
                                continue;
                            }
                            EditorTestNodes.Add(AddNode(new UATTestNode(this, HostPlatform, NonCodeProject, Test.Key, Test.Value)));
                        }
                    }
                    if (EditorTestNodes.Count > 0)
                    {
                        AddNode(new GameAggregateNode(this, HostPlatform, Branch.BaseEngineProject, "AllEditorTests", EditorTestNodes, true, 0.0f));
                    }
                }

                var ServerPlatforms = new List<UnrealTargetPlatform>();
                var GamePlatforms = new List<UnrealTargetPlatform>();

                foreach (var Kind in BranchInfo.MonolithicKinds)
                {
                    if (Branch.BaseEngineProject.Properties.Targets.ContainsKey(Kind))
                    {
                        var Target = Branch.BaseEngineProject.Properties.Targets[Kind];
                        var Platforms = Target.Rules.GUBP_GetPlatforms_MonolithicOnly(HostPlatform);
                        foreach (var Plat in Platforms)
                        {
                            if (ActivePlatforms.Contains(Plat))
                            {
                                if (Kind == TargetRules.TargetType.Server && !ServerPlatforms.Contains(Plat))
                                {
                                    ServerPlatforms.Add(Plat);
                                }
                                if (Kind == TargetRules.TargetType.Game && !GamePlatforms.Contains(Plat))
                                {
                                    GamePlatforms.Add(Plat);
                                }
                                if (!GUBPNodes.ContainsKey(GamePlatformMonolithicsNode.StaticGetFullName(HostPlatform, Branch.BaseEngineProject, Plat)))
                                {
                                    AddNode(new GamePlatformMonolithicsNode(this, HostPlatform, Branch.BaseEngineProject, Plat));
                                }
                            }
                        }
                    }
                }

                var GameTestNodes = new List<string>();
                var GameCookNodes = new List<string>();

                //foreach (var Kind in BranchInfo.MonolithicKinds)//for now, non-code projects don't do client or server.
                {
                    var Kind = TargetRules.TargetType.Game;
                    if (Branch.BaseEngineProject.Properties.Targets.ContainsKey(Kind))
                    {
                        var Target = Branch.BaseEngineProject.Properties.Targets[Kind];
                        var Platforms = Target.Rules.GUBP_GetPlatforms_MonolithicOnly(HostPlatform);
                        foreach (var Plat in Platforms)
                        {
                            if (ActivePlatforms.Contains(Plat))
                            {
                                foreach (var NonCodeProject in Branch.NonCodeProjects)
                                {
                                    if (!NonCodeProjectNames.ContainsKey(NonCodeProject.GameName) || !NonCodeProjectNames[NonCodeProject.GameName].Contains(Plat))
                                    {
                                        continue;
                                    }

                                    string CookedPlatform = Platform.Platforms[Plat].GetCookPlatform(Kind == TargetRules.TargetType.Server, Kind == TargetRules.TargetType.Client, "");
                                    if (!GUBPNodes.ContainsKey(CookNode.StaticGetFullName(HostPlatform, NonCodeProject, CookedPlatform)))
                                    {
                                        GameCookNodes.Add(AddNode(new CookNode(this, HostPlatform, NonCodeProject, Plat, CookedPlatform)));
                                    }
                                    if (!GUBPNodes.ContainsKey(GamePlatformCookedAndCompiledNode.StaticGetFullName(HostPlatform, NonCodeProject, Plat)))
                                    {
                                        AddNode(new GamePlatformCookedAndCompiledNode(this, HostPlatform, NonCodeProject, Plat, false));
                                    }
                                    var GameTests = Target.Rules.GUBP_GetGameTests_MonolithicOnly(HostPlatform, GetAltHostPlatform(HostPlatform), Plat);
                                    var RequiredPlatforms = new List<UnrealTargetPlatform> { Plat };
                                    var ThisMonoGameTestNodes = new List<string>();
                                    foreach (var Test in GameTests)
                                    {
                                        ThisMonoGameTestNodes.Add(AddNode(new UATTestNode(this, HostPlatform, NonCodeProject, Test.Key + "_" + Plat.ToString(), Test.Value, false, RequiredPlatforms)));
                                    }
                                    if (ThisMonoGameTestNodes.Count > 0)
                                    {
                                        GameTestNodes.Add(AddNode(new GameAggregateNode(this, HostPlatform, NonCodeProject, "CookedTests_" + Plat.ToString() + "_" + Kind.ToString(), ThisMonoGameTestNodes, true, 0.0f)));
                                    }
                                }
                            }
                        }
                    }
                }
        #if false
                //for now, non-code projects don't do client or server.
                foreach (var ServerPlatform in ServerPlatforms)
                {
                    var ServerTarget = Branch.BaseEngineProject.Properties.Targets[TargetRules.TargetType.Server];
                    foreach (var GamePlatform in GamePlatforms)
                    {
                        var Target = Branch.BaseEngineProject.Properties.Targets[TargetRules.TargetType.Game];

                        foreach (var NonCodeProject in Branch.NonCodeProjects)
                        {
                            if (!NonCodeProjectNames.ContainsKey(NonCodeProject.GameName) || !NonCodeProjectNames.ContainsKey(NonCodeProject.GameName) ||
                                 !NonCodeProjectNames[NonCodeProject.GameName].Contains(ServerPlatform)  || !NonCodeProjectNames[NonCodeProject.GameName].Contains(GamePlatform) )
                            {
                                continue;
                            }

                            var ClientServerTests = Target.Rules.GUBP_GetClientServerTests_MonolithicOnly(HostPlatform, GetAltHostPlatform(HostPlatform), ServerPlatform, GamePlatform);
                            var RequiredPlatforms = new List<UnrealTargetPlatform> { ServerPlatform };
                            if (ServerPlatform != GamePlatform)
                            {
                                RequiredPlatforms.Add(GamePlatform);
                            }
                            foreach (var Test in ClientServerTests)
                            {
                                GameTestNodes.Add(AddNode(new UATTestNode(this, HostPlatform, NonCodeProject, Test.Key + "_" + GamePlatform.ToString() + "_" + ServerPlatform.ToString(), Test.Value, false, RequiredPlatforms, true)));
                            }
                        }
                    }
                }
        #endif
                if (GameTestNodes.Count > 0)
                {
                    AddNode(new GameAggregateNode(this, HostPlatform, Branch.BaseEngineProject, "AllCookedTests", GameTestNodes, false));
                }
            }
            AddCustomNodes(HostPlatform);
        }

        int NumSharedAllHosts = 0;
        foreach (var CodeProj in Branch.CodeProjects)
        {
            if (CodeProj.Properties.Targets.ContainsKey(TargetRules.TargetType.Editor))
            {
                var Target = CodeProj.Properties.Targets[TargetRules.TargetType.Editor];
                bool AnySeparate = false;
                var PromotedHosts = new List<UnrealTargetPlatform>();

                foreach (var HostPlatform in HostPlatforms)
                {
                    var Options = CodeProj.Options(HostPlatform);
                    AnySeparate = AnySeparate || Options.bSeparateGamePromotion;
                    if (Options.bIsPromotable)
                    {
                        if (!Options.bSeparateGamePromotion)
                        {
                            NumSharedAllHosts++;
                        }
                        PromotedHosts.Add(HostPlatform);
                    }
                }
                if (PromotedHosts.Count > 0)
                {
                    AddNode(new GameAggregatePromotableNode(this, PromotedHosts, CodeProj));
                    if (AnySeparate)
                    {
                        AddNode(new WaitForGamePromotionUserInput(this, CodeProj, false));
                        AddNode(new GameLabelPromotableNode(this, CodeProj, false));
                        AddNode(new WaitForGamePromotionUserInput(this, CodeProj, true));
                        AddNode(new GameLabelPromotableNode(this, CodeProj, true));
                    }
                }
            }
        }
        if (NumSharedAllHosts > 0)
        {
            AddNode(new GameAggregatePromotableNode(this, HostPlatforms, Branch.BaseEngineProject));

            AddNode(new SharedAggregatePromotableNode(this, HostPlatforms));
            AddNode(new WaitForSharedPromotionUserInput(this, false));
            AddNode(new SharedLabelPromotableNode(this, false));
            AddNode(new WaitForTestShared(this));
            AddNode(new WaitForSharedPromotionUserInput(this, true));
            AddNode(new SharedLabelPromotableNode(this, true));
        }

        var NodesToDo = new HashSet<string>();

        if (bCleanLocalTempStorage)  // shared temp storage can never be wiped
        {
            DeleteLocalTempStorageManifests(CmdEnv);
        }
        Log("******* Caching completion");
        GUBPNodesCompleted = new Dictionary<string, bool>();
        GUBPNodesControllingTrigger = new Dictionary<string, string>();
        GUBPNodesControllingTriggerDotName = new Dictionary<string, string>();
        GUBPNodesHistory = new Dictionary<string, NodeHistory>();

        foreach (var Node in GUBPNodes)
        {
            Log("** {0}", Node.Key);
            NodeIsAlreadyComplete(Node.Key, LocalOnly); // cache these now to avoid spam later
            GetControllingTriggerDotName(Node.Key);
            if (CLString != "" && StoreName.Contains(CLString))
            {
                if (GUBPNodes[Node.Key].RunInEC() && !GUBPNodes[Node.Key].TriggerNode())
                {
                    string GameNameIfAny = GUBPNodes[Node.Key].GameNameIfAnyForTempStorage();
                    string NodeStoreWildCard = StoreName.Replace(CLString, "*") + "-" + GUBPNodes[Node.Key].GetFullName();
                    var History = new NodeHistory();

                    History.AllStarted = ConvertCLToIntList(FindTempStorageManifests(CmdEnv, NodeStoreWildCard + StartedTempStorageSuffix, false, true, GameNameIfAny));
                    History.AllSucceeded = ConvertCLToIntList(FindTempStorageManifests(CmdEnv, NodeStoreWildCard + SucceededTempStorageSuffix, false, true, GameNameIfAny));
                    History.AllFailed = ConvertCLToIntList(FindTempStorageManifests(CmdEnv, NodeStoreWildCard + FailedTempStorageSuffix, false, true, GameNameIfAny));

                    if (History.AllSucceeded.Count > 0)
                    {
                        History.LastSucceeded = History.AllSucceeded[History.AllSucceeded.Count - 1];
                        foreach (var Failed in History.AllFailed)
                        {
                            if (Failed > History.LastSucceeded)
                            {
                                History.Failed.Add(Failed);
                                History.FailedString = GUBPNode.MergeSpaceStrings(History.FailedString, String.Format("{0}", Failed));
                            }
                        }
                        foreach (var Started in History.AllStarted)
                        {
                            if (Started > History.LastSucceeded && !History.Failed.Contains(Started))
                            {
                                History.InProgress.Add(Started);
                                History.InProgressString = GUBPNode.MergeSpaceStrings(History.InProgressString, String.Format("{0}", Started));
                            }
                        }
                        GUBPNodesHistory.Add(Node.Key, History);
                    }
                }
            }
        }
        Log("******* {0} GUBP Nodes", GUBPNodes.Count);
        foreach (var Node in GUBPNodes)
        {
            Log("  {0}", Node.Key);
        }
        bool bOnlyNode = false;
        string PreconditionSuffix = "_PreconditionOnly";
        {
            string NodeSpec = ParseParamValue("Node");
            if (String.IsNullOrEmpty(NodeSpec))
            {
                NodeSpec = ParseParamValue("OnlyNode");
                bOnlyNode = true;
            }
            if (!String.IsNullOrEmpty(NodeSpec))
            {
                if (NodeSpec.Equals("Noop", StringComparison.InvariantCultureIgnoreCase) || NodeSpec.EndsWith(PreconditionSuffix, StringComparison.InvariantCultureIgnoreCase))
                {
                    Log("Request for Noop node, done.");
                    PrintRunTime();
                    return;
                }
                List<string> Nodes = new List<string>(NodeSpec.Split('+'));
                foreach (var NodeArg in Nodes)
                {
                    var NodeName = NodeArg.Trim();
                    if (!String.IsNullOrEmpty(NodeName))
                    {
                        foreach (var Node in GUBPNodes)
                        {
                            if (Node.Value.GetFullName().Equals(NodeArg, StringComparison.InvariantCultureIgnoreCase))
                            {
                                NodesToDo.Add(Node.Key);
                                NodeName = null;
                                break;
                            }
                        }
                        if (NodeName != null)
                        {
                            throw new AutomationException("Could not find node named {0}", NodeName);
                        }
                    }
                }
            }
        }
        string GameSpec = ParseParamValue("Game");
        if (!String.IsNullOrEmpty(GameSpec))
        {
            List<string> Games = new List<string>(GameSpec.Split('+'));
            foreach (var GameArg in Games)
            {
                var GameName = GameArg.Trim();
                if (!String.IsNullOrEmpty(GameName))
                {
                    foreach (var GameProj in Branch.CodeProjects)
                    {
                        if (GameProj.GameName.Equals(GameName, StringComparison.InvariantCultureIgnoreCase))
                        {

                            NodesToDo.Add(GameAggregatePromotableNode.StaticGetFullName(GameProj));
                            foreach (var Node in GUBPNodes)
                            {
                                if (Node.Value.GameNameIfAnyForTempStorage() == GameProj.GameName)
                                {
                                    NodesToDo.Add(Node.Key);
                                }
                            }

                            GameName = null;
                        }
                    }
                    if (GameName != null)
                    {
                        throw new AutomationException("Could not find game named {0}", GameName);
                    }
                }
            }
        }
        if (NodesToDo.Count == 0)
        {
            Log("No nodes specified, adding all nodes");
            foreach (var Node in GUBPNodes)
            {
                NodesToDo.Add(Node.Key);
            }
        }
        Log("Desired Nodes");
        foreach (var NodeToDo in NodesToDo)
        {
            Log("  {0}", NodeToDo);
        }

        if (!bOnlyNode)
        {
            bool bDoneWithDependencies = false;

            while (!bDoneWithDependencies)
            {
                bDoneWithDependencies = true;
                var Fringe = new HashSet<string>();
                foreach (var NodeToDo in NodesToDo)
                {
                    foreach (var Dep in GUBPNodes[NodeToDo].FullNamesOfDependencies)
                    {
                        if (!GUBPNodes.ContainsKey(Dep))
                        {
                            throw new AutomationException("Node {0} is not in the graph. It is a dependency of {1}.", Dep, NodeToDo);
                        }
                        if (!NodesToDo.Contains(Dep))
                        {
                            Fringe.Add(Dep);
                            bDoneWithDependencies = false;
                        }
                    }
                    foreach (var Dep in GUBPNodes[NodeToDo].FullNamesOfPseudosependencies)
                    {
                        if (!GUBPNodes.ContainsKey(Dep))
                        {
                            throw new AutomationException("Node {0} is not in the graph. It is a pseudodependency of {1}.", Dep, NodeToDo);
                        }
                    }
                }
                NodesToDo.UnionWith(Fringe);
            }
        }

        string ExplicitTrigger = "";
        bool CommanderSetup = ParseParam("CommanderJobSetupOnly");
        string FakeFail = ParseParamValue("FakeFail");
        if (CommanderSetup)
        {
            ExplicitTrigger = ParseParamValue("TriggerNode");
            if (!String.IsNullOrEmpty(ExplicitTrigger))
            {
                bool bFoundIt = false;
                foreach (var Node in GUBPNodes)
                {
                    if (Node.Value.GetFullName().Equals(ExplicitTrigger, StringComparison.InvariantCultureIgnoreCase))
                    {
                        if (Node.Value.TriggerNode() && Node.Value.RunInEC())
                        {
                            Node.Value.SetAsExplicitTrigger();
                            bFoundIt = true;
                            break;
                        }
                    }
                }
                if (!bFoundIt)
                {
                    throw new AutomationException("Could not find trigger node named {0}", ExplicitTrigger);
                }
            }
            else
            {
                ExplicitTrigger = "";
                if (bSkipTriggers)
                {
                    foreach (var Node in GUBPNodes)
                    {
                        if (Node.Value.TriggerNode() && Node.Value.RunInEC())
                        {
                            Node.Value.SetAsExplicitTrigger();
                        }
                    }
                }
            }
        }
        var OrdereredToDo = new List<string>();

        // here we do a topological sort of the nodes, subject to a lexographical and priority sort
        while (NodesToDo.Count > 0)
        {
            bool bProgressMade = false;
            float BestPriority = -1E20f;
            string BestNode = "";
            bool BestPseudoReady = false;
            foreach (var NodeToDo in NodesToDo)
            {
                bool bReady = true;
                foreach (var Dep in GUBPNodes[NodeToDo].FullNamesOfDependencies)
                {
                    if (!GUBPNodes.ContainsKey(Dep))
                    {
                        throw new AutomationException("Dependency {0} node found.", Dep);
                    }
                    if (NodesToDo.Contains(Dep))
                    {
                        bReady = false;
                        break;
                    }
                }
                bool bPseudoReady = true;
                foreach (var Dep in GUBPNodes[NodeToDo].FullNamesOfPseudosependencies)
                {
                    if (!GUBPNodes.ContainsKey(Dep))
                    {
                        throw new AutomationException("Pseudodependency {0} node found.", Dep);
                    }
                    if (NodesToDo.Contains(Dep))
                    {
                        bPseudoReady = false;
                        break;
                    }
                }
                var Priority = GUBPNodes[NodeToDo].Priority();

                if (bReady && BestNode != "")
                {
                    if (String.Compare(GetControllingTriggerDotName(BestNode), GetControllingTriggerDotName(NodeToDo)) < 0) //sorted by controlling trigger
                    {
                        bReady = false;
                    }
                    else if (String.Compare(GetControllingTriggerDotName(BestNode), GetControllingTriggerDotName(NodeToDo)) == 0) //sorted by controlling trigger
                    {
                        if (GUBPNodes[BestNode].IsSticky() && !GUBPNodes[NodeToDo].IsSticky()) //sticky nodes first
                        {
                            bReady = false;
                        }
                        else if (GUBPNodes[BestNode].IsSticky() == GUBPNodes[NodeToDo].IsSticky())
                        {
                            if (BestPseudoReady && !bPseudoReady)
                            {
                                bReady = false;
                            }
                            else if (BestPseudoReady == bPseudoReady)
                            {
                                bool IamLateTrigger = GUBPNodes[NodeToDo].TriggerNode() && NodeToDo != ExplicitTrigger && !NodeIsAlreadyComplete(NodeToDo, LocalOnly);
                                bool BestIsLateTrigger = GUBPNodes[BestNode].TriggerNode() && BestNode != ExplicitTrigger && !NodeIsAlreadyComplete(BestNode, LocalOnly);
                                if (BestIsLateTrigger && !IamLateTrigger)
                                {
                                    bReady = false;
                                }
                                else if (BestIsLateTrigger == IamLateTrigger)
                                {

                                    if (Priority < BestPriority)
                                    {
                                        bReady = false;
                                    }
                                    else if (Priority == BestPriority)
                                    {
                                        if (BestNode.CompareTo(NodeToDo) < 0)
                                        {
                                            bReady = false;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                if (bReady)
                {
                    BestPriority = Priority;
                    BestNode = NodeToDo;
                    BestPseudoReady = bPseudoReady;
                    bProgressMade = true;
                }
            }
            if (bProgressMade)
            {
                OrdereredToDo.Add(BestNode);
                NodesToDo.Remove(BestNode);
            }

            if (!bProgressMade && NodesToDo.Count > 0)
            {
                Log("Cycle in GUBP, could not resolve:");
                foreach (var NodeToDo in NodesToDo)
                {
                    Log("  {0}", NodeToDo);
                }
                throw new AutomationException("Cycle in GUBP");
            }
        }

        // find all unfinished triggers, excepting the one we are triggering right now
        var UnfinishedTriggers = new List<string>();
        if (!bSkipTriggers)
        {
            foreach (var NodeToDo in OrdereredToDo)
            {
                if (GUBPNodes[NodeToDo].TriggerNode() && !NodeIsAlreadyComplete(NodeToDo, LocalOnly))
                {
                    if (String.IsNullOrEmpty(ExplicitTrigger) || ExplicitTrigger != NodeToDo)
                    {
                        UnfinishedTriggers.Add(NodeToDo);
                    }
                }
            }
        }

        Log("*********** Desired And Dependent Nodes, in order.");
        PrintNodes(this, OrdereredToDo, LocalOnly, UnfinishedTriggers);

        if (CommanderSetup)
        {
            if (OrdereredToDo.Count == 0)
            {
                throw new AutomationException("No nodes to do!");
            }
            // we don't really need this yet, but it is left here for future use
            var ECProps = new List<string>();
            var ECJobProps = new List<string>();
            if (ExplicitTrigger != "")
            {
                ECJobProps.Add("IsRoot=0");
            }
            else
            {
                ECJobProps.Add("IsRoot=1");
            }

            var FilteredOrdereredToDo = new List<string>();
            // remove nodes that have unfinished triggers
            foreach (var NodeToDo in OrdereredToDo)
            {
                string ControllingTrigger = GetControllingTrigger(NodeToDo);
                bool bNoUnfinishedTriggers = !UnfinishedTriggers.Contains(ControllingTrigger);

                if (bNoUnfinishedTriggers)
                {
                    // if we are triggering, then remove nodes that are not controlled by the trigger or are dependencies of this trigger
                    if (!String.IsNullOrEmpty(ExplicitTrigger))
                    {
                        if (ExplicitTrigger != NodeToDo && !NodeDependsOn(NodeToDo, ExplicitTrigger) && !NodeDependsOn(ExplicitTrigger, NodeToDo))
                        {
                            continue; // this wasn't on the chain related to the trigger we are triggering, so it is not relevant
                        }
                    }
                    FilteredOrdereredToDo.Add(NodeToDo);
                }
            }
            OrdereredToDo = FilteredOrdereredToDo;
            Log("*********** EC Nodes, in order.");
            PrintNodes(this, OrdereredToDo, LocalOnly, UnfinishedTriggers);

            // here we are just making sure everything before the explicit trigger is completed.
            if (!String.IsNullOrEmpty(ExplicitTrigger))
            {
                foreach (var NodeToDo in FilteredOrdereredToDo)
                {
                    if (GUBPNodes[NodeToDo].RunInEC() && !NodeIsAlreadyComplete(NodeToDo, LocalOnly) && NodeToDo != ExplicitTrigger && !NodeDependsOn(ExplicitTrigger, NodeToDo)) // if something is already finished, we don't put it into EC
                    {
                        throw new AutomationException("We are being asked to process node {0}, however, this is an explicit trigger {1}, so everything before it should already be handled. It seems likely that you waited too long to run the trigger. You will have to do a new build from scratch.", NodeToDo, ExplicitTrigger);
                    }
                }
            }

            string LastSticky = "";
            bool HitNonSticky = false;
            bool bHaveECNodes = false;
            // sticky nodes are ones that we run on the main agent. We run then first and they must not be intermixed with parallel jobs
            foreach (var NodeToDo in OrdereredToDo)
            {
                if (GUBPNodes[NodeToDo].RunInEC() && !NodeIsAlreadyComplete(NodeToDo, LocalOnly)) // if something is already finished, we don't put it into EC
                {
                    bHaveECNodes = true;
                    if (GUBPNodes[NodeToDo].IsSticky())
                    {
                        LastSticky = NodeToDo;
                        if (HitNonSticky && !bSkipTriggers)
                        {
                            throw new AutomationException("Sticky and non-sticky jobs did not sort right.");
                        }
                    }
                    else
                    {
                        HitNonSticky = true;
                    }
                }
            }
            string ParentPath = ParseParamValue("ParentPath");
            string BaseArgs = String.Format("createJobStep --parentPath {0}", ParentPath);

            if (LastSticky == "" && bHaveECNodes)
            {
                // if we don't have any sticky nodes and we have other nodes, we run a fake noop just to release the resource
                string Args = String.Format("{0} --subprocedure GUBP_UAT_Node --parallel 0 --jobStepName Noop --actualParameter NodeName=Noop --actualParameter Sticky=1 --releaseMode release", BaseArgs);
                RunECTool(Args);
            }

            var FakeECArgs = new List<string>();
            bool AddEmailProps = !ParseParam("NoAddEmailProps");
            var BranchForEmail = "";
            if (P4Enabled)
            {
                BranchForEmail = P4Env.BuildRootP4;
            }

            foreach (var NodeToDo in OrdereredToDo)
            {
                if (GUBPNodes[NodeToDo].RunInEC() && !NodeIsAlreadyComplete(NodeToDo, LocalOnly)) // if something is already finished, we don't put it into EC
                {
                    string EMails = "";
                    if (AddEmailProps)
                    {
                        if (bFake)
                        {
                            EMails = "kellan.carr[epic] gil.gribb[epic]";
                        }
                        else
                        {
                            EMails = GetEMailListForNode(this, NodeToDo);
                        }
                        EMails = EMails.Trim().Replace("[epic]", "@epicgames.com");
                        ECProps.Add("FailEmails/" + NodeToDo + "=" + EMails);
                    }
                    if (GUBPNodes[NodeToDo].SendSuccessEmail())
                    {
                        ECProps.Add("SendSuccessEmail/" + NodeToDo + "=1");
                    }
                    else
                    {
                        ECProps.Add("SendSuccessEmail/" + NodeToDo + "=0");
                    }
                    ECProps.Add(string.Format("AgentRequirementString/{0}={1}", NodeToDo, GUBPNodes[NodeToDo].ECAgentString()));
                    ECProps.Add(string.Format("RequiredMemory/{0}={1}", NodeToDo, GUBPNodes[NodeToDo].AgentMemoryRequirement()));
                    ECProps.Add(string.Format("Timeouts/{0}={1}", NodeToDo, GUBPNodes[NodeToDo].TimeoutInMinutes()));
                    if (GUBPNodesHistory.ContainsKey(NodeToDo))
                    {
                        ECProps.Add(string.Format("LastGreen/{0}={1}", NodeToDo, GUBPNodesHistory[NodeToDo].LastSucceeded));
                        ECProps.Add(string.Format("RedsSince/{0}={1}", NodeToDo, GUBPNodesHistory[NodeToDo].FailedString));
                        ECProps.Add(string.Format("InProgress/{0}={1}", NodeToDo, GUBPNodesHistory[NodeToDo].InProgressString));
                    }
                    else
                    {
                        ECProps.Add(string.Format("LastGreen/{0}=0", NodeToDo));
                        ECProps.Add(string.Format("RedsSince/{0}=", NodeToDo));
                        ECProps.Add(string.Format("InProgress/{0}=", NodeToDo));
                    }
                    bool Sticky = GUBPNodes[NodeToDo].IsSticky();
                    bool DoParallel = !Sticky;
                    if (Sticky && GUBPNodes[NodeToDo].ECAgentString() != "")
                    {
                        throw new AutomationException("Node {1} is sticky but has agent requirements.", NodeToDo);
                    }
                    string Procedure = GUBPNodes[NodeToDo].ECProcedure();

                    string Args = String.Format("{0} --subprocedure {1} --parallel {2} --jobStepName {3} --actualParameter NodeName={4}",
                        BaseArgs, Procedure, DoParallel ? 1 : 0, NodeToDo, NodeToDo);
                    string ProcedureParams = GUBPNodes[NodeToDo].ECProcedureParams();
                    if (!String.IsNullOrEmpty(ProcedureParams))
                    {
                        Args = Args + " " + ProcedureParams;
                    }

                    string PreCondition = "";
                    string RunCondition = "";

                    var EcDeps = GetECDependencies(NodeToDo);
                    var UncompletedEcDeps = new List<string>();
                    foreach (var Dep in EcDeps)
                    {
                        if (GUBPNodes[Dep].RunInEC() && !NodeIsAlreadyComplete(Dep, LocalOnly) && OrdereredToDo.Contains(Dep)) // if something is already finished, we don't put it into EC
                        {
                            UncompletedEcDeps.Add(Dep);
                        }
                    }
                    if (UncompletedEcDeps.Count > 0)
                    {
                        //$[/javascript if(getProperty("$[/myWorkflow/ParentWorkflow]/GUBP_Game_$[/myWorkflow/GameName]_MonoPlatform1_Exists") != "1" || getProperty("$[/myWorkflow/ParentWorkflow]/$[$[/myWorkflow/ParentWorkflow]/GUBP_Game_$[/myWorkflow/GameName]_MonoPlatform1_Name]_EditorPlatform_Success") != "1") true; else false;]
                        {
                            PreCondition = "\"$[/javascript if(";
                            int Index = 0;
                            foreach (var Dep in UncompletedEcDeps)
                            {
                                PreCondition = PreCondition + "getProperty('" + ParentPath + "/jobSteps[" + Dep + "]/status') == 'completed'";
                                Index++;
                                if (Index != UncompletedEcDeps.Count)
                                {
                                    PreCondition = PreCondition + " && ";
                                }
                            }
                            PreCondition = PreCondition + ") true;]\"";
                        }
                        {
                            RunCondition = "\"$[/javascript if(";
                            int Index = 0;
                            foreach (var Dep in UncompletedEcDeps)
                            {
                                RunCondition = RunCondition + "('$[" + ParentPath + "/jobSteps[" + Dep + "]/outcome]' == 'success' || ";
                                RunCondition = RunCondition + "'$[" + ParentPath + "/jobSteps[" + Dep + "]/outcome]' == 'warning')";

                                Index++;
                                if (Index != UncompletedEcDeps.Count)
                                {
                                    RunCondition = RunCondition + " && ";
                                }
                            }
                            RunCondition = RunCondition + ") true; else false;]\"";
                        }
                    }

                    if (!String.IsNullOrEmpty(PreCondition))
                    {
                        Args = Args + " --precondition " + PreCondition;
                    }
                    if (!String.IsNullOrEmpty(RunCondition))
                    {
                        Args = Args + " --condition " + RunCondition;
                    }
                    if (Sticky && NodeToDo == LastSticky)
                    {
                        Args = Args + " --releaseMode release";
                    }
                    RunECTool(Args);
                    if (bFakeEC &&
                        !UnfinishedTriggers.Contains(NodeToDo) &&
                        GUBPNodes[NodeToDo].ECProcedure().StartsWith("GUBP_UAT_Node") // other things we really can't test
                        ) // unfinished triggers are never run directly by EC, rather it does another job setup
                    {
                        string Arg = String.Format("gubp -Node={0} -FakeEC {1} {2} {3} {4}",
                            NodeToDo,
                            bFake ? "-Fake" : "" ,
                            ParseParam("AllPlatforms") ? "-AllPlatforms" : "",
                            ParseParam("UnfinishedTriggersFirst") ? "-UnfinishedTriggersFirst" : "",
                            ParseParam("UnfinishedTriggersParallel") ? "-UnfinishedTriggersParallel" : ""
                            );

                        string Node = ParseParamValue("-Node");
                        if (!String.IsNullOrEmpty(Node))
                        {
                            Arg = Arg + " -Node=" + Node;
                        }
                        if (!String.IsNullOrEmpty(FakeFail))
                        {
                            Arg = Arg + " -FakeFail=" + FakeFail;
                        }
                        FakeECArgs.Add(Arg);
                    }
                }

            }
            {
                ECProps.Add("GUBP_LoadedProps=1");
                string BranchDefFile = CommandUtils.CombinePaths(CommandUtils.CmdEnv.LogFolder, "BranchDef.properties");
                CommandUtils.WriteAllLines(BranchDefFile, ECProps.ToArray());
                RunECTool(String.Format("setProperty \"/myWorkflow/BranchDefFile\" \"{0}\"", BranchDefFile.Replace("\\", "\\\\")));
            }
            {
                ECProps.Add("GUBP_LoadedJobProps=1");
                string BranchJobDefFile = CommandUtils.CombinePaths(CommandUtils.CmdEnv.LogFolder, "BranchJobDef.properties");
                CommandUtils.WriteAllLines(BranchJobDefFile, ECProps.ToArray());
                RunECTool(String.Format("setProperty \"/myJob/BranchJobDefFile\" \"{0}\"", BranchJobDefFile.Replace("\\", "\\\\")));
            }
            if (bFakeEC)
            {
                foreach (var Args in FakeECArgs)
                {
                    RunUAT(CmdEnv, Args);
                }
            }
            Log("Commander setup only, done.");
            PrintRunTime();
            return;

        }
        if (ParseParam("SaveGraph"))
        {
            SaveGraphVisualization(OrdereredToDo);
        }
        if (bListOnly)
        {
            Log("List only, done.");
            return;
        }

        var BuildProductToNodeMap = new Dictionary<string, string>();
        foreach (var NodeToDo in OrdereredToDo)
        {
            if (GUBPNodes[NodeToDo].BuildProducts != null || GUBPNodes[NodeToDo].AllDependencyBuildProducts != null)
            {
                throw new AutomationException("topological sort error");
            }

            GUBPNodes[NodeToDo].AllDependencyBuildProducts = new List<string>();
            if (GUBPNodes[NodeToDo].FullNamesOfDependencies == null)
            {
                throw new AutomationException("Node {0} was not processed yet?", NodeToDo);
            }
            foreach (var Dep in GUBPNodes[NodeToDo].FullNamesOfDependencies)
            {
                if (GUBPNodes[Dep].BuildProducts == null)
                {
                    throw new AutomationException("Node {0} was not processed yet2? Processing {1}", Dep, NodeToDo);
                }
                foreach (var Prod in GUBPNodes[Dep].BuildProducts)
                {
                    GUBPNodes[NodeToDo].AddDependentBuildProduct(Prod);
                }
                if (GUBPNodes[Dep].AllDependencyBuildProducts == null)
                {
                    throw new AutomationException("Node {0} was not processed yet3?  Processing {1}", Dep, NodeToDo);
                }
                foreach (var Prod in GUBPNodes[Dep].AllDependencyBuildProducts)
                {
                    GUBPNodes[NodeToDo].AddDependentBuildProduct(Prod);
                }
            }
            string NodeStoreName = StoreName + "-" + GUBPNodes[NodeToDo].GetFullName();

            string GameNameIfAny = GUBPNodes[NodeToDo].GameNameIfAnyForTempStorage();
            string StorageRootIfAny = GUBPNodes[NodeToDo].RootIfAnyForTempStorage();
            if (bFake)
            {
                StorageRootIfAny = ""; // we don't rebase fake runs since those are entirely "records of success", which are always in the logs folder
            }

            // this is kinda complicated
            bool SaveSuccessRecords = (IsBuildMachine || bFakeEC) && // no real reason to make these locally except for fakeEC tests
                (!GUBPNodes[NodeToDo].TriggerNode() || GUBPNodes[NodeToDo].IsSticky()) // trigger nodes are run twice, one to start the new workflow and once when it is actually triggered, we will save reconds for the latter
                && (GUBPNodes[NodeToDo].RunInEC() || !GUBPNodes[NodeToDo].IsAggregate()); //aggregates not in EC can be "run" multiple times, so we can't track those

            Log("***** Running GUBP Node {0} -> {1} : {2}", GUBPNodes[NodeToDo].GetFullName(), GameNameIfAny, NodeStoreName);
            if (NodeIsAlreadyComplete(NodeToDo, LocalOnly))
            {
                Log("***** Retrieving GUBP Node {0} from {1}", GUBPNodes[NodeToDo].GetFullName(), NodeStoreName);
                GUBPNodes[NodeToDo].BuildProducts = RetrieveFromTempStorage(CmdEnv, NodeStoreName, GameNameIfAny, StorageRootIfAny);
            }
            else
            {
                if (SaveSuccessRecords)
                {
                    SaveStatus(NodeToDo, StartedTempStorageSuffix, NodeStoreName, bSaveSharedTempStorage, GameNameIfAny);
                }
                try
                {
                    if (!String.IsNullOrEmpty(FakeFail) && FakeFail.Equals(NodeToDo, StringComparison.InvariantCultureIgnoreCase))
                    {
                        throw new AutomationException("Failing node {0} by request.", NodeToDo);
                    }
                    if (bFake)
                    {
                        Log("***** FAKE!! Building GUBP Node {0} for {1}", NodeToDo, NodeStoreName);
                        GUBPNodes[NodeToDo].DoFakeBuild(this);
                    }
                    else
                    {
                        Log("***** Building GUBP Node {0} for {1}", NodeToDo, NodeStoreName);
                        GUBPNodes[NodeToDo].DoBuild(this);
                    }
                    if (!GUBPNodes[NodeToDo].IsAggregate())
                    {
                        StoreToTempStorage(CmdEnv, NodeStoreName, GUBPNodes[NodeToDo].BuildProducts, !bSaveSharedTempStorage, GameNameIfAny, StorageRootIfAny);
                    }
                }
                catch (Exception Ex)
                {
                    if (SaveSuccessRecords)
                    {
                        SaveStatus(NodeToDo, FailedTempStorageSuffix, NodeStoreName, bSaveSharedTempStorage, GameNameIfAny);
                    }

                    Log("{0}", ExceptionToString(Ex));

                    if (GUBPNodesHistory.ContainsKey(NodeToDo))
                    {
                        var History = GUBPNodesHistory[NodeToDo];
                        Log("Changes since last green *********************************");
                        Log("");
                        Log("");
                        Log("");
                        PrintDetailedChanges(History);
                    }

                    string FailInfo = "";
                    FailInfo += "********************************* Main log file";
                    FailInfo += Environment.NewLine + Environment.NewLine;
                    FailInfo += LogUtils.GetLogTail();
                    FailInfo += Environment.NewLine + Environment.NewLine + Environment.NewLine;

                    string OtherLog = "See logfile for details: '";
                    if (FailInfo.Contains(OtherLog))
                    {
                        string LogFile = FailInfo.Substring(FailInfo.IndexOf(OtherLog) + OtherLog.Length);
                        if (LogFile.Contains("'"))
                        {
                            LogFile = CombinePaths(CmdEnv.LogFolder, LogFile.Substring(0, LogFile.IndexOf("'")));
                            if (FileExists_NoExceptions(LogFile))
                            {
                                FailInfo += "********************************* Sub log file " + LogFile;
                                FailInfo += Environment.NewLine + Environment.NewLine;

                                FailInfo += LogUtils.GetLogTail(LogFile);
                                FailInfo += Environment.NewLine + Environment.NewLine + Environment.NewLine;
                            }
                        }
                    }

                    string Filename = CombinePaths(CmdEnv.LogFolder, "LogTailsAndChanges.log");
                    WriteAllText(Filename, FailInfo);

                    throw(Ex);
                }
                if (SaveSuccessRecords)
                {
                    SaveStatus(NodeToDo, SucceededTempStorageSuffix, NodeStoreName, bSaveSharedTempStorage, GameNameIfAny);
                }
            }
            foreach (var Product in GUBPNodes[NodeToDo].BuildProducts)
            {
                if (BuildProductToNodeMap.ContainsKey(Product))
                {
                    throw new AutomationException("Overlapping build product: {0} and {1} both produce {2}", BuildProductToNodeMap[Product], NodeToDo, Product);
                }
                BuildProductToNodeMap.Add(Product, NodeToDo);
            }
        }

        PrintRunTime();
    }
예제 #10
0
 void PrintDetailedChanges(NodeHistory History, bool bShowAllChanges = false)
 {
     string Me = String.Format("{0}   <<<< local sync", P4Env.Changelist);
     int LastOutputForChanges = 0;
     int LastGreen = History.LastSucceeded;
     if (bShowAllChanges)
     {
         LastGreen = 0;
     }
     foreach (var cl in History.AllStarted)
     {
         if (cl < LastGreen)
         {
             continue;
         }
         if (P4Env.Changelist < cl && Me != "")
         {
             LastOutputForChanges = PrintChanges(LastOutputForChanges, P4Env.Changelist, LastGreen);
             Log("         {0}", Me);
             Me = "";
         }
         string Status = "In Process";
         if (History.AllSucceeded.Contains(cl))
         {
             Status = "ok";
         }
         if (History.AllFailed.Contains(cl))
         {
             Status = "FAIL";
         }
         LastOutputForChanges = PrintChanges(LastOutputForChanges, cl, LastGreen);
         Log("         {0}   {1}", cl, Status);
     }
     if (Me != "")
     {
         LastOutputForChanges = PrintChanges(LastOutputForChanges, P4Env.Changelist, LastGreen);
         Log("         {0}", Me);
     }
 }
예제 #11
0
 internal IEnumerable <NodePlacement> GetNodePlacementsMatchingCurrentFilters(NodeHistory node)
 {
     return(node.Placements
            .Where(p => this.Model.IsInstanceAnnotatedAs(
                       p.Instance, this.osTypes, this.licenseTypes)));
 }
예제 #12
0
    static NodeHistory FindNodeHistory(BuildNode NodeToDo, string CLString, string StoreName)
    {
        NodeHistory History = null;

        if (!(NodeToDo is TriggerNode) && CLString != "")
        {
            string GameNameIfAny = NodeToDo.Node.GameNameIfAnyForTempStorage();
            string NodeStoreWildCard = StoreName.Replace(CLString, "*") + "-" + NodeToDo.Name;
            History = new NodeHistory();

            History.AllStarted = ConvertCLToIntList(TempStorage.FindTempStorageManifests(CmdEnv, NodeStoreWildCard + StartedTempStorageSuffix, false, true, GameNameIfAny));
            History.AllSucceeded = ConvertCLToIntList(TempStorage.FindTempStorageManifests(CmdEnv, NodeStoreWildCard + SucceededTempStorageSuffix, false, true, GameNameIfAny));
            History.AllFailed = ConvertCLToIntList(TempStorage.FindTempStorageManifests(CmdEnv, NodeStoreWildCard + FailedTempStorageSuffix, false, true, GameNameIfAny));

            int CL;
            int.TryParse(CLString, out CL);

            if (History.AllFailed.Count > 0)
            {
                History.LastFailed = History.AllFailed.LastOrDefault(x => x < CL);
            }
            if (History.AllSucceeded.Count > 0)
            {
                History.LastSucceeded = History.AllSucceeded.LastOrDefault(x => x < CL);

                foreach (int Failed in History.AllFailed)
                {
                    if (Failed > History.LastSucceeded)
                    {
                        History.Failed.Add(Failed);
                        History.FailedString = GUBPNode.MergeSpaceStrings(History.FailedString, String.Format("{0}", Failed));
                    }
                }
                foreach (int Started in History.AllStarted)
                {
                    if (Started > History.LastSucceeded && !History.Failed.Contains(Started))
                    {
                        History.InProgress.Add(Started);
                        History.InProgressString = GUBPNode.MergeSpaceStrings(History.InProgressString, String.Format("{0}", Started));
                    }
                }
            }
        }

        return History;
    }
예제 #13
0
    int FindLastNonDuplicateFail(BuildNode NodeToDo, NodeHistory History, string CLString, string StoreName)
    {
        int Result = P4Env.Changelist;

        string GameNameIfAny = NodeToDo.Node.GameNameIfAnyForTempStorage();
        string NodeStore = StoreName + "-" + NodeToDo.Name + FailedTempStorageSuffix;

        List<int> BackwardsFails = new List<int>(History.AllFailed);
        BackwardsFails.Add(P4Env.Changelist);
        BackwardsFails.Sort();
        BackwardsFails.Reverse();
        HashSet<string> CurrentErrors = null;
        foreach (int CL in BackwardsFails)
        {
            if (CL > P4Env.Changelist)
            {
                continue;
            }
            if (CL <= History.LastSucceeded)
            {
                break;
            }
            string ThisNodeStore = NodeStore.Replace(CLString, String.Format("{0}", CL));
            TempStorage.DeleteLocalTempStorage(CmdEnv, ThisNodeStore, true); // these all clash locally, which is fine we just retrieve them from shared

            List<string> Files = null;
            try
            {
                bool WasLocal;
                Files = TempStorage.RetrieveFromTempStorage(CmdEnv, ThisNodeStore, out WasLocal, GameNameIfAny); // this will fail on our CL if we didn't fail or we are just setting up the branch
            }
            catch (Exception)
            {
            }
            if (Files == null)
            {
                continue;
            }
            if (Files.Count != 1)
            {
                throw new AutomationException("Unexpected number of files for fail record {0}", Files.Count);
            }
            string ErrorFile = Files[0];
            HashSet<string> ThisErrors = ECJobPropsUtils.ErrorsFromProps(ErrorFile);
            if (CurrentErrors == null)
            {
                CurrentErrors = ThisErrors;
            }
            else
            {
                if (CurrentErrors.Count == 0 || !HashSetEqual(CurrentErrors, ThisErrors))
                {
                    break;
                }
                Result = CL;
            }
        }
        return Result;
    }
예제 #14
0
    static NodeHistory FindNodeHistory(BuildNode NodeToDo, JobInfo JobInfo)
    {
		NodeHistory History = null;

        // Don't get node history on nodes that don't have a valid CL.
        if (!(NodeToDo is TriggerNode) && JobInfo.Changelist > 0)
        {
            string GameNameIfAny = NodeToDo.GameNameIfAnyForTempStorage;

            History = new NodeHistory
            {
                AllStarted = TempStorage.FindMatchingSharedTempStorageNodeCLs(new TempStorageNodeInfo(JobInfo, NodeToDo.Name + StartedTempStorageSuffix), GameNameIfAny),
                AllSucceeded = TempStorage.FindMatchingSharedTempStorageNodeCLs(new TempStorageNodeInfo(JobInfo, NodeToDo.Name + SucceededTempStorageSuffix), GameNameIfAny),
                AllFailed = TempStorage.FindMatchingSharedTempStorageNodeCLs(new TempStorageNodeInfo(JobInfo, NodeToDo.Name + FailedTempStorageSuffix), GameNameIfAny)
            };

            if (History.AllFailed.Count > 0)
            {
                History.LastFailed = History.AllFailed.LastOrDefault(x => x < JobInfo.Changelist);
            }
            if (History.AllSucceeded.Count > 0)
            {
                History.LastSucceeded = History.AllSucceeded.LastOrDefault(x => x < JobInfo.Changelist);

                foreach (int Failed in History.AllFailed)
                {
                    if (Failed > History.LastSucceeded)
                    {
                        History.Failed.Add(Failed);
                        History.FailedString = GUBPNode.MergeSpaceStrings(History.FailedString, String.Format("{0}", Failed));
                    }
                }
                foreach (int Started in History.AllStarted)
                {
                    if (Started > History.LastSucceeded && !History.Failed.Contains(Started))
                    {
                        History.InProgress.Add(Started);
                        History.InProgressString = GUBPNode.MergeSpaceStrings(History.InProgressString, String.Format("{0}", Started));
                    }
                }
			}
        }

		return History;
    }
예제 #15
0
    int FindLastNonDuplicateFail(BuildNode NodeToDo, NodeHistory History, JobInfo JobInfo)
    {
        int Result = P4Env.Changelist;

        string GameNameIfAny = NodeToDo.GameNameIfAnyForTempStorage;
        var TempStorageNodeInfo = new TempStorageNodeInfo(JobInfo, NodeToDo.Name + FailedTempStorageSuffix);

        List<int> BackwardsFails = new List<int>(History.AllFailed);
        BackwardsFails.Add(P4Env.Changelist);
        BackwardsFails.Sort();
        BackwardsFails.Reverse();
        HashSet<string> CurrentErrors = null;
        foreach (int CL in BackwardsFails)
        {
            if (CL > P4Env.Changelist)
            {
                continue;
            }
            if (CL <= History.LastSucceeded)
            {
                break;
            }
            // Find any local temp storage manifest for this changelist and delete it.
            var ThisTempStorageNodeInfo = new TempStorageNodeInfo(JobInfo.CreateWithNewChangelist(CL), TempStorageNodeInfo.NodeStorageName);
            TempStorage.DeleteLocalTempStorageManifest(ThisTempStorageNodeInfo, true); // these all clash locally, which is fine we just retrieve them from shared

            List<string> Files = null;
            try
            {
                bool WasLocal;
                Files = TempStorage.RetrieveFromTempStorage(ThisTempStorageNodeInfo, out WasLocal, GameNameIfAny, CmdEnv.LocalRoot); // this will fail on our CL if we didn't fail or we are just setting up the branch
            }
            catch (Exception)
            {
            }
            if (Files == null)
            {
                continue;
            }
            if (Files.Count != 1)
            {
                throw new AutomationException("Unexpected number of files for fail record {0}", Files.Count);
            }
            string ErrorFile = Files[0];
            HashSet<string> ThisErrors = ECJobPropsUtils.ErrorsFromProps(ErrorFile);
            if (CurrentErrors == null)
            {
                CurrentErrors = ThisErrors;
            }
            else
            {
                if (CurrentErrors.Count == 0 || !HashSetEqual(CurrentErrors, ThisErrors))
                {
                    break;
                }
                Result = CL;
            }
        }
        return Result;
    }
예제 #16
0
 protected virtual void WriteHistory(INode node, String action, Boolean success, String remark)
 {
     NodeHistory.Create(node, action, success, remark, Environment.MachineName, UserHost);
 }
예제 #17
0
        protected virtual NodeHistory WriteHistory(String action, Boolean success, INode node, String remark = null)
        {
            var ip = UserHost;

            return(NodeHistory.Create(node, action, success, remark, Environment.MachineName, ip));
        }
예제 #18
0
    void PrintDetailedChanges(NodeHistory History, bool bShowAllChanges = false)
    {
        var StartTime = DateTime.UtcNow;

        string Me = String.Format("{0}   <<<< local sync", P4Env.Changelist);
        int LastOutputForChanges = 0;
        int LastGreen = History.LastSucceeded;
        if (bShowAllChanges)
        {
            if (History.AllStarted.Count > 0)
            {
                LastGreen = History.AllStarted[0];
            }
        }
        foreach (var cl in History.AllStarted)
        {
            if (cl < LastGreen)
            {
                continue;
            }
            if (P4Env.Changelist < cl && Me != "")
            {
                LastOutputForChanges = PrintChanges(LastOutputForChanges, P4Env.Changelist, LastGreen);
                Log("         {0}", Me);
                Me = "";
            }
            string Status = "In Process";
            if (History.AllSucceeded.Contains(cl))
            {
                Status = "ok";
            }
            if (History.AllFailed.Contains(cl))
            {
                Status = "FAIL";
            }
            LastOutputForChanges = PrintChanges(LastOutputForChanges, cl, LastGreen);
            Log("         {0}   {1}", cl, Status);
        }
        if (Me != "")
        {
            LastOutputForChanges = PrintChanges(LastOutputForChanges, P4Env.Changelist, LastGreen);
            Log("         {0}", Me);
        }
        var BuildDuration = (DateTime.UtcNow - StartTime).TotalMilliseconds;
        Log("Took {0}s to get P4 history", BuildDuration / 1000);
    }
예제 #19
0
    void GetFailureEmails(ElectricCommander EC, BuildNode NodeToDo, NodeHistory History, string CLString, string StoreName, bool OnlyLateUpdates = false)
    {
        string FailCauserEMails = "";
        string EMailNote = "";
        bool SendSuccessForGreenAfterRed = false;
        int NumPeople = 0;
        if (History != null)
        {
            if (History.LastSucceeded > 0 && History.LastSucceeded < P4Env.Changelist)
            {
                int LastNonDuplicateFail = P4Env.Changelist;
                try
                {
                    if (OnlyLateUpdates)
                    {
                        LastNonDuplicateFail = FindLastNonDuplicateFail(NodeToDo, History, CLString, StoreName);
                        if (LastNonDuplicateFail < P4Env.Changelist)
                        {
                            Log("*** Red-after-red spam reduction, changed CL {0} to CL {1} because the errors didn't change.", P4Env.Changelist, LastNonDuplicateFail);
                        }
                    }
                }
                catch (Exception Ex)
                {
                    LastNonDuplicateFail = P4Env.Changelist;
                    Log(System.Diagnostics.TraceEventType.Warning, "Failed to FindLastNonDuplicateFail.");
                    Log(System.Diagnostics.TraceEventType.Warning, LogUtils.FormatException(Ex));
                }

                if(LastNonDuplicateFail > History.LastSucceeded)
                {
                    List<P4Connection.ChangeRecord> ChangeRecords = GetSourceChangeRecords(History.LastSucceeded + 1, LastNonDuplicateFail);
                    foreach (P4Connection.ChangeRecord Record in ChangeRecords)
                    {
                        FailCauserEMails = GUBPNode.MergeSpaceStrings(FailCauserEMails, Record.UserEmail);
                    }
                }

                if (!String.IsNullOrEmpty(FailCauserEMails))
                {
                    NumPeople++;
                    foreach (char AChar in FailCauserEMails.ToCharArray())
                    {
                        if (AChar == ' ')
                        {
                            NumPeople++;
                        }
                    }
                    if (NumPeople > 50)
                    {
                        EMailNote = String.Format("This step has been broken for more than 50 changes. It last succeeded at CL {0}. ", History.LastSucceeded);
                    }
                }
            }
            else if (History.LastSucceeded <= 0)
            {
                EMailNote = String.Format("This step has been broken for more than a few days, so there is no record of it ever succeeding. ");
            }
            if (EMailNote != "" && !String.IsNullOrEmpty(History.FailedString))
            {
                EMailNote += String.Format("It has failed at CLs {0}. ", History.FailedString);
            }
            if (EMailNote != "" && !String.IsNullOrEmpty(History.InProgressString))
            {
                EMailNote += String.Format("These CLs are being built right now {0}. ", History.InProgressString);
            }
            if (History.LastSucceeded > 0 && History.LastSucceeded < P4Env.Changelist && History.LastFailed > History.LastSucceeded && History.LastFailed < P4Env.Changelist)
            {
                SendSuccessForGreenAfterRed = ParseParam("CIS");
            }
        }

        if (History == null)
        {
            EC.UpdateEmailProperties(NodeToDo, 0, "", FailCauserEMails, EMailNote, SendSuccessForGreenAfterRed);
        }
        else
        {
            EC.UpdateEmailProperties(NodeToDo, History.LastSucceeded, History.FailedString, FailCauserEMails, EMailNote, SendSuccessForGreenAfterRed);
        }
    }
예제 #20
0
    void UpdateNodeHistory(string Node, string CLString)
    {
        if (GUBPNodes[Node].RunInEC() && !GUBPNodes[Node].TriggerNode() && CLString != "")
        {
            string GameNameIfAny = GUBPNodes[Node].GameNameIfAnyForTempStorage();
            string NodeStoreWildCard = StoreName.Replace(CLString, "*") + "-" + GUBPNodes[Node].GetFullName();
            var History = new NodeHistory();

            History.AllStarted = ConvertCLToIntList(FindTempStorageManifests(CmdEnv, NodeStoreWildCard + StartedTempStorageSuffix, false, true, GameNameIfAny));
            History.AllSucceeded = ConvertCLToIntList(FindTempStorageManifests(CmdEnv, NodeStoreWildCard + SucceededTempStorageSuffix, false, true, GameNameIfAny));
            History.AllFailed = ConvertCLToIntList(FindTempStorageManifests(CmdEnv, NodeStoreWildCard + FailedTempStorageSuffix, false, true, GameNameIfAny));

            if (History.AllFailed.Count > 0)
            {
                History.LastFailed = History.AllFailed[History.AllFailed.Count - 1];
            }
            if (History.AllSucceeded.Count > 0)
            {
                History.LastSucceeded = History.AllSucceeded[History.AllSucceeded.Count - 1];

                foreach (var Failed in History.AllFailed)
                {
                    if (Failed > History.LastSucceeded)
                    {
                        History.Failed.Add(Failed);
                        History.FailedString = GUBPNode.MergeSpaceStrings(History.FailedString, String.Format("{0}", Failed));
                    }
                }
                foreach (var Started in History.AllStarted)
                {
                    if (Started > History.LastSucceeded && !History.Failed.Contains(Started))
                    {
                        History.InProgress.Add(Started);
                        History.InProgressString = GUBPNode.MergeSpaceStrings(History.InProgressString, String.Format("{0}", Started));
                    }
                }
            }
            if (GUBPNodesHistory.ContainsKey(Node))
            {
                GUBPNodesHistory.Remove(Node);
            }
            GUBPNodesHistory.Add(Node, History);
        }
    }
예제 #21
0
    /// <summary>
    /// Print a list of source changes, along with the success state for other builds of this node.
    /// </summary>
    /// <param name="History">History for this node</param>
    static void PrintDetailedChanges(NodeHistory History, int CurrentCL)
    {
        DateTime StartTime = DateTime.UtcNow;

        // Find all the changelists that we're interested in
        SortedSet<int> BuildChanges = new SortedSet<int>();
        BuildChanges.UnionWith(History.AllStarted.Where(x => x >= History.LastSucceeded));
        BuildChanges.Add(CurrentCL);

        // Find all the changelists that we're interested in
        List<P4Connection.ChangeRecord> ChangeRecords = GetSourceChangeRecords(BuildChanges.First(), BuildChanges.Last());

        // Print all the changes in the set
        int LastCL = BuildChanges.First();
        foreach(int BuildCL in BuildChanges)
        {
            // Show all the changes in this range
            foreach(P4Connection.ChangeRecord Record in ChangeRecords.Where(x => x.CL > LastCL && x.CL <= BuildCL))
            {
                string[] Lines = Record.Summary.Split(new char[]{ '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
                Log("             {0} {1} {2}", Record.CL, Record.UserEmail, (Lines.Length > 0)? Lines[0] : "");
            }

            // Show the status of this build
            string BuildStatus;
            if(BuildCL == CurrentCL)
            {
                BuildStatus = "this sync";
            }
            else if (History.AllSucceeded.Contains(BuildCL))
            {
                BuildStatus = "built";
            }
            else if (History.AllFailed.Contains(BuildCL))
            {
                BuildStatus = "FAILED";
            }
            else
            {
                BuildStatus = "running";
            }
            Log(" {0} {1} {2}", (BuildCL == CurrentCL)? ">>>>" : "    ", BuildCL, BuildStatus.ToString());

            // Update the last CL now that we've output this one
            LastCL = BuildCL;
        }

        Log("Took {0:0.0}s to get P4 history", (DateTime.UtcNow - StartTime).TotalSeconds);
    }
예제 #22
0
 private void WriteHistory(Node node, String action, Boolean success, String remark) => NodeHistory.Create(node, action, success, remark, Environment.MachineName, UserHost);