Example #1
0
        public void Setup()
        {
            // setup data objects
            channelVersionRules = new List <ChannelVersionRuleResource>();
            projectResource     = new ProjectResource
            {
                DeploymentProcessId = TestHelpers.GetId("deploymentprocess"),
                Id = TestHelpers.GetId("project")
            };
            deploymentProcessResource = new DeploymentProcessResource
            {
                ProjectId = projectResource.Id,
                Id        = projectResource.DeploymentProcessId
            };

            releaseTemplateResource = new ReleaseTemplateResource
            {
                DeploymentProcessId = projectResource.DeploymentProcessId,
                Packages            = new List <ReleaseTemplatePackage>(),
                Id = TestHelpers.GetId("releaseTemplate")
            };
            channelResource = new ChannelResource
            {
                IsDefault = true,
                Id        = TestHelpers.GetId("channel"),
                ProjectId = projectResource.Id,
                Rules     = channelVersionRules,
                Name      = TestHelpers.GetId("channelname")
            };
            feedResource = new FeedResource
            {
                Links = new LinkCollection {
                    { "SearchTemplate", TestHelpers.GetId("searchUri") }
                }
            };

            // setup mocks
            logger            = Substitute.For <ILogger>();
            versionResolver   = Substitute.For <IPackageVersionResolver>();
            versionRuleTester = Substitute.For <IChannelVersionRuleTester>();

            deploymentProcessRepository = Substitute.For <IDeploymentProcessRepository>();
            deploymentProcessRepository.Get(projectResource.DeploymentProcessId)
            .Returns(Task.FromResult(deploymentProcessResource));
            deploymentProcessRepository
            .GetTemplate(Arg.Is <DeploymentProcessResource>(deploymentProcessResource),
                         Arg.Is <ChannelResource>(channelResource)).Returns(Task.FromResult(releaseTemplateResource));
            versionRuleTester
            .Test(Arg.Any <IOctopusAsyncRepository>(), Arg.Any <ChannelVersionRuleResource>(), Arg.Any <string>())
            .Returns(Task.FromResult(channelVersionRuleTestResult));

            releaseRepository = Substitute.For <IReleaseRepository>();
            feedRepository    = Substitute.For <IFeedRepository>();
            feedRepository.Get(Arg.Any <string>()).Returns(feedResource);

            repository = Substitute.For <IOctopusAsyncRepository>();
            repository.DeploymentProcesses.Returns(deploymentProcessRepository);
            repository.Releases.Returns(releaseRepository);
            repository.Feeds.Returns(feedRepository);
            repository.Client
            .Get <List <PackageResource> >(Arg.Any <string>(), Arg.Any <IDictionary <string, object> >()).Returns(packages);

            builder = new ReleasePlanBuilder(logger, versionResolver, versionRuleTester);
        }
        public async Task <ReleasePlan> Build(IOctopusAsyncRepository repository, ProjectResource project, ChannelResource channel, string versionPreReleaseTag)
        {
            if (repository == null)
            {
                throw new ArgumentNullException(nameof(repository));
            }
            if (project == null)
            {
                throw new ArgumentNullException(nameof(project));
            }

            log.Debug("Finding deployment process...");
            var deploymentProcess = await repository.DeploymentProcesses.Get(project.DeploymentProcessId).ConfigureAwait(false);

            log.Debug("Finding release template...");
            var releaseTemplate = await repository.DeploymentProcesses.GetTemplate(deploymentProcess, channel).ConfigureAwait(false);

            var plan = new ReleasePlan(project, channel, releaseTemplate, versionResolver);

            if (plan.UnresolvedSteps.Any())
            {
                log.Debug("The package version for some steps was not specified. Going to try and resolve those automatically...");
                foreach (var unresolved in plan.UnresolvedSteps)
                {
                    if (!unresolved.IsResolveable)
                    {
                        log.Error("The version number for step '{Step:l}' cannot be automatically resolved because the feed or package ID is dynamic.", unresolved.StepName);
                        continue;
                    }

                    if (!string.IsNullOrEmpty(versionPreReleaseTag))
                    {
                        log.Debug("Finding latest package with pre-release '{Tag:l}' for step: {StepName:l}", versionPreReleaseTag, unresolved.StepName);
                    }
                    else
                    {
                        log.Debug("Finding latest package for step: {StepName:l}", unresolved.StepName);
                    }

                    var feed = await repository.Feeds.Get(unresolved.PackageFeedId).ConfigureAwait(false);

                    if (feed == null)
                    {
                        throw new CommandException(string.Format("Could not find a feed with ID {0}, which is used by step: " + unresolved.StepName, unresolved.PackageFeedId));
                    }

                    var filters = BuildChannelVersionFilters(unresolved.StepName, channel);
                    filters["packageId"] = unresolved.PackageId;
                    if (!string.IsNullOrWhiteSpace(versionPreReleaseTag))
                    {
                        filters["preReleaseTag"] = versionPreReleaseTag;
                    }

                    var packages = await repository.Client.Get <List <PackageResource> >(feed.Link("SearchTemplate"), filters).ConfigureAwait(false);

                    var latestPackage = packages.FirstOrDefault();

                    if (latestPackage == null)
                    {
                        log.Error("Could not find any packages with ID '{PackageId:l}' in the feed '{FeedUri:l}'", unresolved.PackageId, feed.FeedUri);
                    }
                    else
                    {
                        log.Debug("Selected '{PackageId:l}' version '{Version:l}' for '{StepName:l}'", latestPackage.PackageId, latestPackage.Version, unresolved.StepName);
                        unresolved.SetVersionFromLatest(latestPackage.Version);
                    }
                }
            }

            // Test each step in this plan satisfies the channel version rules
            if (channel != null)
            {
                foreach (var step in plan.Steps)
                {
                    // Note the rule can be null, meaning: anything goes
                    var rule   = channel.Rules.SingleOrDefault(r => r.Actions.Any(s => s.Equals(step.StepName, StringComparison.OrdinalIgnoreCase)));
                    var result = await versionRuleTester.Test(repository, rule, step.Version).ConfigureAwait(false);

                    step.SetChannelVersionRuleTestResult(result);
                }
            }

            return(plan);
        }
        public void Setup()
        {
            // setup data objects
            channelVersionRules = new List <ChannelVersionRuleResource>();
            projectResource     = new ProjectResource
            {
                DeploymentProcessId = TestHelpers.GetId("deploymentprocess"),
                Id = TestHelpers.GetId("project")
            };
            deploymentProcessResource = new DeploymentProcessResource
            {
                ProjectId = projectResource.Id,
                Id        = projectResource.DeploymentProcessId
            };

            releaseTemplateResource = new ReleaseTemplateResource
            {
                DeploymentProcessId = projectResource.DeploymentProcessId,
                Packages            = new List <ReleaseTemplatePackage>(),
                Id = TestHelpers.GetId("releaseTemplate")
            };
            channelResource = new ChannelResource
            {
                IsDefault = true,
                Id        = TestHelpers.GetId("channel"),
                ProjectId = projectResource.Id,
                Rules     = channelVersionRules,
                Name      = TestHelpers.GetId("channelname")
            };
            feedResource = new FeedResource
            {
                Id    = BuiltInFeedId,
                Name  = "Built in feed",
                Links = new LinkCollection {
                    { "SearchTemplate", TestHelpers.GetId("searchUri") }
                }
            };

            // setup mocks
            logger                = Substitute.For <ILogger>();
            versionResolver       = Substitute.For <IPackageVersionResolver>();
            versionRuleTester     = Substitute.For <IChannelVersionRuleTester>();
            commandOutputProvider = Substitute.For <ICommandOutputProvider>();

            deploymentProcessRepository = Substitute.For <IDeploymentProcessRepository>();
            deploymentProcessRepository.Get(projectResource.DeploymentProcessId)
            .Returns(Task.FromResult(deploymentProcessResource));
            deploymentProcessRepository
            .GetTemplate(Arg.Is(deploymentProcessResource),
                         Arg.Is(channelResource))
            .Returns(Task.FromResult(releaseTemplateResource));
            versionRuleTester
            .Test(Arg.Any <IOctopusAsyncRepository>(), Arg.Any <ChannelVersionRuleResource>(), Arg.Any <string>(), Arg.Any <string>())
            .Returns(Task.FromResult(channelVersionRuleTestResult));

            deploymentProcessRepositoryBeta = Substitute.For <IDeploymentProcessRepositoryBeta>();
            deploymentProcessRepositoryBeta.Get(projectResource, Arg.Any <string>())
            .Returns(Task.FromResult(deploymentProcessResource));

            var feeds = new List <FeedResource>
            {
                feedResource
            };

            releaseRepository = Substitute.For <IReleaseRepository>();
            feedRepository    = Substitute.For <IFeedRepository>();
            feedRepository.Get(Arg.Any <string[]>()).Returns(feeds);
            feedRepository.FindByNames(Arg.Any <IEnumerable <string> >()).Returns(feeds);

            repository = Substitute.For <IOctopusAsyncRepository>();
            repository.DeploymentProcesses.Returns(deploymentProcessRepository);
            repository.DeploymentProcesses.Beta().Returns(deploymentProcessRepositoryBeta);
            repository.Releases.Returns(releaseRepository);
            repository.Feeds.Returns(feedRepository);
            repository.Client
            .Get <List <PackageResource> >(Arg.Any <string>(), Arg.Any <IDictionary <string, object> >())
            .Returns(packages);

            builder      = new ReleasePlanBuilder(logger, versionResolver, versionRuleTester, commandOutputProvider);
            gitReference = null;
        }
        private async Task <ReleasePlan> Build(IOctopusAsyncRepository repository, ProjectResource project, ChannelResource channel,
                                               string versionPreReleaseTag, ReleaseTemplateResource releaseTemplate, DeploymentProcessResource deploymentProcess)
        {
            var plan = new ReleasePlan(project, channel, releaseTemplate, deploymentProcess, versionResolver);

            if (plan.UnresolvedSteps.Any())
            {
                commandOutputProvider.Debug(
                    "The package version for some steps was not specified. Going to try and resolve those automatically...");

                var allRelevantFeeds = await LoadFeedsForSteps(repository, project, plan.UnresolvedSteps);

                foreach (var unresolved in plan.UnresolvedSteps)
                {
                    if (!unresolved.IsResolveable)
                    {
                        commandOutputProvider.Error(
                            "The version number for step '{Step:l}' cannot be automatically resolved because the feed or package ID is dynamic.",
                            unresolved.ActionName);
                        continue;
                    }

                    if (!string.IsNullOrEmpty(versionPreReleaseTag))
                    {
                        commandOutputProvider.Debug("Finding latest package with pre-release '{Tag:l}' for step: {StepName:l}",
                                                    versionPreReleaseTag, unresolved.ActionName);
                    }
                    else
                    {
                        commandOutputProvider.Debug("Finding latest package for step: {StepName:l}", unresolved.ActionName);
                    }

                    if (!allRelevantFeeds.TryGetValue(unresolved.PackageFeedId, out var feed))
                    {
                        throw new CommandException(string.Format(
                                                       "Could not find a feed with ID {0}, which is used by step: " + unresolved.ActionName,
                                                       unresolved.PackageFeedId));
                    }

                    var filters = BuildChannelVersionFilters(unresolved.ActionName, unresolved.PackageReferenceName, channel);
                    filters["packageId"] = unresolved.PackageId;
                    if (!string.IsNullOrWhiteSpace(versionPreReleaseTag))
                    {
                        filters["preReleaseTag"] = versionPreReleaseTag;
                    }

                    var packages = await repository.Client.Get <List <PackageResource> >(feed.Link("SearchTemplate"), filters)
                                   .ConfigureAwait(false);

                    var latestPackage = packages.FirstOrDefault();

                    if (latestPackage == null)
                    {
                        commandOutputProvider.Error(
                            "Could not find any packages with ID '{PackageId:l}' in the feed '{FeedUri:l}'",
                            unresolved.PackageId, feed.Name);
                    }
                    else
                    {
                        commandOutputProvider.Debug("Selected '{PackageId:l}' version '{Version:l}' for '{StepName:l}'",
                                                    latestPackage.PackageId, latestPackage.Version, unresolved.ActionName);
                        unresolved.SetVersionFromLatest(latestPackage.Version);
                    }
                }
            }

            // Test each step in this plan satisfies the channel version rules
            if (channel != null)
            {
                foreach (var step in plan.PackageSteps)
                {
                    // Note the rule can be null, meaning: anything goes
                    var rule = channel.Rules.SingleOrDefault(r => r.ActionPackages.Any(pkg =>
                                                                                       pkg.DeploymentActionNameMatches(step.ActionName) &&
                                                                                       pkg.PackageReferenceNameMatches(step.PackageReferenceName)));
                    var result = await versionRuleTester.Test(repository, rule, step.Version, step.PackageFeedId).ConfigureAwait(false);

                    step.SetChannelVersionRuleTestResult(result);
                }
            }

            return(plan);
        }
Example #5
0
        public async Task <ReleasePlan> Build(IOctopusAsyncRepository repository, ProjectResource project, ChannelResource channel, string versionPreReleaseTag, string versionPreReleaseTagFallBacks, string softDefaultPackageVersion, bool LatestByPublishDate)
        {
            if (repository == null)
            {
                throw new ArgumentNullException(nameof(repository));
            }
            if (project == null)
            {
                throw new ArgumentNullException(nameof(project));
            }

            commandOutputProvider.Debug("Finding deployment process...");
            var deploymentProcess = await repository.DeploymentProcesses.Get(project.DeploymentProcessId).ConfigureAwait(false);

            commandOutputProvider.Debug("Finding release template...");
            var releaseTemplate = await repository.DeploymentProcesses.GetTemplate(deploymentProcess, channel).ConfigureAwait(false);

            var plan = new ReleasePlan(project, channel, releaseTemplate, deploymentProcess, versionResolver);

            if (plan.UnresolvedSteps.Any())
            {
                commandOutputProvider.Debug("The package version for some steps was not specified. Going to try and resolve those automatically...");

                foreach (var unresolved in plan.UnresolvedSteps)
                {
                    if (!unresolved.IsResolveable)
                    {
                        commandOutputProvider.Error("The version number for step '{Step:l}' cannot be automatically resolved because the feed or package ID is dynamic.", unresolved.ActionName);
                        continue;
                    }


                    var feed = await repository.Feeds.Get(unresolved.PackageFeedId).ConfigureAwait(false);

                    if (feed == null)
                    {
                        throw new CommandException(string.Format("Could not find a feed with ID {0}, which is used by step: " + unresolved.ActionName, unresolved.PackageFeedId));
                    }
                    var             packages = new System.Collections.Generic.List <Octopus.Client.Model.PackageResource>();
                    PackageResource latestPackage;
                    String          versionSource = "Latest available";

                    var filters = BuildChannelVersionFilters(unresolved.ActionName, unresolved.PackageReferenceName, channel);
                    filters["packageId"] = unresolved.PackageId;

                    commandOutputProvider.Debug("------------------------------------------\r\n");
                    commandOutputProvider.Debug("Step: '{StepName:l}', Package: '{PackageId:l}':\r\n", unresolved.ActionName, unresolved.PackageId);


                    //look for exact version of package specified softDefaultPackageVersion, bypass all further version-seeking heurstics if succeed
                    if (!string.IsNullOrWhiteSpace(softDefaultPackageVersion))
                    {
                        filters["versionRange"] = "[" + softDefaultPackageVersion + "]";
                        packages = await repository.Client.Get <List <PackageResource> >(feed.Link("SearchTemplate"), filters).ConfigureAwait(false);

                        latestPackage = packages.FirstOrDefault();
                        if (latestPackage != null)
                        {
                            commandOutputProvider.Debug("Version '{Version:l}' was found using softDefaultPackageVersion specified. Any further version-seeking heurstics will be bypassed.", latestPackage.Version);
                            versionSource = "softDefaultPackageVersion";
                            unresolved.SetVersionFromLatest(latestPackage.Version, versionSource);
                            continue;
                        }
                        else
                        {
                            filters.Remove("versionRange");
                            commandOutputProvider.Debug("Could not find package with softDefaultPackageVersion: '{softDefaultPackageVersion:l}', falling back to search with another specified methods (versionPreReleaseTag,versionPreReleaseTagFallBacks)", softDefaultPackageVersion);
                        }
                    }



                    if (!string.IsNullOrWhiteSpace(versionPreReleaseTag))
                    {
                        filters["preReleaseTag"] = versionPreReleaseTag;
                        versionSource            = "versionPreReleaseTag";
                        commandOutputProvider.Debug("versionPreReleaseTag: '{Tag:l}' was specified. Looking for latest package with this tag.'", versionPreReleaseTag);
                    }



                    bool ResolverLooksForPreReleasePackage = !(string.IsNullOrWhiteSpace(versionPreReleaseTag) || versionPreReleaseTag == "^$");


                    //As we can't sort by publishing date on the server side, we have to take all packages and sort them on the client side
                    if (LatestByPublishDate && ResolverLooksForPreReleasePackage)
                    {
                        filters["take"] = 10000;
                    }
                    else
                    {
                        //AIR-1533. Speed-up package choosing
                        filters["take"] = 1;
                    }


                    packages = await repository.Client.Get <List <PackageResource> >(feed.Link("SearchTemplate"), filters).ConfigureAwait(false);



                    //Get the latest published package for release instead of the package has the biggest SemVer
                    //Only for pre-release packages and only if LatestByPublishDate prop specified
                    //Using latest published package is inappropriate for release packages, because hotfix releases for old versions may be pushed after main major versions.
                    if (LatestByPublishDate && ResolverLooksForPreReleasePackage)
                    {
                        latestPackage = packages.OrderByDescending(o => o.Published).FirstOrDefault();
                        if (latestPackage != null)
                        {
                            commandOutputProvider.Debug("'--latestbypublishdate' flag was specified. Package resolver will choose version of package by the latest publishing date instead of the higest SemVer version.");
                        }
                    }
                    else
                    {
                        latestPackage = packages.FirstOrDefault();
                    }



                    if (latestPackage == null && !string.IsNullOrWhiteSpace(versionPreReleaseTag) && !string.IsNullOrWhiteSpace(versionPreReleaseTagFallBacks))
                    {
                        commandOutputProvider.Debug("Could not find latest package with pre-release '{Tag:l}', falling back to search with pre-release tags '{FallBackTags:l}' ", versionPreReleaseTag, versionPreReleaseTagFallBacks);
                        //trim values and remove empty ones
                        List <string> versionPreReleaseTagFallBacksList = versionPreReleaseTagFallBacks.Split(',').ToList().Select(s => s.Trim()).ToList();
                        versionPreReleaseTagFallBacksList = versionPreReleaseTagFallBacksList.Where(s => !string.IsNullOrWhiteSpace(s)).Distinct().ToList();

                        foreach (string versionPreReleaseTagFallBack in versionPreReleaseTagFallBacksList)
                        {
                            //similar beahaviour as for general versionPreReleaseTag
                            //Get the latest published package for release instead of the package has the biggest SemVer
                            filters.Remove("take");

                            filters["preReleaseTag"] = versionPreReleaseTagFallBack;

                            ResolverLooksForPreReleasePackage = !(versionPreReleaseTagFallBack == "^$");


                            //As we can't sort by publishing date on the server side, we have to take all packages and sort them on the client side
                            if (LatestByPublishDate && ResolverLooksForPreReleasePackage)
                            {
                                filters["take"] = 10000;
                            }
                            else
                            {
                                //AIR-1533. Speed-up package choosing
                                filters["take"] = 1;
                            }

                            packages = await repository.Client.Get <List <PackageResource> >(feed.Link("SearchTemplate"), filters).ConfigureAwait(false);

                            if (LatestByPublishDate && ResolverLooksForPreReleasePackage)
                            {
                                latestPackage = packages.OrderByDescending(o => o.Published).FirstOrDefault();
                                if (latestPackage != null)
                                {
                                    commandOutputProvider.Debug("'--latestbypublishdate' flag was specified. Package resolver will choose version by the latest publishing date instead of the higest SemVer version.");
                                }
                            }
                            else
                            {
                                latestPackage = packages.FirstOrDefault();
                            }

                            if (latestPackage != null)
                            {
                                versionSource = "versionPreReleaseTagFallBacks";
                                break;
                            }
                        }
                    }


                    if (latestPackage == null)
                    {
                        commandOutputProvider.Error("Could not find any packages with this ID in the feed '{FeedUri:l}'", feed.Name);
                    }
                    else
                    {
                        commandOutputProvider.Debug("Selected version '{Version:l}'", latestPackage.Version);
                        unresolved.SetVersionFromLatest(latestPackage.Version, versionSource);
                    }
                }
            }

            // Test each step in this plan satisfies the channel version rules
            if (channel != null)
            {
                foreach (var step in plan.PackageSteps)
                {
                    // Note the rule can be null, meaning: anything goes
                    var rule   = channel.Rules.SingleOrDefault(r => r.ActionPackages.Any(pkg => pkg.DeploymentActionNameMatches(step.ActionName) && pkg.PackageReferenceNameMatches(step.PackageReferenceName)));
                    var result = await versionRuleTester.Test(repository, rule, step.Version).ConfigureAwait(false);

                    step.SetChannelVersionRuleTestResult(result);
                }
            }

            return(plan);
        }