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); }
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); }