public override int Run(string[] remainingArguments)
        {
            var api = string.IsNullOrEmpty(_teamCityAuthToken) ? new TeamCityApi(ServerName) : new TeamCityApi(ServerName,_teamCityAuthToken);

            var buildPackageCache = string.IsNullOrEmpty(_buildPackageCacheFile) ? null : new PackageBuildMappingCache(_buildPackageCacheFile);

            var resolver = new AggregateBuildDeltaResolver(api, CreateExternalIssueResolvers(), new PackageChangeComparator(),buildPackageCache, new List<NuGetPackageChange>());
            ChangeManifest = string.IsNullOrEmpty(BuildType)
                ? resolver.CreateChangeManifestFromBuildTypeName(ProjectName, BuildName,_referenceBuild, _from, _to, _useBuildSystemIssueResolution, _recurse)
                : resolver.CreateChangeManifestFromBuildTypeId(BuildType, _referenceBuild, _from, _to, _useBuildSystemIssueResolution, _recurse);

            OutputChanges(CreateOutputRenderers(), new List<Action<string>> {Console.Write, a =>
                {
                    if (!string.IsNullOrEmpty(OutputFileName))
                        File.WriteAllText(OutputFileName, a);
                }});
            return 0;
        }
        public static AggregateBuildDeltaResolver CreateMockedAggregateBuildDeltaResolver(IEnumerable<BuildTemplate> buildTemplates)
        {
            const string apiServer = "http://test.server";

            var api = A.Fake<ITeamCityApi>();
            A.CallTo(() => api.Url).Returns(apiServer);

            var packageCache = new PackageBuildMappingCache();

            var issueResolver = A.Fake<IExternalIssueResolver>();

            foreach (var template in buildTemplates)
            {
                SetExpectations(template, api, issueResolver, packageCache);
            }

            var resolver = new AggregateBuildDeltaResolver(api, new IssueDetailResolver(new[] {issueResolver}), new PackageChangeComparator(), packageCache, new ConcurrentBag<NuGetPackageChange>());
            return resolver;
        }
        public override int Run(string[] remainingArguments)
        {
            ICacheClient client = new MemoryCacheClient();
            var api = new TeamCityApi(new CachingThreadSafeAuthenticatedRestClient(new MemoryCacheClient(), _serverName, _teamCityAuthToken), client);

            var buildPackageCache = string.IsNullOrEmpty(_buildPackageCacheFile) ? null : new PackageBuildMappingCache(_buildPackageCacheFile);

            var issueDetailResolver = new IssueDetailResolver(CreateExternalIssueResolvers());

            var resolver = new AggregateBuildDeltaResolver(api, issueDetailResolver, new PackageChangeComparator(), buildPackageCache, new ConcurrentBag<NuGetPackageChange>());
            _changeManifest = string.IsNullOrEmpty(_buildType)
                ? resolver.CreateChangeManifestFromBuildTypeName(_projectName, _buildName,_referenceBuild, _from, _to, _useBuildSystemIssueResolution, _recurse, _branchName)
                : resolver.CreateChangeManifestFromBuildTypeId(_buildType, _referenceBuild, _from, _to, _useBuildSystemIssueResolution, _recurse, _branchName);

            OutputChanges(CreateOutputRenderers(), new List<Action<string>> {Console.Write, a =>
                {
                    if (!string.IsNullOrEmpty(_outputFileName))
                        File.WriteAllText(_outputFileName, a);
                }});
            return 0;
        }
        public override int Run(string[] remainingArguments)
        {
            var api = string.IsNullOrEmpty(_teamCityAuthToken) ? new TeamCityApi(ServerName) : new TeamCityApi(ServerName, _teamCityAuthToken);

            var buildPackageCache = string.IsNullOrEmpty(_buildPackageCacheFile) ? null : new PackageBuildMappingCache(_buildPackageCacheFile);

            var resolver = new AggregateBuildDeltaResolver(api, CreateExternalIssueResolvers(), new PackageChangeComparator(), buildPackageCache, new List <NuGetPackageChange>());

            ChangeManifest = string.IsNullOrEmpty(BuildType)
                ? resolver.CreateChangeManifestFromBuildTypeName(ProjectName, BuildName, _referenceBuild, _from, _to, _useBuildSystemIssueResolution, _recurse)
                : resolver.CreateChangeManifestFromBuildTypeId(BuildType, _referenceBuild, _from, _to, _useBuildSystemIssueResolution, _recurse);

            OutputChanges(CreateOutputRenderers(), new List <Action <string> > {
                Console.Write, a =>
                {
                    if (!string.IsNullOrEmpty(OutputFileName))
                    {
                        File.WriteAllText(OutputFileName, a);
                    }
                }
            });
            return(0);
        }
        private ChangeManifest CreateChangeManifest(string buildName, string buildType, string referenceBuild = null, string from = null, string to = null, string projectName = null, bool useBuildSystemIssueResolution = true, bool recurse = false)
        {
            var changeManifest = new ChangeManifest();

            if (recurse && _packageBuildMappingCache == null)
            {
                changeManifest.GenerationLog.Add(new LogEntry(DateTime.Now, Status.Warning, "Recurse option provided with no PackageBuildMappingCache, we will not be honoring the Recurse option."));
                changeManifest.GenerationStatus = Status.Warning;
            }

            buildType = buildType ?? ResolveBuildTypeId(projectName, buildName);

            if (String.IsNullOrEmpty(from))
            {
                changeManifest.GenerationLog.Add(new LogEntry(DateTime.Now, Status.Warning, "Resolving FROM version based on the provided BuildType (FROM was not provided)."));
                from = ResolveFromVersion(buildType);
            }

            if (String.IsNullOrEmpty(to))
            {
                changeManifest.GenerationLog.Add(new LogEntry(DateTime.Now, Status.Warning, "Resolving TO version based on the provided BuildType (TO was not provided)."));
                to = ResolveToVersion(buildType);
            }

            var buildWithCommitData       = referenceBuild ?? buildType;
            var buildTypeDetails          = _api.GetBuildTypeDetailsById(buildType);
            var referenceBuildTypeDetails = !String.IsNullOrEmpty(referenceBuild) ? _api.GetBuildTypeDetailsById(referenceBuild) : null;

            if (!String.IsNullOrEmpty(from) && !String.IsNullOrEmpty(to) && !String.IsNullOrEmpty(buildWithCommitData))
            {
                changeManifest.GenerationLog.Add(new LogEntry(DateTime.Now, Status.Ok, "Getting builds based on BuildType"));
                var builds = _api.GetBuildsByBuildType(buildWithCommitData);
                if (builds != null)
                {
                    var buildList = builds as List <Build> ?? builds.ToList();
                    changeManifest.GenerationLog.Add(new LogEntry(DateTime.Now, Status.Ok, string.Format("Got {0} builds for BuildType {1}.", buildList.Count(), buildType)));
                    var changeDetails       = _api.GetChangeDetailsByBuildTypeAndBuildNumber(buildWithCommitData, @from, to, buildList).ToList();
                    var issueDetailResolver = new IssueDetailResolver(_externalIssueResolvers);

                    //Rather than use TeamCity to resolve the issue to commit details (via TeamCity plugins) use the issue resolvers directly...
                    var issues = useBuildSystemIssueResolution
                                     ? _api.GetIssuesByBuildTypeAndBuildRange(buildWithCommitData, @from, to, buildList).ToList()
                                     : issueDetailResolver.GetAssociatedIssues(changeDetails).ToList();

                    changeManifest.GenerationLog.Add(new LogEntry(DateTime.Now, Status.Ok, string.Format("Got {0} issues for BuildType {1}.", issues.Count(), buildType)));

                    changeManifest.GenerationLog.Add(new LogEntry(DateTime.Now, Status.Ok, "Checking package dependencies."));
                    var buildFrom       = buildList.FirstOrDefault(b => b.Number == @from);
                    var buildTo         = buildList.FirstOrDefault(b => b.Number == to);
                    var initialPackages = new List <TeamCityApi.PackageDetails>();
                    var finalPackages   = new List <TeamCityApi.PackageDetails>();
                    if (buildFrom != null)
                    {
                        initialPackages = _api.GetNuGetDependenciesByBuildTypeAndBuildId(buildType, buildFrom.Id).ToList();
                    }
                    if (buildTo != null)
                    {
                        finalPackages = _api.GetNuGetDependenciesByBuildTypeAndBuildId(buildType, buildTo.Id).ToList();
                    }

                    var packageChanges = _packageChangeComparator.GetPackageChanges(initialPackages, finalPackages).ToList();

                    var issueDetails = issueDetailResolver.GetExternalIssueDetails(issues);

                    changeManifest.NuGetPackageChanges = packageChanges;
                    changeManifest.ChangeDetails.AddRange(changeDetails);
                    changeManifest.IssueDetails.AddRange(issueDetails);
                    changeManifest.Generated                   = DateTime.Now;
                    changeManifest.FromVersion                 = @from;
                    changeManifest.ToVersion                   = to;
                    changeManifest.BuildConfiguration          = buildTypeDetails;
                    changeManifest.ReferenceBuildConfiguration = referenceBuildTypeDetails ?? new BuildTypeDetails();
                }
                else
                {
                    changeManifest.GenerationLog.Add(new LogEntry(DateTime.Now, Status.Warning, string.Format("No builds returned for BuildType {0}.", buildType)));
                }
            }
            //Now we need to see if we need to recurse, and whether we have been given a cache file....
            if (changeManifest.NuGetPackageChanges.Any() && recurse && _packageBuildMappingCache != null)
            {
                foreach (var dependency in changeManifest.NuGetPackageChanges.Where(c => c.Type == NuGetPackageChangeType.Modified))
                {
                    var traversedDependency = _traversedPackageChanges.FirstOrDefault(p => p.NewVersion == dependency.NewVersion && p.OldVersion == dependency.OldVersion && p.PackageId == dependency.PackageId);
                    if (traversedDependency != null)
                    {
                        dependency.ChangeManifest = traversedDependency.ChangeManifest;
                        continue;
                    }
                    var mappings = _packageBuildMappingCache.PackageBuildMappings.Where(m => m.PackageId.Equals(dependency.PackageId, StringComparison.CurrentCultureIgnoreCase)).ToList();
                    PackageBuildMapping build = null;
                    if (mappings.Count == 1)
                    {
                        //We only got one back, this is good...
                        build = mappings.First();
                        changeManifest.GenerationLog.Add(new LogEntry(DateTime.Now, Status.Ok, string.Format("Found singular packages to build mapping {0}.", build.BuildConfigurationName)));
                    }
                    else if (mappings.Any())
                    {
                        //Ok, so multiple builds are outputting this package, so we need to try and constrain on project...
                        build = mappings.FirstOrDefault(m => m.Project.Equals(buildTypeDetails.Project.Name, StringComparison.OrdinalIgnoreCase));
                        if (build != null)
                        {
                            changeManifest.GenerationLog.Add(new LogEntry(DateTime.Now, Status.Warning, string.Format("Found duplicate mappings, using package to build mapping {0}.", build.BuildConfigurationName)));
                        }
                    }

                    if (build != null)
                    {
                        if (build.BuildConfigurationId == buildType)
                        {
                            continue;
                        }
                        var instanceTeamCityApi = _api.TeamCityServer.Equals(build.ServerUrl, StringComparison.OrdinalIgnoreCase)
                                                              ? _api
                                                              : new TeamCityApi(build.ServerUrl);

                        var resolver           = new AggregateBuildDeltaResolver(instanceTeamCityApi, _externalIssueResolvers, _packageChangeComparator, _packageBuildMappingCache, _traversedPackageChanges);
                        var dependencyManifest = resolver.CreateChangeManifest(null, build.BuildConfigurationId, null, dependency.OldVersion, dependency.NewVersion, null, true, true);
                        dependency.ChangeManifest = dependencyManifest;
                    }
                    else
                    {
                        changeManifest.GenerationLog.Add(new LogEntry(DateTime.Now, Status.Warning, string.Format("Did not find a mapping for package: {0}.", dependency.PackageId)));
                    }
                    _traversedPackageChanges.Add(dependency);
                }
            }

            return(changeManifest);
        }
        private ChangeManifest CreateChangeManifest(string buildName, string buildType, string referenceBuild = null, string from = null, string to = null, string projectName = null, bool useBuildSystemIssueResolution = true, bool recurse = false)
        {
            var changeManifest = new ChangeManifest();
            if (recurse && _packageBuildMappingCache == null)
            {
                changeManifest.GenerationLog.Add(new LogEntry(DateTime.Now,Status.Warning,"Recurse option provided with no PackageBuildMappingCache, we will not be honoring the Recurse option."));
                changeManifest.GenerationStatus = Status.Warning;
            }

            buildType = buildType ?? ResolveBuildTypeId(projectName, buildName);

            if (String.IsNullOrEmpty(from))
            {
                changeManifest.GenerationLog.Add(new LogEntry(DateTime.Now, Status.Warning, "Resolving FROM version based on the provided BuildType (FROM was not provided)."));
                from = ResolveFromVersion(buildType);
            }

            if (String.IsNullOrEmpty(to))
            {
                changeManifest.GenerationLog.Add(new LogEntry(DateTime.Now, Status.Warning, "Resolving TO version based on the provided BuildType (TO was not provided)."));
                to = ResolveToVersion(buildType);
            }

            var buildWithCommitData = referenceBuild ?? buildType;
            var buildTypeDetails = _api.GetBuildTypeDetailsById(buildType);
            var referenceBuildTypeDetails = !String.IsNullOrEmpty(referenceBuild) ? _api.GetBuildTypeDetailsById(referenceBuild) : null;

            if (!String.IsNullOrEmpty(from) && !String.IsNullOrEmpty(to) && !String.IsNullOrEmpty(buildWithCommitData))
            {
                changeManifest.GenerationLog.Add(new LogEntry(DateTime.Now, Status.Ok, "Getting builds based on BuildType"));
                var builds = _api.GetBuildsByBuildType(buildWithCommitData);
                if (builds != null)
                {
                    var buildList = builds as List<Build> ?? builds.ToList();
                    changeManifest.GenerationLog.Add(new LogEntry(DateTime.Now,Status.Ok, string.Format("Got {0} builds for BuildType {1}.",buildList.Count(), buildType)));
                    var changeDetails =_api.GetChangeDetailsByBuildTypeAndBuildNumber(buildWithCommitData, @from, to, buildList).ToList();
                    var issueDetailResolver = new IssueDetailResolver(_externalIssueResolvers);

                    //Rather than use TeamCity to resolve the issue to commit details (via TeamCity plugins) use the issue resolvers directly...
                    var issues = useBuildSystemIssueResolution
                                     ? _api.GetIssuesByBuildTypeAndBuildRange(buildWithCommitData, @from, to, buildList).ToList()
                                     : issueDetailResolver.GetAssociatedIssues(changeDetails).ToList();

                    changeManifest.GenerationLog.Add(new LogEntry(DateTime.Now,Status.Ok, string.Format("Got {0} issues for BuildType {1}.", issues.Count(),buildType)));

                    changeManifest.GenerationLog.Add(new LogEntry(DateTime.Now, Status.Ok, "Checking package dependencies."));
                    var buildFrom = buildList.FirstOrDefault(b => b.Number == @from);
                    var buildTo = buildList.FirstOrDefault(b => b.Number == to);
                    var initialPackages = new List<TeamCityApi.PackageDetails>();
                    var finalPackages = new List<TeamCityApi.PackageDetails>();
                    if (buildFrom != null)
                        initialPackages = _api.GetNuGetDependenciesByBuildTypeAndBuildId(buildType,buildFrom.Id).ToList();
                    if (buildTo != null)
                        finalPackages = _api.GetNuGetDependenciesByBuildTypeAndBuildId(buildType, buildTo.Id).ToList();

                    var packageChanges = _packageChangeComparator.GetPackageChanges(initialPackages, finalPackages).ToList();

                    var issueDetails = issueDetailResolver.GetExternalIssueDetails(issues);

                    changeManifest.NuGetPackageChanges = packageChanges;
                    changeManifest.ChangeDetails.AddRange(changeDetails);
                    changeManifest.IssueDetails.AddRange(issueDetails);
                    changeManifest.Generated = DateTime.Now;
                    changeManifest.FromVersion = @from;
                    changeManifest.ToVersion = to;
                    changeManifest.BuildConfiguration = buildTypeDetails;
                    changeManifest.ReferenceBuildConfiguration = referenceBuildTypeDetails ?? new BuildTypeDetails();
                }
                else
                {
                    changeManifest.GenerationLog.Add(new LogEntry(DateTime.Now, Status.Warning, string.Format("No builds returned for BuildType {0}.", buildType)));
                }
            }
            //Now we need to see if we need to recurse, and whether we have been given a cache file....
            if (changeManifest.NuGetPackageChanges.Any() && recurse && _packageBuildMappingCache != null)
            {
                foreach (var dependency in changeManifest.NuGetPackageChanges.Where(c => c.Type == NuGetPackageChangeType.Modified))
                {
                    var traversedDependency = _traversedPackageChanges.FirstOrDefault(p => p.NewVersion == dependency.NewVersion && p.OldVersion == dependency.OldVersion && p.PackageId == dependency.PackageId);
                    if (traversedDependency != null)
                    {
                        dependency.ChangeManifest = traversedDependency.ChangeManifest;
                        continue;
                    }
                    var mappings = _packageBuildMappingCache.PackageBuildMappings.Where(m => m.PackageId.Equals(dependency.PackageId, StringComparison.CurrentCultureIgnoreCase)).ToList();
                    PackageBuildMapping build = null;
                    if (mappings.Count == 1)
                    {
                        //We only got one back, this is good...
                        build = mappings.First();
                        changeManifest.GenerationLog.Add(new LogEntry(DateTime.Now, Status.Ok, string.Format("Found singular packages to build mapping {0}.", build.BuildConfigurationName)));
                    }
                    else if (mappings.Any())
                    {
                        //Ok, so multiple builds are outputting this package, so we need to try and constrain on project...
                        build = mappings.FirstOrDefault(m => m.Project.Equals(buildTypeDetails.Project.Name, StringComparison.OrdinalIgnoreCase));
                        if (build != null) changeManifest.GenerationLog.Add(new LogEntry(DateTime.Now, Status.Warning, string.Format("Found duplicate mappings, using package to build mapping {0}.", build.BuildConfigurationName)));
                    }

                    if (build != null)
                    {
                        if (build.BuildConfigurationId == buildType)
                            continue;
                        var instanceTeamCityApi = _api.TeamCityServer.Equals(build.ServerUrl, StringComparison.OrdinalIgnoreCase)
                                                              ? _api
                                                              : new TeamCityApi(build.ServerUrl);

                        var resolver = new AggregateBuildDeltaResolver(instanceTeamCityApi, _externalIssueResolvers,_packageChangeComparator,_packageBuildMappingCache, _traversedPackageChanges);
                        var dependencyManifest = resolver.CreateChangeManifest(null, build.BuildConfigurationId, null,dependency.OldVersion,dependency.NewVersion, null, true, true);
                        dependency.ChangeManifest = dependencyManifest;
                    }
                    else
                    {
                        changeManifest.GenerationLog.Add(new LogEntry(DateTime.Now, Status.Warning, string.Format("Did not find a mapping for package: {0}.", dependency.PackageId)));
                    }
                    _traversedPackageChanges.Add(dependency);
                }
            }

            return changeManifest;
        }